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/BaritoneAPI.java b/src/api/java/baritone/api/BaritoneAPI.java index 4ea6ef3e..53937bd8 100644 --- a/src/api/java/baritone/api/BaritoneAPI.java +++ b/src/api/java/baritone/api/BaritoneAPI.java @@ -34,12 +34,12 @@ public final class BaritoneAPI { private static final Settings settings; static { + settings = new Settings(); + SettingsUtil.readAndApply(settings); + ServiceLoader baritoneLoader = ServiceLoader.load(IBaritoneProvider.class); Iterator instances = baritoneLoader.iterator(); provider = instances.next(); - - settings = new Settings(); - SettingsUtil.readAndApply(settings); } public static IBaritoneProvider getProvider() { diff --git a/src/api/java/baritone/api/IBaritone.java b/src/api/java/baritone/api/IBaritone.java index 2c892d98..8c5de47a 100644 --- a/src/api/java/baritone/api/IBaritone.java +++ b/src/api/java/baritone/api/IBaritone.java @@ -23,8 +23,10 @@ import baritone.api.cache.IWorldProvider; import baritone.api.event.listener.IEventBus; import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.process.*; +import baritone.api.selection.ISelectionManager; import baritone.api.utils.IInputOverrideHandler; import baritone.api.utils.IPlayerContext; +import baritone.api.utils.command.manager.ICommandManager; /** * @author Brady @@ -32,15 +34,6 @@ import baritone.api.utils.IPlayerContext; */ public interface IBaritone { - /** - * Call as soon as Minecraft is ready, initializes all of the processes, behaviors, etc. This will - * only effectively be ran once, any additional calls are redundant because the initialization state - * is saved. - *

- * Or whenever your overeager utility client wants. - */ - void init(); - /** * @return The {@link IPathingBehavior} instance * @see IPathingBehavior @@ -128,6 +121,18 @@ public interface IBaritone { */ IEventBus getGameEventHandler(); + /** + * @return The {@link ISelectionManager} instance + * @see ISelectionManager + */ + ISelectionManager getSelectionManager(); + + /** + * @return The {@link ICommandManager} instance + * @see ICommandManager + */ + ICommandManager getCommandManager(); + /** * Open click */ diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index a95d30d2..8d54cbde 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -30,8 +30,8 @@ import java.awt.*; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.*; import java.util.List; +import java.util.*; import java.util.function.Consumer; /** @@ -495,15 +495,20 @@ public final class Settings { public final Setting chatControl = new Setting<>(true); /** - * A second override over chatControl to force it on + * Some clients like Impact try to force chatControl to off, so here's a second setting to do it anyway */ - public final Setting removePrefix = new Setting<>(false); + public final Setting chatControlAnyway = new Setting<>(false); /** * Render the path */ public final Setting renderPath = new Setting<>(true); + /** + * Render the path as a line instead of a frickin thingy + */ + public final Setting renderPathAsLine = new Setting<>(false); + /** * Render the goal */ @@ -604,21 +609,41 @@ public final class Settings { public final Setting cachedChunksOpacity = new Setting<>(0.5f); /** - * Whether or not to use the "#" command prefix + * Whether or not to allow you to run Baritone commands with the prefix */ public final Setting prefixControl = new Setting<>(true); + /** + * The command prefix for chat control + */ + public final Setting prefix = new Setting<>("#"); + + /** + * Use a short Baritone prefix [B] instead of [Baritone] when logging to chat + */ + public final Setting shortBaritonePrefix = new Setting<>(false); + + /** + * Echo commands to chat when they are run + */ + public final Setting echoCommands = new Setting<>(true); + + /** + * Censor coordinates in goals and block positions + */ + public final Setting censorCoordinates = new Setting<>(false); + + /** + * Censor arguments to ran commands, to hide, for example, coordinates to #goal + */ + public final Setting censorRanCommands = new Setting<>(false); + /** * Always prefer silk touch tools over regular tools. This will not sacrifice speed, but it will always prefer silk * touch tools over other tools of the same speed. This includes always choosing ANY silk touch tool over your hand. */ public final Setting preferSilkTouch = new Setting<>(false); - /* - * Censor coordinates in goals and block positions - */ - public final Setting censorCoordinates = new Setting<>(false); - /** * Don't stop walking forward when you need to break blocks in your way */ @@ -938,6 +963,51 @@ public final class Settings { */ public final Setting colorGoalBox = new Setting<>(Color.GREEN); + /** + * The color of the goal box when it's inverted + */ + public final Setting colorInvertedGoalBox = new Setting<>(Color.RED); + + /** + * The color of all selections + */ + public final Setting colorSelection = new Setting<>(Color.CYAN); + + /** + * The color of the selection pos 1 + */ + public final Setting colorSelectionPos1 = new Setting<>(Color.BLACK); + + /** + * The color of the selection pos 2 + */ + public final Setting colorSelectionPos2 = new Setting<>(Color.ORANGE); + + /** + * The opacity of the selection. 0 is completely transparent, 1 is completely opaque + */ + public final Setting selectionOpacity = new Setting<>(.5f); + + /** + * Line width of the goal when rendered, in pixels + */ + public final Setting selectionLineWidth = new Setting<>(2F); + + /** + * Render selections + */ + public final Setting renderSelection = new Setting<>(true); + + /** + * Ignore depth when rendering selections + */ + public final Setting renderSelectionIgnoreDepth = new Setting<>(true); + + /** + * Render selection corners + */ + public final Setting renderSelectionCorners = new Setting<>(true); + /** * A map of lowercase setting field names to their respective setting @@ -952,6 +1022,7 @@ public final class Settings { public final Map, Type> settingTypes; public final class Setting { + public T value; public final T defaultValue; private String name; diff --git a/src/api/java/baritone/api/accessor/IGuiScreen.java b/src/api/java/baritone/api/accessor/IGuiScreen.java new file mode 100644 index 00000000..ba01e275 --- /dev/null +++ b/src/api/java/baritone/api/accessor/IGuiScreen.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +import java.net.URI; + +public interface IGuiScreen { + + void openLink(URI url); +} diff --git a/src/api/java/baritone/api/accessor/IItemStack.java b/src/api/java/baritone/api/accessor/IItemStack.java new file mode 100644 index 00000000..480c713f --- /dev/null +++ b/src/api/java/baritone/api/accessor/IItemStack.java @@ -0,0 +1,23 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +public interface IItemStack { + + int getBaritoneHash(); +} diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java index 15777448..5444bb83 100644 --- a/src/api/java/baritone/api/behavior/IPathingBehavior.java +++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java @@ -64,9 +64,17 @@ public interface IPathingBehavior extends IBehavior { Goal getGoal(); /** - * @return Whether or not a path is currently being executed. + * @return Whether or not a path is currently being executed. This will be false if there's currently a pause. + * @see #hasPath() */ - default boolean isPathing() { + boolean isPathing(); + + /** + * @return If there is a current path. Note that the path is not necessarily being executed, for example when there + * is a pause in effect. + * @see #isPathing() + */ + default boolean hasPath() { return getCurrent() != null; } diff --git a/src/api/java/baritone/api/cache/IWaypoint.java b/src/api/java/baritone/api/cache/IWaypoint.java index 01df2a48..ff235099 100644 --- a/src/api/java/baritone/api/cache/IWaypoint.java +++ b/src/api/java/baritone/api/cache/IWaypoint.java @@ -17,12 +17,9 @@ package baritone.api.cache; -import net.minecraft.util.math.BlockPos; -import org.apache.commons.lang3.ArrayUtils; +import baritone.api.utils.BetterBlockPos; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * A marker for a position in the world. @@ -60,7 +57,7 @@ public interface IWaypoint { * * @return The block position of this waypoint */ - BlockPos getLocation(); + BetterBlockPos getLocation(); enum Tag { @@ -92,20 +89,48 @@ public interface IWaypoint { /** * The names for the tag, anything that the tag can be referred to as. */ - private final String[] names; + public final String[] names; Tag(String... names) { this.names = names; } /** - * Finds a tag from one of the names that could be used to identify said tag. - * - * @param name The name of the tag - * @return The tag, if one is found, otherwise, {@code null} + * @return A name that can be passed to {@link #getByName(String)} to retrieve this tag */ - public static Tag fromString(String name) { - return TAG_LIST.stream().filter(tag -> ArrayUtils.contains(tag.names, name.toLowerCase())).findFirst().orElse(null); + public String getName() { + return names[0]; + } + + /** + * Gets a tag by one of its names. + * + * @param name The name to search for. + * @return The tag, if found, or null. + */ + public static Tag getByName(String name) { + for (Tag action : Tag.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + + return null; + } + + /** + * @return All tag names. + */ + public static String[] getAllNames() { + Set names = new HashSet<>(); + + for (Tag tag : Tag.values()) { + names.addAll(Arrays.asList(tag.names)); + } + + return names.toArray(new String[0]); } } } diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java index caa44cbc..325d4bd0 100644 --- a/src/api/java/baritone/api/cache/IWorldScanner.java +++ b/src/api/java/baritone/api/cache/IWorldScanner.java @@ -17,6 +17,7 @@ package baritone.api.cache; +import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import net.minecraft.block.Block; import net.minecraft.util.math.BlockPos; @@ -33,28 +34,53 @@ public interface IWorldScanner { /** * Scans the world, up to the specified max chunk radius, for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon - * @param blocks The blocks to scan for + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @param maxSearchRadius The maximum chunk search radius * @return The matching block positions */ - List scanChunkRadius(IPlayerContext ctx, List blocks, int max, int yLevelThreshold, int maxSearchRadius); + List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius); + + default List scanChunkRadius(IPlayerContext ctx, List filter, int max, int yLevelThreshold, int maxSearchRadius) { + return scanChunkRadius(ctx, new BlockOptionalMetaLookup(filter.toArray(new Block[0])), max, yLevelThreshold, maxSearchRadius); + } /** * Scans a single chunk for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for + * @param pos The position of the target chunk + * @param max The maximum number of blocks to scan before cutoff + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. + * @return The matching block positions + */ + List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold); + + /** + * Scans a single chunk for the specified blocks. + * + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param blocks The blocks to scan for * @param pos The position of the target chunk * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @return The matching block positions */ - List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold); + default List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) { + return scanChunk(ctx, new BlockOptionalMetaLookup(blocks), pos, max, yLevelThreshold); + } + + /** + * Repacks 40 chunks around the player. + * + * @param ctx The player context for that player. + * @return The number of chunks queued for repacking. + */ + int repack(IPlayerContext ctx); } diff --git a/src/api/java/baritone/api/cache/Waypoint.java b/src/api/java/baritone/api/cache/Waypoint.java index 357028d2..9c4fbfbf 100644 --- a/src/api/java/baritone/api/cache/Waypoint.java +++ b/src/api/java/baritone/api/cache/Waypoint.java @@ -18,7 +18,6 @@ package baritone.api.cache; import baritone.api.utils.BetterBlockPos; -import net.minecraft.util.math.BlockPos; import java.util.Date; @@ -32,9 +31,9 @@ public class Waypoint implements IWaypoint { private final String name; private final Tag tag; private final long creationTimestamp; - private final BlockPos location; + private final BetterBlockPos location; - public Waypoint(String name, Tag tag, BlockPos location) { + public Waypoint(String name, Tag tag, BetterBlockPos location) { this(name, tag, location, System.currentTimeMillis()); } @@ -47,7 +46,7 @@ public class Waypoint implements IWaypoint { * @param location The waypoint location * @param creationTimestamp When the waypoint was created */ - public Waypoint(String name, Tag tag, BlockPos location, long creationTimestamp) { + public Waypoint(String name, Tag tag, BetterBlockPos location, long creationTimestamp) { this.name = name; this.tag = tag; this.location = location; @@ -56,7 +55,7 @@ public class Waypoint implements IWaypoint { @Override public int hashCode() { - return name.hashCode() + tag.hashCode() + location.hashCode(); //lol + return name.hashCode() ^ tag.hashCode() ^ location.hashCode() ^ Long.hashCode(creationTimestamp); } @Override @@ -75,17 +74,17 @@ public class Waypoint implements IWaypoint { } @Override - public BlockPos getLocation() { + public BetterBlockPos getLocation() { return this.location; } @Override public String toString() { return String.format( - "%s %s %s", - name, - BetterBlockPos.from(location).toString(), - new Date(creationTimestamp).toString() + "%s %s %s", + name, + BetterBlockPos.from(location).toString(), + new Date(creationTimestamp).toString() ); } 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..fea1da29 --- /dev/null +++ b/src/api/java/baritone/api/event/events/TabCompleteEvent.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..cbad9b99 --- /dev/null +++ b/src/api/java/baritone/api/event/events/type/Overrideable.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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=%b,value=%s}", + 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..e7d4dc29 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..b7522125 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#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/GoalBlock.java b/src/api/java/baritone/api/pathing/goals/GoalBlock.java index 836b47df..c85e5cad 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalBlock.java @@ -69,10 +69,10 @@ public class GoalBlock implements Goal, IGoalRenderPos { @Override public String toString() { return String.format( - "GoalBlock{x=%s,y=%s,z=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z) + "GoalBlock{x=%s,y=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z) ); } diff --git a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java index 7fe64a4f..8d6fdcb3 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java @@ -63,10 +63,10 @@ public class GoalGetToBlock implements Goal, IGoalRenderPos { @Override public String toString() { return String.format( - "GoalGetToBlock{x=%s,y=%s,z=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z) + "GoalGetToBlock{x=%s,y=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z) ); } } 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..19736924 --- /dev/null +++ b/src/api/java/baritone/api/pathing/goals/GoalInverted.java @@ -0,0 +1,52 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.pathing.goals; + +/** + * Invert any goal. + *

+ * In the old chat control system, #invert just tried to pick a {@link GoalRunAway} that effectively inverted the + * current goal. This goal just reverses the heuristic to act as a TRUE invert. Inverting a Y level? Baritone tries to + * get away from that Y level. Inverting a GoalBlock? Baritone will try to make distance whether it's in the X, Y or Z + * directions. And of course, you can always invert a GoalXZ. + * + * @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/GoalNear.java b/src/api/java/baritone/api/pathing/goals/GoalNear.java index 1482b904..73d64ed9 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalNear.java +++ b/src/api/java/baritone/api/pathing/goals/GoalNear.java @@ -22,6 +22,7 @@ import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.util.math.BlockPos; public class GoalNear implements Goal, IGoalRenderPos { + private final int x; private final int y; private final int z; @@ -58,11 +59,11 @@ public class GoalNear implements Goal, IGoalRenderPos { @Override public String toString() { return String.format( - "GoalNear{x=%s, y=%s, z=%s, rangeSq=%d}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z), - rangeSq + "GoalNear{x=%s, y=%s, z=%s, rangeSq=%d}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z), + rangeSq ); } } diff --git a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java index fa9c29fc..a1a15378 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java +++ b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java @@ -84,9 +84,9 @@ public class GoalRunAway implements Goal { public String toString() { if (maintainY != null) { return String.format( - "GoalRunAwayFromMaintainY y=%s, %s", - SettingsUtil.maybeCensor(maintainY), - Arrays.asList(from) + "GoalRunAwayFromMaintainY y=%s, %s", + SettingsUtil.maybeCensor(maintainY), + Arrays.asList(from) ); } else { return "GoalRunAwayFrom" + Arrays.asList(from); diff --git a/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java index 3f14bf24..b48a029a 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java +++ b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java @@ -25,6 +25,7 @@ import net.minecraft.util.math.BlockPos; * Dig a tunnel in a certain direction, but if you have to deviate from the path, go back to where you started */ public class GoalStrictDirection implements Goal { + public final int x; public final int y; public final int z; @@ -66,12 +67,12 @@ public class GoalStrictDirection implements Goal { @Override public String toString() { return String.format( - "GoalStrictDirection{x=%s, y=%s, z=%s, dx=%s, dz=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z), - SettingsUtil.maybeCensor(dx), - SettingsUtil.maybeCensor(dz) + "GoalStrictDirection{x=%s, y=%s, z=%s, dx=%s, dz=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z), + SettingsUtil.maybeCensor(dx), + SettingsUtil.maybeCensor(dz) ); } } diff --git a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java index 52286dd0..27be981e 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java +++ b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java @@ -75,10 +75,10 @@ public class GoalTwoBlocks implements Goal, IGoalRenderPos { @Override public String toString() { return String.format( - "GoalTwoBlocks{x=%s,y=%s,z=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z) + "GoalTwoBlocks{x=%s,y=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z) ); } } diff --git a/src/api/java/baritone/api/pathing/goals/GoalXZ.java b/src/api/java/baritone/api/pathing/goals/GoalXZ.java index 65d506f8..63d39cd7 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 baritone.api.utils.SettingsUtil; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -46,6 +47,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; @@ -61,9 +67,9 @@ public class GoalXZ implements Goal { @Override public String toString() { return String.format( - "GoalXZ{x=%s,z=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(z) + "GoalXZ{x=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(z) ); } @@ -92,7 +98,7 @@ public class GoalXZ implements Goal { float theta = (float) Math.toRadians(yaw); double x = origin.x - MathHelper.sin(theta) * distance; double z = origin.z + MathHelper.cos(theta) * distance; - return new GoalXZ((int) x, (int) z); + return new GoalXZ(MathHelper.floor(x), MathHelper.floor(z)); } public int getX() { diff --git a/src/api/java/baritone/api/pathing/goals/GoalYLevel.java b/src/api/java/baritone/api/pathing/goals/GoalYLevel.java index 84b4cefe..603ef9bd 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalYLevel.java +++ b/src/api/java/baritone/api/pathing/goals/GoalYLevel.java @@ -61,8 +61,8 @@ public class GoalYLevel implements Goal, ActionCosts { @Override public String toString() { return String.format( - "GoalYLevel{y=%s}", - SettingsUtil.maybeCensor(level) + "GoalYLevel{y=%s}", + SettingsUtil.maybeCensor(level) ); } } diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java index f73f0e74..c48c2a04 100644 --- a/src/api/java/baritone/api/process/IBuilderProcess.java +++ b/src/api/java/baritone/api/process/IBuilderProcess.java @@ -18,11 +18,13 @@ package baritone.api.process; import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3i; import java.io.File; +import java.util.List; /** * @author Brady @@ -58,7 +60,16 @@ public interface IBuilderProcess extends IBaritoneProcess { void pause(); + boolean isPaused(); + void resume(); void clearArea(BlockPos corner1, BlockPos corner2); + + /** + * @return A list of block states that are estimated to be placeable by this builder process. You can use this in + * schematics, for example, to pick a state that the builder process will be happy with, because any variation will + * cause it to give up. This is updated every tick, but only while the builder process is active. + */ + List getApproxPlaceable(); } diff --git a/src/api/java/baritone/api/process/IExploreProcess.java b/src/api/java/baritone/api/process/IExploreProcess.java index 811a509f..4950d31d 100644 --- a/src/api/java/baritone/api/process/IExploreProcess.java +++ b/src/api/java/baritone/api/process/IExploreProcess.java @@ -20,6 +20,7 @@ package baritone.api.process; import java.nio.file.Path; public interface IExploreProcess extends IBaritoneProcess { + void explore(int centerX, int centerZ); void applyJsonFilter(Path path, boolean invert) throws Exception; diff --git a/src/api/java/baritone/api/process/IFarmProcess.java b/src/api/java/baritone/api/process/IFarmProcess.java index c9234a24..6ced16fb 100644 --- a/src/api/java/baritone/api/process/IFarmProcess.java +++ b/src/api/java/baritone/api/process/IFarmProcess.java @@ -18,5 +18,6 @@ package baritone.api.process; public interface IFarmProcess extends IBaritoneProcess { + void farm(); } diff --git a/src/api/java/baritone/api/process/IMineProcess.java b/src/api/java/baritone/api/process/IMineProcess.java index 7ebabc9c..9cd76a09 100644 --- a/src/api/java/baritone/api/process/IMineProcess.java +++ b/src/api/java/baritone/api/process/IMineProcess.java @@ -17,8 +17,12 @@ package baritone.api.process; +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.BlockOptionalMetaLookup; import net.minecraft.block.Block; +import java.util.stream.Stream; + /** * @author Brady * @since 9/23/2018 @@ -28,9 +32,9 @@ 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 +45,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 blocks to mine */ - void mine(int quantity, Block... blocks); + void mine(int quantity, BlockOptionalMetaLookup filter); + + /** + * Begin to search for and mine the specified blocks. + * + * @param filter The blocks to mine + */ + default void mine(BlockOptionalMetaLookup filter) { + mine(0, filter); + } /** * Begin to search for and mine the specified blocks. @@ -54,6 +67,38 @@ public interface IMineProcess extends IBaritoneProcess { mineByName(0, blocks); } + /** + * Begin to search for and mine the specified blocks. + * + * @param boms The blocks to mine + */ + default void mine(int quantity, BlockOptionalMeta... boms) { + mine(quantity, new BlockOptionalMetaLookup(boms)); + } + + /** + * Begin to search for and mine the specified blocks. + * + * @param boms The blocks to mine + */ + default void mine(BlockOptionalMeta... boms) { + mine(0, boms); + } + + /** + * 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 BlockOptionalMetaLookup( + Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new) + )); + } + /** * Begin to search for and mine the specified blocks. * diff --git a/src/api/java/baritone/api/schematic/AbstractSchematic.java b/src/api/java/baritone/api/schematic/AbstractSchematic.java new file mode 100644 index 00000000..7a24e803 --- /dev/null +++ b/src/api/java/baritone/api/schematic/AbstractSchematic.java @@ -0,0 +1,48 @@ +/* + * 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.schematic; + +import baritone.api.utils.ISchematic; + +public abstract class AbstractSchematic implements ISchematic { + + protected int x; + protected int y; + protected int z; + + public AbstractSchematic(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public int widthX() { + return x; + } + + @Override + public int heightY() { + return y; + } + + @Override + public int lengthZ() { + return z; + } +} diff --git a/src/api/java/baritone/api/schematic/CompositeSchematic.java b/src/api/java/baritone/api/schematic/CompositeSchematic.java new file mode 100644 index 00000000..21df29b4 --- /dev/null +++ b/src/api/java/baritone/api/schematic/CompositeSchematic.java @@ -0,0 +1,75 @@ +/* + * 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.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +import java.util.ArrayList; +import java.util.List; + +public class CompositeSchematic extends AbstractSchematic { + + private final List schematics; + private CompositeSchematicEntry[] schematicArr; + + private void recalcArr() { + schematicArr = schematics.toArray(new CompositeSchematicEntry[0]); + for (CompositeSchematicEntry entry : schematicArr) { + this.x = Math.max(x, entry.x + entry.schematic.widthX()); + this.y = Math.max(y, entry.y + entry.schematic.heightY()); + this.z = Math.max(z, entry.z + entry.schematic.lengthZ()); + } + } + + public CompositeSchematic(int x, int y, int z) { + super(x, y, z); + schematics = new ArrayList<>(); + recalcArr(); + } + + public void put(ISchematic extra, int x, int y, int z) { + schematics.add(new CompositeSchematicEntry(extra, x, y, z)); + recalcArr(); + } + + private CompositeSchematicEntry getSchematic(int x, int y, int z, IBlockState currentState) { + for (CompositeSchematicEntry entry : schematicArr) { + if (x >= entry.x && y >= entry.y && z >= entry.z && + entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState)) { + return entry; + } + } + return null; + } + + @Override + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + CompositeSchematicEntry entry = getSchematic(x, y, z, currentState); + return entry != null && entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState); + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + CompositeSchematicEntry entry = getSchematic(x, y, z, current); + if (entry == null) { + throw new IllegalStateException("couldn't find schematic for this position"); + } + return entry.schematic.desiredState(x - entry.x, y - entry.y, z - entry.z, current, approxPlaceable); + } +} diff --git a/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java new file mode 100644 index 00000000..a26f356b --- /dev/null +++ b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java @@ -0,0 +1,35 @@ +/* + * 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.schematic; + +import baritone.api.utils.ISchematic; + +public class CompositeSchematicEntry { + + public final ISchematic schematic; + public final int x; + public final int y; + public final int z; + + public CompositeSchematicEntry(ISchematic schematic, int x, int y, int z) { + this.schematic = schematic; + this.x = x; + this.y = y; + this.z = z; + } +} diff --git a/src/api/java/baritone/api/schematic/FillSchematic.java b/src/api/java/baritone/api/schematic/FillSchematic.java new file mode 100644 index 00000000..aaaeb5dd --- /dev/null +++ b/src/api/java/baritone/api/schematic/FillSchematic.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.BlockOptionalMeta; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; + +import java.util.List; + +public class FillSchematic extends AbstractSchematic { + + private final BlockOptionalMeta bom; + + public FillSchematic(int x, int y, int z, BlockOptionalMeta bom) { + super(x, y, z); + this.bom = bom; + } + + public BlockOptionalMeta getBom() { + return bom; + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + if (bom.matches(current)) { + return current; + } else if (current.getBlock() != Blocks.AIR) { + return Blocks.AIR.getDefaultState(); + } + for (IBlockState placeable : approxPlaceable) { + if (bom.matches(placeable)) { + return placeable; + } + } + return bom.getAnyBlockState(); + } +} diff --git a/src/api/java/baritone/api/schematic/MaskSchematic.java b/src/api/java/baritone/api/schematic/MaskSchematic.java new file mode 100644 index 00000000..fa9b8126 --- /dev/null +++ b/src/api/java/baritone/api/schematic/MaskSchematic.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.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +import java.util.List; + +public abstract class MaskSchematic extends AbstractSchematic { + + private final ISchematic schematic; + + public MaskSchematic(ISchematic schematic) { + super(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); + this.schematic = schematic; + } + + protected abstract boolean partOfMask(int x, int y, int z, IBlockState currentState); + + @Override + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + return schematic.inSchematic(x, y, z, currentState) && partOfMask(x, y, z, currentState); + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return schematic.desiredState(x, y, z, current, approxPlaceable); + } +} diff --git a/src/api/java/baritone/api/schematic/ReplaceSchematic.java b/src/api/java/baritone/api/schematic/ReplaceSchematic.java new file mode 100644 index 00000000..988cfd7f --- /dev/null +++ b/src/api/java/baritone/api/schematic/ReplaceSchematic.java @@ -0,0 +1,42 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.BlockOptionalMetaLookup; +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class ReplaceSchematic extends MaskSchematic { + + private final BlockOptionalMetaLookup filter; + private final Boolean[][][] cache; + + public ReplaceSchematic(ISchematic schematic, BlockOptionalMetaLookup filter) { + super(schematic); + this.filter = filter; + this.cache = new Boolean[widthX()][heightY()][lengthZ()]; + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + if (cache[x][y][z] == null) { + cache[x][y][z] = filter.has(currentState); + } + return cache[x][y][z]; + } +} diff --git a/src/api/java/baritone/api/schematic/ShellSchematic.java b/src/api/java/baritone/api/schematic/ShellSchematic.java new file mode 100644 index 00000000..4ead61b6 --- /dev/null +++ b/src/api/java/baritone/api/schematic/ShellSchematic.java @@ -0,0 +1,33 @@ +/* + * 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.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class ShellSchematic extends MaskSchematic { + + public ShellSchematic(ISchematic schematic) { + super(schematic); + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + return x == 0 || y == 0 || z == 0 || x == widthX() - 1 || y == heightY() - 1 || z == lengthZ() - 1; + } +} diff --git a/src/api/java/baritone/api/schematic/WallsSchematic.java b/src/api/java/baritone/api/schematic/WallsSchematic.java new file mode 100644 index 00000000..e05de1e7 --- /dev/null +++ b/src/api/java/baritone/api/schematic/WallsSchematic.java @@ -0,0 +1,33 @@ +/* + * 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.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class WallsSchematic extends MaskSchematic { + + public WallsSchematic(ISchematic schematic) { + super(schematic); + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + return x == 0 || z == 0 || x == widthX() - 1 || z == lengthZ() - 1; + } +} diff --git a/src/api/java/baritone/api/selection/ISelection.java b/src/api/java/baritone/api/selection/ISelection.java new file mode 100644 index 00000000..59651feb --- /dev/null +++ b/src/api/java/baritone/api/selection/ISelection.java @@ -0,0 +1,91 @@ +/* + * 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.selection; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3i; + +/** + * A selection is an immutable object representing the current selection. The selection is commonly used for certain + * types of build commands, however it can be used for anything. + */ +public interface ISelection { + + /** + * @return The first corner of this selection. This is meant to preserve the user's original first corner. + */ + BetterBlockPos pos1(); + + /** + * @return The second corner of this selection. This is meant to preserve the user's original second corner. + */ + BetterBlockPos pos2(); + + /** + * @return The {@link BetterBlockPos} with the lowest x, y, and z position in the selection. + */ + BetterBlockPos min(); + + /** + * @return The opposite corner from the {@link #min()}. + */ + BetterBlockPos max(); + + /** + * @return The size of this ISelection. + */ + Vec3i size(); + + /** + * @return An {@link AxisAlignedBB} encompassing all blocks in this selection. + */ + AxisAlignedBB aabb(); + + /** + * Returns a new {@link ISelection} expanded in the specified direction by the specified number of blocks. + * + * @param direction The direction to expand the selection. + * @param blocks How many blocks to expand it. + * @return A new selection, expanded as specified. + */ + ISelection expand(EnumFacing direction, int blocks); + + /** + * Returns a new {@link ISelection} contracted in the specified direction by the specified number of blocks. + *

+ * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it + * is DOWN, the top of the selection will be shifted down. + * + * @param direction The direction to contract the selection. + * @param blocks How many blocks to contract it. + * @return A new selection, contracted as specified. + */ + ISelection contract(EnumFacing direction, int blocks); + + /** + * Returns a new {@link ISelection} shifted in the specified direction by the specified number of blocks. This moves + * the whole selection. + * + * @param direction The direction to shift the selection. + * @param blocks How many blocks to shift it. + * @return A new selection, shifted as specified. + */ + ISelection shift(EnumFacing direction, int blocks); +} diff --git a/src/api/java/baritone/api/selection/ISelectionManager.java b/src/api/java/baritone/api/selection/ISelectionManager.java new file mode 100644 index 00000000..de5ee288 --- /dev/null +++ b/src/api/java/baritone/api/selection/ISelectionManager.java @@ -0,0 +1,116 @@ +/* + * 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.selection; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; + +/** + * The selection manager handles setting Baritone's selections. You can set the selection here, as well as retrieving + * the current selection. + */ +public interface ISelectionManager { + + /** + * Adds a new selection. The added selection is returned. + * + * @param selection The new selection to add. + */ + ISelection addSelection(ISelection selection); + + /** + * Adds a new {@link ISelection} constructed from the given block positions. The new selection is returned. + * + * @param pos1 One corner of the selection + * @param pos2 The new corner of the selection + */ + ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2); + + /** + * Removes the selection from the current selections. + * + * @param selection The selection to remove. + * @return The removed selection. + */ + ISelection removeSelection(ISelection selection); + + /** + * Removes all selections. + * + * @return The selections that were removed, sorted from oldest to newest.. + */ + ISelection[] removeAllSelections(); + + /** + * @return The current selections, sorted from oldest to newest. + */ + ISelection[] getSelections(); + + /** + * For anything expecting only one selection, this method is provided. However, to enforce multi-selection support, + * this method will only return a selection if there is ONLY one. + * + * @return The only selection, or null if there isn't only one. + */ + ISelection getOnlySelection(); + + /** + * This method will always return the last selection. ONLY use this if you want to, for example, modify the most + * recent selection based on user input. ALWAYS use {@link #getOnlySelection()} or, ideally, + * {@link #getSelections()} for retrieving the content of selections. + * + * @return The last selection, or null if it doesn't exist. + */ + ISelection getLastSelection(); + + /** + * Replaces the specified {@link ISelection} with one expanded in the specified direction by the specified number of + * blocks. Returns the new selection. + * + * @param selection The selection to expand. + * @param direction The direction to expand the selection. + * @param blocks How many blocks to expand it. + * @return The new selection, expanded as specified. + */ + ISelection expand(ISelection selection, EnumFacing direction, int blocks); + + /** + * Replaces the specified {@link ISelection} with one contracted in the specified direction by the specified number + * of blocks. + *

+ * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it + * is DOWN, the top of the selection will be shifted down. + * + * @param selection The selection to contract. + * @param direction The direction to contract the selection. + * @param blocks How many blocks to contract it. + * @return The new selection, contracted as specified. + */ + ISelection contract(ISelection selection, EnumFacing direction, int blocks); + + /** + * Replaces the specified {@link ISelection} with one shifted in the specified direction by the specified number of + * blocks. This moves the whole selection. + * + * @param selection The selection to shift. + * @param direction The direction to shift the selection. + * @param blocks How many blocks to shift it. + * @return The new selection, shifted as specified. + */ + ISelection shift(ISelection selection, EnumFacing direction, int blocks); +} diff --git a/src/api/java/baritone/api/utils/BetterBlockPos.java b/src/api/java/baritone/api/utils/BetterBlockPos.java index 6a462d0c..a9b02445 100644 --- a/src/api/java/baritone/api/utils/BetterBlockPos.java +++ b/src/api/java/baritone/api/utils/BetterBlockPos.java @@ -34,6 +34,9 @@ import javax.annotation.Nonnull; * @author leijurv */ public final class BetterBlockPos extends BlockPos { + + public static final BetterBlockPos ORIGIN = new BetterBlockPos(0, 0, 0); + public final int x; public final int y; public final int z; @@ -203,10 +206,10 @@ public final class BetterBlockPos extends BlockPos { @Nonnull public String toString() { return String.format( - "BetterBlockPos{x=%s,y=%s,z=%s}", - SettingsUtil.maybeCensor(x), - SettingsUtil.maybeCensor(y), - SettingsUtil.maybeCensor(z) + "BetterBlockPos{x=%s,y=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z) ); } } diff --git a/src/api/java/baritone/api/utils/BlockOptionalMeta.java b/src/api/java/baritone/api/utils/BlockOptionalMeta.java new file mode 100644 index 00000000..c65b6d3d --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockOptionalMeta.java @@ -0,0 +1,299 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import baritone.api.accessor.IItemStack; +import com.google.common.collect.ImmutableSet; +import net.minecraft.block.*; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Consumer; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public final class BlockOptionalMeta { + + private final Block block; + private final int meta; + private final boolean noMeta; + private final Set blockstates; + private final ImmutableSet stateHashes; + private final ImmutableSet stackHashes; + private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$"); + private static final Map normalizations; + + public BlockOptionalMeta(@Nonnull Block block, @Nullable Integer meta) { + this.block = block; + this.noMeta = meta == null; + this.meta = noMeta ? 0 : meta; + this.blockstates = getStates(block, meta); + this.stateHashes = getStateHashes(blockstates); + this.stackHashes = getStackHashes(blockstates); + } + + public BlockOptionalMeta(@Nonnull Block block) { + this(block, null); + } + + public BlockOptionalMeta(@Nonnull String selector) { + Matcher matcher = pattern.matcher(selector); + + if (!matcher.find()) { + throw new IllegalArgumentException("invalid block selector"); + } + + MatchResult matchResult = matcher.toMatchResult(); + noMeta = matchResult.group(2) == null; + + ResourceLocation id = new ResourceLocation(matchResult.group(1)); + + if (!Block.REGISTRY.containsKey(id)) { + throw new IllegalArgumentException("Invalid block ID"); + } + + block = Block.REGISTRY.getObject(id); + meta = noMeta ? 0 : Integer.parseInt(matchResult.group(2)); + blockstates = getStates(block, getMeta()); + stateHashes = getStateHashes(blockstates); + stackHashes = getStackHashes(blockstates); + } + + static { + Map _normalizations = new HashMap<>(); + Consumer put = instance -> _normalizations.put(instance.getClass(), instance); + put.accept(EnumFacing.NORTH); + put.accept(EnumFacing.Axis.Y); + put.accept(BlockLog.EnumAxis.Y); + put.accept(BlockStairs.EnumHalf.BOTTOM); + put.accept(BlockStairs.EnumShape.STRAIGHT); + put.accept(BlockLever.EnumOrientation.DOWN_X); + put.accept(BlockDoublePlant.EnumBlockHalf.LOWER); + put.accept(BlockSlab.EnumBlockHalf.BOTTOM); + put.accept(BlockDoor.EnumDoorHalf.LOWER); + put.accept(BlockDoor.EnumHingePosition.LEFT); + put.accept(BlockBed.EnumPartType.HEAD); + put.accept(BlockRailBase.EnumRailDirection.NORTH_SOUTH); + put.accept(BlockTrapDoor.DoorHalf.BOTTOM); + _normalizations.put(BlockBanner.ROTATION, 0); + _normalizations.put(BlockBed.OCCUPIED, false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[0], false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[1], false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[2], false); + _normalizations.put(BlockButton.POWERED, false); + // _normalizations.put(BlockCactus.AGE, 0); + // _normalizations.put(BlockCauldron.LEVEL, 0); + // _normalizations.put(BlockChorusFlower.AGE, 0); + _normalizations.put(BlockChorusPlant.NORTH, false); + _normalizations.put(BlockChorusPlant.EAST, false); + _normalizations.put(BlockChorusPlant.SOUTH, false); + _normalizations.put(BlockChorusPlant.WEST, false); + _normalizations.put(BlockChorusPlant.UP, false); + _normalizations.put(BlockChorusPlant.DOWN, false); + // _normalizations.put(BlockCocoa.AGE, 0); + // _normalizations.put(BlockCrops.AGE, 0); + _normalizations.put(BlockDirt.SNOWY, false); + _normalizations.put(BlockDoor.OPEN, false); + _normalizations.put(BlockDoor.POWERED, false); + // _normalizations.put(BlockFarmland.MOISTURE, 0); + _normalizations.put(BlockFence.NORTH, false); + _normalizations.put(BlockFence.EAST, false); + _normalizations.put(BlockFence.WEST, false); + _normalizations.put(BlockFence.SOUTH, false); + // _normalizations.put(BlockFenceGate.POWERED, false); + // _normalizations.put(BlockFenceGate.IN_WALL, false); + _normalizations.put(BlockFire.AGE, 0); + _normalizations.put(BlockFire.NORTH, false); + _normalizations.put(BlockFire.EAST, false); + _normalizations.put(BlockFire.SOUTH, false); + _normalizations.put(BlockFire.WEST, false); + _normalizations.put(BlockFire.UPPER, false); + // _normalizations.put(BlockFrostedIce.AGE, 0); + _normalizations.put(BlockGrass.SNOWY, false); + // _normalizations.put(BlockHopper.ENABLED, true); + // _normalizations.put(BlockLever.POWERED, false); + // _normalizations.put(BlockLiquid.LEVEL, 0); + // _normalizations.put(BlockMycelium.SNOWY, false); + // _normalizations.put(BlockNetherWart.AGE, false); + _normalizations.put(BlockLeaves.CHECK_DECAY, false); + // _normalizations.put(BlockLeaves.DECAYABLE, false); + // _normalizations.put(BlockObserver.POWERED, false); + _normalizations.put(BlockPane.NORTH, false); + _normalizations.put(BlockPane.EAST, false); + _normalizations.put(BlockPane.WEST, false); + _normalizations.put(BlockPane.SOUTH, false); + // _normalizations.put(BlockPistonBase.EXTENDED, false); + // _normalizations.put(BlockPressurePlate.POWERED, false); + // _normalizations.put(BlockPressurePlateWeighted.POWER, false); + _normalizations.put(BlockQuartz.EnumType.LINES_X, BlockQuartz.EnumType.LINES_Y); + _normalizations.put(BlockQuartz.EnumType.LINES_Z, BlockQuartz.EnumType.LINES_Y); + // _normalizations.put(BlockRailDetector.POWERED, false); + // _normalizations.put(BlockRailPowered.POWERED, false); + _normalizations.put(BlockRedstoneWire.NORTH, false); + _normalizations.put(BlockRedstoneWire.EAST, false); + _normalizations.put(BlockRedstoneWire.SOUTH, false); + _normalizations.put(BlockRedstoneWire.WEST, false); + // _normalizations.put(BlockReed.AGE, false); + _normalizations.put(BlockSapling.STAGE, 0); + _normalizations.put(BlockSkull.NODROP, false); + _normalizations.put(BlockStandingSign.ROTATION, 0); + _normalizations.put(BlockStem.AGE, 0); + _normalizations.put(BlockTripWire.NORTH, false); + _normalizations.put(BlockTripWire.EAST, false); + _normalizations.put(BlockTripWire.WEST, false); + _normalizations.put(BlockTripWire.SOUTH, false); + _normalizations.put(BlockVine.NORTH, false); + _normalizations.put(BlockVine.EAST, false); + _normalizations.put(BlockVine.SOUTH, false); + _normalizations.put(BlockVine.WEST, false); + _normalizations.put(BlockVine.UP, false); + _normalizations.put(BlockWall.UP, false); + _normalizations.put(BlockWall.NORTH, false); + _normalizations.put(BlockWall.EAST, false); + _normalizations.put(BlockWall.WEST, false); + _normalizations.put(BlockWall.SOUTH, false); + normalizations = Collections.unmodifiableMap(_normalizations); + } + + private static , P extends IProperty> P castToIProperty(Object value) { + //noinspection unchecked + return (P) value; + } + + private static , P extends IProperty> C castToIPropertyValue(P iproperty, Object value) { + //noinspection unchecked + return (C) value; + } + + public static IBlockState normalize(IBlockState state) { + IBlockState newState = state; + + for (IProperty property : state.getProperties().keySet()) { + Class valueClass = property.getValueClass(); + if (normalizations.containsKey(property)) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(property)) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(state.getValue(property))) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(state.getValue(property))) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(valueClass)) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(valueClass)) + ); + } catch (IllegalArgumentException ignored) {} + } + } + + return newState; + } + + public static int stateMeta(IBlockState state) { + return state.getBlock().getMetaFromState(normalize(state)); + } + + private static Set getStates(@Nonnull Block block, @Nullable Integer meta) { + return block.getBlockState().getValidStates().stream() + .filter(blockstate -> meta == null || stateMeta(blockstate) == meta) + .collect(Collectors.toSet()); + } + + private static ImmutableSet getStateHashes(Set blockstates) { + return ImmutableSet.copyOf( + blockstates.stream() + .map(IBlockState::hashCode) + .toArray(Integer[]::new) + ); + } + + private static ImmutableSet getStackHashes(Set blockstates) { + //noinspection ConstantConditions + return ImmutableSet.copyOf( + blockstates.stream() + .map(state -> new ItemStack( + state.getBlock().getItemDropped(state, new Random(), 0), + state.getBlock().damageDropped(state) + )) + .map(stack -> ((IItemStack) (Object) stack).getBaritoneHash()) + .toArray(Integer[]::new) + ); + } + + public Block getBlock() { + return block; + } + + public Integer getMeta() { + return noMeta ? null : meta; + } + + public boolean matches(@Nonnull Block block) { + return block == this.block; + } + + public boolean matches(@Nonnull IBlockState blockstate) { + Block block = blockstate.getBlock(); + return block == this.block && stateHashes.contains(blockstate.hashCode()); + } + + public boolean matches(ItemStack stack) { + //noinspection ConstantConditions + int hash = ((IItemStack) (Object) stack).getBaritoneHash(); + + if (noMeta) { + hash -= stack.getItemDamage(); + } + + return stackHashes.contains(hash); + } + + @Override + public String toString() { + return String.format("BlockOptionalMeta{block=%s,meta=%s}", block, getMeta()); + } + + public static IBlockState blockStateFromStack(ItemStack stack) { + //noinspection deprecation + return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata()); + } + + public IBlockState getAnyBlockState() { + if (blockstates.size() > 0) { + return blockstates.iterator().next(); + } + + return null; + } +} diff --git a/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java new file mode 100644 index 00000000..041c6162 --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java @@ -0,0 +1,95 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class BlockOptionalMetaLookup { + + private final BlockOptionalMeta[] boms; + + public BlockOptionalMetaLookup(BlockOptionalMeta... boms) { + this.boms = boms; + } + + public BlockOptionalMetaLookup(Block... blocks) { + this.boms = Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public BlockOptionalMetaLookup(List blocks) { + this.boms = blocks.stream() + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public BlockOptionalMetaLookup(String... blocks) { + this.boms = Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public boolean has(Block block) { + for (BlockOptionalMeta bom : boms) { + if (bom.getBlock() == block) { + return true; + } + } + + return false; + } + + public boolean has(IBlockState state) { + for (BlockOptionalMeta bom : boms) { + if (bom.matches(state)) { + return true; + } + } + + return false; + } + + public boolean has(ItemStack stack) { + for (BlockOptionalMeta bom : boms) { + if (bom.matches(stack)) { + return true; + } + } + + return false; + } + + public List blocks() { + return Arrays.asList(boms); + } + + @Override + public String toString() { + return String.format( + "BlockOptionalMetaLookup{%s}", + Arrays.toString(boms) + ); + } +} diff --git a/src/api/java/baritone/api/utils/BlockUtils.java b/src/api/java/baritone/api/utils/BlockUtils.java index a7e00608..43a97f83 100644 --- a/src/api/java/baritone/api/utils/BlockUtils.java +++ b/src/api/java/baritone/api/utils/BlockUtils.java @@ -22,9 +22,9 @@ import net.minecraft.util.ResourceLocation; import java.util.HashMap; import java.util.Map; -import java.util.Objects; public class BlockUtils { + private static transient Map resourceCache = new HashMap<>(); public static String blockToString(Block block) { @@ -39,7 +39,11 @@ public class BlockUtils { public static Block stringToBlockRequired(String name) { Block block = stringToBlockNullable(name); - Objects.requireNonNull(block); + + if (block == null) { + throw new IllegalArgumentException(String.format("Invalid block name %s", name)); + } + return block; } diff --git a/src/api/java/baritone/api/utils/ExampleBaritoneControl.java b/src/api/java/baritone/api/utils/ExampleBaritoneControl.java deleted file mode 100644 index c63c2462..00000000 --- a/src/api/java/baritone/api/utils/ExampleBaritoneControl.java +++ /dev/null @@ -1,755 +0,0 @@ -/* - * This file is part of Baritone. - * - * Baritone is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Baritone is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Baritone. If not, see . - */ - -package baritone.api.utils; - -import baritone.api.BaritoneAPI; -import baritone.api.IBaritone; -import baritone.api.Settings; -import baritone.api.behavior.IPathingBehavior; -import baritone.api.cache.IRememberedInventory; -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.process.IBaritoneProcess; -import baritone.api.process.ICustomGoalProcess; -import baritone.api.process.IGetToBlockProcess; -import net.minecraft.block.Block; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.crash.CrashReport; -import net.minecraft.entity.Entity; -import net.minecraft.entity.item.EntityItem; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ReportedException; -import net.minecraft.util.math.BlockPos; -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.world.DimensionType; -import net.minecraft.world.chunk.Chunk; - -import java.nio.file.Path; -import java.util.*; - -import static org.apache.commons.lang3.math.NumberUtils.isCreatable; - -public class ExampleBaritoneControl implements Helper, AbstractGameEventListener { - private static final String COMMAND_PREFIX = "#"; - - public final IBaritone baritone; - public final IPlayerContext ctx; - - public ExampleBaritoneControl(IBaritone baritone) { - this.baritone = baritone; - this.ctx = baritone.getPlayerContext(); - baritone.getGameEventHandler().registerEventListener(this); - } - - @Override - public void onSendChatMessage(ChatEvent event) { - String msg = event.getMessage(); - if (BaritoneAPI.getSettings().prefixControl.value && msg.startsWith(COMMAND_PREFIX)) { - if (!runCommand(msg.substring(COMMAND_PREFIX.length()))) { - logDirect("Invalid command"); - } - event.cancel(); // always cancel if using prefixControl - return; - } - if (!BaritoneAPI.getSettings().chatControl.value && !BaritoneAPI.getSettings().removePrefix.value) { - return; - } - if (runCommand(msg)) { - event.cancel(); - } - } - - public boolean runCommand(String msg0) { // you may think this can be private, but impcat calls it from .b =) - String msg = msg0.toLowerCase(Locale.US).trim(); // don't reassign the argument LOL - IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); - ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); - List> toggleable = BaritoneAPI.getSettings().getAllValuesByType(Boolean.class); - for (Settings.Setting setting : toggleable) { - if (msg.equalsIgnoreCase(setting.getName())) { - setting.value ^= true; - logDirect("Toggled " + setting.getName() + " to " + setting.value); - SettingsUtil.save(BaritoneAPI.getSettings()); - return true; - } - } - if (msg.equals("baritone") || msg.equals("modifiedsettings") || msg.startsWith("settings m") || msg.equals("modified")) { - logDirect("All settings that have been modified from their default values:"); - for (Settings.Setting setting : SettingsUtil.modifiedSettings(BaritoneAPI.getSettings())) { - logDirect(setting.toString()); - } - return true; - } - if (msg.startsWith("settings")) { - String rest = msg.substring("settings".length()); - try { - int page = Integer.parseInt(rest.trim()); - int min = page * 10; - int max = Math.min(BaritoneAPI.getSettings().allSettings.size(), (page + 1) * 10); - logDirect("Settings " + min + " to " + (max - 1) + ":"); - for (int i = min; i < max; i++) { - logDirect(BaritoneAPI.getSettings().allSettings.get(i).toString()); - } - } catch (Exception ex) { // NumberFormatException | ArrayIndexOutOfBoundsException and probably some others I'm forgetting lol - ex.printStackTrace(); - logDirect("All settings:"); - for (Settings.Setting setting : BaritoneAPI.getSettings().allSettings) { - logDirect(setting.toString()); - } - logDirect("To get one page of ten settings at a time, do settings "); - } - return true; - } - if (msg.equals("") || msg.equals("help") || msg.equals("?")) { - ITextComponent component = MESSAGE_PREFIX.createCopy(); - 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")); - component.appendSibling(helpLink); - BaritoneAPI.getSettings().logger.value.accept(component); - return true; - } - if (msg.contains(" ")) { - String settingName = msg.substring(0, msg.indexOf(' ')); - String settingValue = msg.substring(msg.indexOf(' ') + 1); - Settings.Setting setting = BaritoneAPI.getSettings().byLowerName.get(settingName); - if (setting != null) { - if (settingValue.equals("reset")) { - logDirect("Resetting setting " + settingName + " to default value."); - setting.reset(); - } else { - try { - SettingsUtil.parseAndApply(BaritoneAPI.getSettings(), settingName, settingValue); - } catch (Exception ex) { - logDirect("Unable to parse setting"); - return true; - } - } - SettingsUtil.save(BaritoneAPI.getSettings()); - logDirect(setting.toString()); - return true; - } - } - if (BaritoneAPI.getSettings().byLowerName.containsKey(msg)) { - Settings.Setting setting = BaritoneAPI.getSettings().byLowerName.get(msg); - logDirect(setting.toString()); - return true; - } - - if (msg.startsWith("goal")) { - String rest = msg.substring(4).trim(); - Goal goal; - if (rest.equals("clear") || rest.equals("none")) { - goal = null; - } else { - String[] params = rest.split(" "); - if (params[0].equals("")) { - params = new String[]{}; - } - goal = parseGoal(params); - if (goal == null) { - return true; - } - } - customGoalProcess.setGoal(goal); - logDirect("Goal: " + goal); - return true; - } - if (msg.equals("crash")) { - StringBuilder meme = new StringBuilder(); - CrashReport rep = new CrashReport("Manually triggered debug crash", new Throwable()); - mc.addGraphicsAndWorldToCrashReport(rep); - new ReportedException(rep).printStackTrace(); - rep.getSectionsInStringBuilder(meme); - System.out.println(meme); - logDirect(meme.toString()); - logDirect("ok"); - return true; - } - if (msg.equals("path")) { - if (pathingBehavior.getGoal() == null) { - logDirect("No goal."); - } else if (pathingBehavior.getGoal().isInGoal(ctx.playerFeet())) { - logDirect("Already in goal"); - } else if (pathingBehavior.isPathing()) { - logDirect("Currently executing a path. Please cancel it first."); - } else { - customGoalProcess.setGoalAndPath(pathingBehavior.getGoal()); - } - return true; - } - /*if (msg.equals("fullpath")) { - if (pathingBehavior.getGoal() == null) { - logDirect("No goal."); - } else { - logDirect("Started segmented calculator"); - SegmentedCalculator.calculateSegmentsThreaded(pathingBehavior.pathStart(), pathingBehavior.getGoal(), new CalculationContext(baritone, true), ipath -> { - logDirect("Found a path"); - logDirect("Ends at " + ipath.getDest()); - logDirect("Length " + ipath.length()); - logDirect("Estimated time " + ipath.ticksRemainingFrom(0)); - pathingBehavior.secretCursedFunctionDoNotCall(ipath); // it's okay when *I* do it - }, () -> { - logDirect("Path calculation failed, no path"); - }); - } - return true; - }*/ - if (msg.equals("proc")) { - Optional proc = baritone.getPathingControlManager().mostRecentInControl(); - if (!proc.isPresent()) { - logDirect("No process is in control"); - return true; - } - IBaritoneProcess p = proc.get(); - logDirect("Class: " + p.getClass()); - logDirect("Priority: " + p.priority()); - logDirect("Temporary: " + p.isTemporary()); - logDirect("Display name: " + p.displayName()); - logDirect("Command: " + baritone.getPathingControlManager().mostRecentCommand()); - return true; - } - if (msg.equals("version")) { - String version = ExampleBaritoneControl.class.getPackage().getImplementationVersion(); - if (version == null) { - logDirect("No version detected. Either dev environment or broken install."); - } else { - logDirect("You are using Baritone v" + version); - } - return true; - } - if (msg.equals("repack") || msg.equals("rescan")) { - logDirect("Queued " + repack() + " chunks for repacking"); - return true; - } - if (msg.startsWith("build")) { - String file; - BlockPos origin; - try { - String[] coords = msg.substring("build".length()).trim().split(" "); - file = coords[0] + ".schematic"; - origin = new BlockPos(parseOrDefault(coords[1], ctx.playerFeet().x, 1), parseOrDefault(coords[2], ctx.playerFeet().y, 1), parseOrDefault(coords[3], ctx.playerFeet().z, 1)); - } catch (Exception ex) { - file = msg.substring(5).trim() + ".schematic"; - origin = ctx.playerFeet(); - } - logDirect("Loading '" + file + "' to build from origin " + origin); - boolean success = baritone.getBuilderProcess().build(file, origin); - logDirect(success ? "Loaded" : "Unable to load"); - return true; - } - if (msg.startsWith("schematica")) { - baritone.getBuilderProcess().buildOpenSchematic(); - return true; - } - if (msg.equals("come")) { - customGoalProcess.setGoalAndPath(new GoalBlock(new BlockPos(Helper.mc.getRenderViewEntity()))); - logDirect("Coming"); - return true; - } - if (msg.equals("axis") || msg.equals("highway")) { - customGoalProcess.setGoalAndPath(new GoalAxis()); - return true; - } - if (msg.equals("cancel") || msg.equals("stop")) { - pathingBehavior.cancelEverything(); - logDirect("ok canceled"); - return true; - } - if (msg.equals("forcecancel")) { - pathingBehavior.cancelEverything(); - pathingBehavior.forceCancel(); - logDirect("ok force canceled"); - return true; - } - if (msg.equals("gc")) { - System.gc(); - logDirect("Called System.gc();"); - return true; - } - if (msg.equals("invert")) { - Goal goal = pathingBehavior.getGoal(); - BlockPos runAwayFrom; - if (goal instanceof GoalXZ) { - runAwayFrom = new BlockPos(((GoalXZ) goal).getX(), 0, ((GoalXZ) goal).getZ()); - } else if (goal instanceof GoalBlock) { - runAwayFrom = ((GoalBlock) goal).getGoalPos(); - } else { - logDirect("Goal must be GoalXZ or GoalBlock to invert"); - logDirect("Inverting goal of player feet"); - runAwayFrom = ctx.playerFeet(); - } - customGoalProcess.setGoalAndPath(new GoalRunAway(1, runAwayFrom) { - @Override - public boolean isInGoal(int x, int y, int z) { - return false; - } - }); - return true; - } - if (msg.startsWith("cleararea")) { - String suffix = msg.substring("cleararea".length()); - BlockPos corner1; - BlockPos corner2; - if (suffix.isEmpty()) { - // clear the area from the current goal to here - Goal goal = baritone.getPathingBehavior().getGoal(); - if (!(goal instanceof GoalBlock)) { - logDirect("Need to specify goal of opposite corner"); - return true; - } - corner1 = ((GoalBlock) goal).getGoalPos(); - corner2 = ctx.playerFeet(); - } else { - try { - String[] spl = suffix.split(" "); - corner1 = ctx.playerFeet(); - corner2 = new BlockPos(Integer.parseInt(spl[0]), Integer.parseInt(spl[1]), Integer.parseInt(spl[2])); - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) { - logDirect("unable to parse"); - return true; - } - } - baritone.getBuilderProcess().clearArea(corner1, corner2); - return true; - } - if (msg.equals("resume")) { - baritone.getBuilderProcess().resume(); - logDirect("resumed"); - return true; - } - if (msg.equals("pause")) { - baritone.getBuilderProcess().pause(); - logDirect("paused"); - return true; - } - if (msg.equals("reset")) { - for (Settings.Setting setting : BaritoneAPI.getSettings().allSettings) { - setting.reset(); - } - SettingsUtil.save(BaritoneAPI.getSettings()); - logDirect("Baritone settings reset"); - return true; - } - if (msg.equals("tunnel")) { - customGoalProcess.setGoalAndPath(new GoalStrictDirection(ctx.playerFeet(), ctx.player().getHorizontalFacing())); - logDirect("tunneling"); - return true; - } - if (msg.equals("render")) { - BetterBlockPos pf = ctx.playerFeet(); - int dist = (Minecraft.getMinecraft().gameSettings.renderDistanceChunks + 1) * 16; - Minecraft.getMinecraft().renderGlobal.markBlockRangeForRenderUpdate(pf.x - dist, pf.y - 256, pf.z - dist, pf.x + dist, pf.y + 256, pf.z + dist); - logDirect("okay"); - return true; - } - if (msg.equals("farm")) { - baritone.getFarmProcess().farm(); - logDirect("farming"); - return true; - } - if (msg.equals("chests")) { - for (Map.Entry entry : baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet()) { - logDirect(BetterBlockPos.from(entry.getKey()) + ""); - log(entry.getValue().getContents()); - } - return true; - } - if (msg.startsWith("followentities")) { - baritone.getFollowProcess().follow(Entity.class::isInstance); - logDirect("Following any entities"); - return true; - } - if (msg.startsWith("followplayers")) { - baritone.getFollowProcess().follow(EntityPlayer.class::isInstance); // O P P A - logDirect("Following any players"); - return true; - } - if (msg.startsWith("followentity")) { - String name = msg.substring(12).trim(); - Optional toFollow = Optional.empty(); - for (Entity entity : ctx.world().loadedEntityList) { - String entityName = entity.getName().trim().toLowerCase(); - if ((entityName.contains(name) || name.contains(entityName)) && !(entity instanceof EntityItem || entity instanceof EntityPlayer)) { // We dont want it following players while `#follow` exists. - toFollow = Optional.of(entity); - } - } - if (!toFollow.isPresent()) { - logDirect("Entity not found"); - return true; - } - Entity effectivelyFinal = toFollow.get(); - baritone.getFollowProcess().follow(effectivelyFinal::equals); - logDirect("Following entity " + toFollow.get()); - return true; - } - if (msg.startsWith("follow")) { - String name = msg.substring(6).trim(); - Optional toFollow = Optional.empty(); - if (name.length() == 0) { - toFollow = ctx.getSelectedEntity(); - } else { - for (EntityPlayer pl : ctx.world().playerEntities) { - String theirName = pl.getName().trim().toLowerCase(); - if (!theirName.equals(ctx.player().getName().trim().toLowerCase()) && (theirName.contains(name) || name.contains(theirName))) { // don't follow ourselves lol - toFollow = Optional.of(pl); - } - } - } - if (!toFollow.isPresent()) { - logDirect("Not found"); - return true; - } - Entity effectivelyFinal = toFollow.get(); - baritone.getFollowProcess().follow(effectivelyFinal::equals); - logDirect("Following " + toFollow.get()); - return true; - } - if (msg.startsWith("explorefilter")) { - // explorefilter blah.json - // means that entries in blah.json are already explored - // explorefilter blah.json invert - // means that entries in blah.json are NOT already explored - String path = msg.substring("explorefilter".length()).trim(); - String[] parts = path.split(" "); - Path path1 = Minecraft.getMinecraft().gameDir.toPath().resolve(parts[0]); - boolean invert = parts.length > 1; - try { - baritone.getExploreProcess().applyJsonFilter(path1, invert); - logDirect("Loaded filter. Inverted: " + invert); - if (invert) { - logDirect("Chunks on this list will be treated as possibly unexplored, all others will be treated as certainly explored"); - } else { - logDirect("Chunks on this list will be treated as certainly explored, all others will be treated as possibly unexplored"); - } - } catch (Exception e) { - e.printStackTrace(); - logDirect("Unable to load " + path1); - } - return true; - } - if (msg.equals("reloadall")) { - baritone.getWorldProvider().getCurrentWorld().getCachedWorld().reloadAllFromDisk(); - logDirect("ok"); - return true; - } - if (msg.equals("saveall")) { - baritone.getWorldProvider().getCurrentWorld().getCachedWorld().save(); - logDirect("ok"); - return true; - } - if (msg.startsWith("explore")) { - String rest = msg.substring("explore".length()).trim(); - int centerX; - int centerZ; - try { - centerX = Integer.parseInt(rest.split(" ")[0]); - centerZ = Integer.parseInt(rest.split(" ")[1]); - } catch (Exception ex) { - centerX = ctx.playerFeet().x; - centerZ = ctx.playerFeet().z; - } - baritone.getExploreProcess().explore(centerX, centerZ); - logDirect("Exploring from " + centerX + "," + centerZ); - return true; - } - if (msg.equals("blacklist")) { - IGetToBlockProcess proc = baritone.getGetToBlockProcess(); - if (!proc.isActive()) { - logDirect("GetToBlockProcess is not currently active"); - return true; - } - if (proc.blacklistClosest()) { - logDirect("Blacklisted closest instances"); - } else { - logDirect("No known locations, unable to blacklist"); - } - return true; - } - if (msg.startsWith("find")) { - repack(); - String blockType = msg.substring(4).trim(); - ArrayList locs = baritone.getWorldProvider().getCurrentWorld().getCachedWorld().getLocationsOf(blockType, 1, ctx.playerFeet().getX(), ctx.playerFeet().getZ(), 4); - logDirect("Have " + locs.size() + " locations"); - for (BlockPos pos : locs) { - Block actually = ctx.world().getBlockState(pos).getBlock(); - if (!BlockUtils.blockToString(actually).equalsIgnoreCase(blockType)) { - logDebug("Was looking for " + blockType + " but actually found " + actually + " " + BlockUtils.blockToString(actually)); - } - } - return true; - } - if (msg.startsWith("mine")) { - repack(); - String[] blockTypes = msg.substring(4).trim().split(" "); - try { - int quantity = Integer.parseInt(blockTypes[1]); - Block block = BlockUtils.stringToBlockRequired(blockTypes[0]); - baritone.getMineProcess().mine(quantity, block); - logDirect("Will mine " + quantity + " " + blockTypes[0]); - return true; - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) {} - for (String s : blockTypes) { - if (BlockUtils.stringToBlockNullable(s) == null) { - logDirect(s + " isn't a valid block name"); - return true; - } - - } - baritone.getMineProcess().mineByName(0, blockTypes); - logDirect("Started mining blocks of type " + Arrays.toString(blockTypes)); - return true; - } - if (msg.equals("click")) { - baritone.openClick(); - logDirect("aight dude"); - return true; - } - if (msg.startsWith("thisway") || msg.startsWith("forward")) { - try { - Goal goal = GoalXZ.fromDirection(ctx.playerFeetAsVec(), ctx.player().rotationYaw, Double.parseDouble(msg.substring(7).trim())); - customGoalProcess.setGoal(goal); - logDirect("Goal: " + goal); - } catch (NumberFormatException ex) { - logDirect("Error unable to parse '" + msg.substring(7).trim() + "' to a double."); - } - return true; - } - if (msg.startsWith("list") || msg.startsWith("get ") || msg.startsWith("show")) { - String waypointType = msg.substring(4).trim(); - if (waypointType.endsWith("s")) { - // for example, "show deaths" - waypointType = waypointType.substring(0, waypointType.length() - 1); - } - IWaypoint.Tag tag = IWaypoint.Tag.fromString(waypointType); - if (tag == null) { - logDirect("Not a valid tag. Tags are: " + Arrays.asList(IWaypoint.Tag.values()).toString().toLowerCase()); - return true; - } - Set waypoints = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getByTag(tag); - // might as well show them from oldest to newest - List sorted = new ArrayList<>(waypoints); - sorted.sort(Comparator.comparingLong(IWaypoint::getCreationTimestamp)); - logDirect("Waypoints under tag " + tag + ":"); - for (IWaypoint waypoint : sorted) { - logDirect(waypoint.toString()); - } - return true; - } - if (msg.startsWith("save")) { - String name = msg.substring(4).trim(); - BlockPos pos = ctx.playerFeet(); - if (name.contains(" ")) { - logDirect("Name contains a space, assuming it's in the format 'save waypointName X Y Z'"); - String[] parts = name.split(" "); - if (parts.length != 4) { - logDirect("Unable to parse, expected four things"); - return true; - } - try { - pos = new BlockPos(Integer.parseInt(parts[1]), Integer.parseInt(parts[2]), Integer.parseInt(parts[3])); - } catch (NumberFormatException ex) { - logDirect("Unable to parse coordinate integers"); - return true; - } - name = parts[0]; - } - for (IWaypoint.Tag tag : IWaypoint.Tag.values()) { - if (tag.name().equalsIgnoreCase(name)) { - logDirect("Unable to use tags as name. Tags are: " + Arrays.asList(IWaypoint.Tag.values()).toString().toLowerCase()); - return true; - } - } - baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint(name, IWaypoint.Tag.USER, pos)); - logDirect("Saved user defined position " + pos + " under name '" + name + "'. Say 'goto " + name + "' to set goal, say 'list user' to list custom waypoints."); - return true; - } - if (msg.startsWith("delete")) { - String name = msg.substring(6).trim(); - IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getAllWaypoints().stream().filter(w -> w.getTag() == IWaypoint.Tag.USER && w.getName().equalsIgnoreCase(name)).findFirst().orElse(null); - if (waypoint == null) { - logDirect("No user defined position under the name '" + name + "' found."); - return true; - } - baritone.getWorldProvider().getCurrentWorld().getWaypoints().removeWaypoint(waypoint); - logDirect("Deleted user defined position under name '" + name + "'."); - return true; - } - if (msg.startsWith("goto")) { - repack(); - String waypointType = msg.substring(4).trim(); - if (waypointType.endsWith("s") && IWaypoint.Tag.fromString(waypointType.substring(0, waypointType.length() - 1)) != null) { - // for example, "show deaths" - waypointType = waypointType.substring(0, waypointType.length() - 1); - } - IWaypoint.Tag tag = IWaypoint.Tag.fromString(waypointType); - IWaypoint waypoint; - if (tag == null) { - String mining = waypointType; - Block block = BlockUtils.stringToBlockNullable(mining); - //logDirect("Not a valid tag. Tags are: " + Arrays.asList(Waypoint.Tag.values()).toString().toLowerCase()); - if (block == null) { - waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getAllWaypoints().stream().filter(w -> w.getName().equalsIgnoreCase(mining)).max(Comparator.comparingLong(IWaypoint::getCreationTimestamp)).orElse(null); - if (waypoint == null) { - Goal goal = parseGoal(waypointType.split(" ")); - if (goal != null) { - logDirect("Going to " + goal); - customGoalProcess.setGoalAndPath(goal); - } - return true; - } - } else { - baritone.getGetToBlockProcess().getToBlock(block); - return true; - } - } else { - waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(tag); - if (waypoint == null) { - logDirect("None saved for tag " + tag); - return true; - } - } - Goal goal = waypoint.getTag() == IWaypoint.Tag.BED ? new GoalGetToBlock(waypoint.getLocation()) : new GoalBlock(waypoint.getLocation()); - customGoalProcess.setGoalAndPath(goal); - return true; - } - if (msg.equals("spawn") || msg.equals("bed")) { - IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(IWaypoint.Tag.BED); - if (waypoint == null) { - BlockPos spawnPoint = ctx.player().getBedLocation(); - // for some reason the default spawnpoint is underground sometimes - Goal goal = new GoalXZ(spawnPoint.getX(), spawnPoint.getZ()); - logDirect("spawn not saved, defaulting to world spawn. set goal to " + goal); - customGoalProcess.setGoalAndPath(goal); - } else { - Goal goal = new GoalGetToBlock(waypoint.getLocation()); - customGoalProcess.setGoalAndPath(goal); - logDirect("Set goal to most recent bed " + goal); - } - return true; - } - if (msg.equals("sethome")) { - baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("", IWaypoint.Tag.HOME, ctx.playerFeet())); - logDirect("Saved. Say home to set goal."); - return true; - } - if (msg.equals("home")) { - IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(IWaypoint.Tag.HOME); - if (waypoint == null) { - logDirect("home not saved"); - } else { - Goal goal = new GoalBlock(waypoint.getLocation()); - customGoalProcess.setGoalAndPath(goal); - logDirect("Going to saved home " + goal); - } - return true; - } - if (msg.equals("damn")) { - logDirect("daniel"); - } - return false; - } - - private int repack() { - ChunkProviderClient cli = (ChunkProviderClient) ctx.world().getChunkProvider(); - int playerChunkX = ctx.playerFeet().getX() >> 4; - int playerChunkZ = ctx.playerFeet().getZ() >> 4; - int count = 0; - for (int x = playerChunkX - 40; x <= playerChunkX + 40; x++) { - for (int z = playerChunkZ - 40; z <= playerChunkZ + 40; z++) { - Chunk chunk = cli.getLoadedChunk(x, z); - if (chunk != null && !chunk.isEmpty()) { - count++; - baritone.getWorldProvider().getCurrentWorld().getCachedWorld().queueForPacking(chunk); - } - } - } - return count; - } - - private int parseOrDefault(String str, int i, double dimensionFactor) { - return str.equals("~") ? i : str.startsWith("~") ? (int) (Integer.parseInt(str.substring(1)) * dimensionFactor) + i : (int) (Integer.parseInt(str) * dimensionFactor); - } - - private void log(List stacks) { - for (ItemStack stack : stacks) { - if (!stack.isEmpty()) { - logDirect(stack.getCount() + "x " + stack.getDisplayName() + "@" + stack.getItemDamage()); - } - } - } - - private Goal parseGoal(String[] params) { - Goal goal; - try { - BetterBlockPos playerFeet = ctx.playerFeet(); - - int length = params.length - 1; // length has to be smaller when a dimension parameter is added - if (params.length < 1 || (isCreatable(params[params.length - 1]) || params[params.length - 1].startsWith("~"))) { - length = params.length; - } - switch (length) { - case 0: - goal = new GoalBlock(playerFeet); - break; - case 1: - goal = new GoalYLevel(parseOrDefault(params[0], playerFeet.y, 1)); - break; - case 2: - goal = new GoalXZ(parseOrDefault(params[0], playerFeet.x, calculateDimensionFactor(params[params.length - 1])), parseOrDefault(params[1], playerFeet.z, calculateDimensionFactor(params[params.length - 1]))); - break; - case 3: - goal = new GoalBlock(new BlockPos(parseOrDefault(params[0], playerFeet.x, calculateDimensionFactor(params[params.length - 1])), parseOrDefault(params[1], playerFeet.y, 1), parseOrDefault(params[2], playerFeet.z, calculateDimensionFactor(params[params.length - 1])))); - break; - default: - logDirect("unable to understand lol"); - return null; - } - } catch (NumberFormatException ex) { - logDirect("unable to parse integer " + ex); - return null; - } - return goal; - } - - - private double calculateDimensionFactor(String to) { - return Math.pow(8, ctx.world().provider.getDimensionType().getId() - getDimensionByName(to.toLowerCase()).getId()); - } - - private DimensionType getDimensionByName(String name) { - if ("the_end".contains(name)) { - return DimensionType.THE_END; - } - if ("the_overworld".contains(name) || "surface".contains(name)) { - return DimensionType.OVERWORLD; - } - if ("the_nether".contains(name) || "hell".contains(name)) { - return DimensionType.NETHER; - } - return ctx.world().provider.getDimensionType(); - } - -} diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java index 99683d8a..91d7890f 100755 --- a/src/api/java/baritone/api/utils/Helper.java +++ b/src/api/java/baritone/api/utils/Helper.java @@ -23,6 +23,9 @@ import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; +import java.util.Arrays; +import java.util.stream.Stream; + /** * @author Brady * @since 8/1/2018 @@ -34,16 +37,26 @@ public interface 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 - )); - + /** + * Instance of the game + */ Minecraft mc = Minecraft.getMinecraft(); + static ITextComponent getPrefix() { + // Inner text component + ITextComponent baritone = new TextComponentString(BaritoneAPI.getSettings().shortBaritonePrefix.value ? "B" : "Baritone"); + baritone.getStyle().setColor(TextFormatting.LIGHT_PURPLE); + + // Outer brackets + ITextComponent prefix = new TextComponentString(""); + prefix.getStyle().setColor(TextFormatting.DARK_PURPLE); + prefix.appendText("["); + prefix.appendSibling(baritone); + prefix.appendText("]"); + + return prefix; + } + /** * Send a message to chat only if chatDebug is on * @@ -59,14 +72,40 @@ public interface Helper { } /** - * 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) + * Send components to chat with the [Baritone] prefix + * + * @param components The components to send + */ + default void logDirect(ITextComponent... components) { + ITextComponent component = new TextComponentString(""); + component.appendSibling(getPrefix()); + component.appendSibling(new TextComponentString(" ")); + Arrays.asList(components).forEach(component::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) { + Stream.of(message.split("\n")).forEach(line -> { + ITextComponent component = new TextComponentString(line.replace("\t", " ")); + component.getStyle().setColor(color); + logDirect(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 */ 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/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java index 9acb2ad9..b461c389 100644 --- a/src/api/java/baritone/api/utils/IPlayerContext.java +++ b/src/api/java/baritone/api/utils/IPlayerContext.java @@ -47,9 +47,21 @@ public interface IPlayerContext { default BetterBlockPos playerFeet() { // TODO find a better way to deal with soul sand!!!!! BetterBlockPos feet = new BetterBlockPos(player().posX, player().posY + 0.1251, player().posZ); - if (world().getBlockState(feet).getBlock() instanceof BlockSlab) { - return feet.up(); - } + + // sometimes when calling this from another thread or while world is null, it'll throw a NullPointerException + // that causes the game to immediately crash + // + // so of course crashing on 2b is horribly bad due to queue times and logout spot + // catch the NPE and ignore it if it does happen + // + // this does not impact performance at all since we're not null checking constantly + // if there is an exception, the only overhead is Java generating the exception object... so we can ignore it + try { + if (world().getBlockState(feet).getBlock() instanceof BlockSlab) { + return feet.up(); + } + } catch (NullPointerException ignored) {} + return feet; } diff --git a/src/api/java/baritone/api/utils/ISchematic.java b/src/api/java/baritone/api/utils/ISchematic.java index 821dc68c..2adcbdd5 100644 --- a/src/api/java/baritone/api/utils/ISchematic.java +++ b/src/api/java/baritone/api/utils/ISchematic.java @@ -20,9 +20,11 @@ package baritone.api.utils; import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; +import java.util.List; + /** - * Basic representation of a schematic. Provides the dimensions and - * the desired statefor a given position relative to the origin. + * Basic representation of a schematic. Provides the dimensions and the desired state for a given position relative to + * the origin. * * @author leijurv */ @@ -36,12 +38,13 @@ public interface ISchematic { * However, in the case of something like a map art, anything that's below the level of the map art doesn't matter, * so this function should return false in that case. (i.e. it doesn't really have to be air below the art blocks) * - * @param x The x position of the block, relative to the origin - * @param y The y position of the block, relative to the origin - * @param z The z position of the block, relative to the origin + * @param x The x position of the block, relative to the origin + * @param y The y position of the block, relative to the origin + * @param z The z position of the block, relative to the origin + * @param currentState The current state of that block in the world, or null * @return Whether or not the specified position is within the bounds of this schematic */ - default boolean inSchematic(int x, int y, int z) { + default boolean inSchematic(int x, int y, int z, IBlockState currentState) { return x >= 0 && x < widthX() && y >= 0 && y < heightY() && z >= 0 && z < lengthZ(); } @@ -61,12 +64,14 @@ public interface ISchematic { /** * Returns the desired block state at a given (X, Y, Z) position relative to the origin (0, 0, 0). * - * @param x The x position of the block, relative to the origin - * @param y The y position of the block, relative to the origin - * @param z The z position of the block, relative to the origin + * @param x The x position of the block, relative to the origin + * @param y The y position of the block, relative to the origin + * @param z The z position of the block, relative to the origin + * @param current The current state of that block in the world, or null + * @param approxPlaceable The list of blockstates estimated to be placeable * @return The desired block state at the specified position */ - IBlockState desiredState(int x, int y, int z); + IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable); /** * @return The width (X axis length) of this schematic @@ -82,4 +87,4 @@ public interface ISchematic { * @return The length (Z axis length) of this schematic */ int lengthZ(); -} \ No newline at end of file +} diff --git a/src/api/java/baritone/api/utils/MyChunkPos.java b/src/api/java/baritone/api/utils/MyChunkPos.java index 8b61012f..7cda5aa3 100644 --- a/src/api/java/baritone/api/utils/MyChunkPos.java +++ b/src/api/java/baritone/api/utils/MyChunkPos.java @@ -34,4 +34,4 @@ public class MyChunkPos { public String toString() { return x + ", " + z; } -} \ No newline at end of file +} diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index a562f768..f9cb7136 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -34,7 +34,6 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Consumer; @@ -42,6 +41,7 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import static net.minecraft.client.Minecraft.getMinecraft; @@ -121,6 +121,40 @@ public class SettingsUtil { return modified; } + /** + * Gets the type of a setting and returns it as a string, with package names stripped. + *

+ * For example, if the setting type is {@code java.util.List}, this function returns + * {@code List}. + * + * @param setting The setting + * @return The type + */ + 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 maybeCensor(int coord) { if (BaritoneAPI.getSettings().censorCoordinates.value) { return ""; @@ -133,11 +167,8 @@ public class SettingsUtil { 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 { @@ -183,6 +214,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, @@ -210,7 +242,7 @@ public class SettingsUtil { Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; Parser parser = Parser.getParser(type); - return Arrays.stream(raw.split(",")) + return Stream.of(raw.split(",")) .map(s -> parser.parse(context, s)) .collect(Collectors.toList()); } @@ -269,7 +301,7 @@ public class SettingsUtil { } public static Parser getParser(Type type) { - return Arrays.stream(values()) + return Stream.of(values()) .filter(parser -> parser.accepts(type)) .findFirst().orElse(null); } 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..e83d6c50 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/Command.java @@ -0,0 +1,112 @@ +/* + * 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.IBaritone; +import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.execution.ICommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class Command implements Helper { + + protected IBaritone baritone; + protected IPlayerContext ctx; + + /** + * The names of this command. This is what you put after the command prefix. + */ + public final List names; + + /** + * Creates a new Baritone control command. + * + * @param names The names of this command. This is what you put after the command prefix. + */ + protected Command(IBaritone baritone, List names) { + this.names = names.stream() + .map(s -> s.toLowerCase(Locale.US)) + .collect(Collectors.toList()); + this.baritone = baritone; + this.ctx = baritone.getPlayerContext(); + } + + protected Command(IBaritone baritone, String name) { + this(baritone, Collections.singletonList(name)); + } + + /** + * Executes this command with the specified arguments. + * + * @param execution The command execution to execute this command with + */ + public final void execute(ICommandExecution execution) throws CommandException { + this.executed(execution.getLabel(), execution.getArguments()); + } + + /** + * Tab completes this command with the specified arguments. Any exception that is thrown by + * {@link #tabCompleted(String, ArgConsumer)} will be caught by this method, and then {@link Stream#empty()} + * will be returned. + * + * @param execution The command execution to tab complete + * @return The list of completions. + */ + public final Stream tabComplete(ICommandExecution execution) { + try { + return this.tabCompleted(execution.getLabel(), execution.getArguments()); + } catch (Throwable t) { + return Stream.empty(); + } + } + + /** + * Called when this command is executed. + */ + protected abstract void executed(String label, ArgConsumer args) throws CommandException; + + /** + * 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) throws CommandException; + + /** + * @return A single-line string containing a short description of this command's purpose. + */ + public abstract String getShortDesc(); + + /** + * @return A list of lines that will be printed by the help command when the user wishes to view them. + */ + public abstract List getLongDesc(); + + /** + * @return {@code true} if this command should be hidden from the help menu + */ + public boolean hiddenFromHelp() { + return false; + } +} diff --git a/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java b/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java new file mode 100644 index 00000000..63b6a445 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java @@ -0,0 +1,42 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command; + +import baritone.api.Settings; + +import java.util.UUID; + +/** + * @author Brady + * @since 9/26/2019 + */ +public interface IBaritoneChatControl { + + /** + * In certain cases chat components need to execute commands for you. For example, the paginator automatically runs + * commands when you click the forward and back arrows to show you the previous/next page. + *

+ * If the prefix is changed in the meantime, then the command will go to chat. That's no good. So here's a permanent + * prefix that forces a command to run, regardless of the current prefix, chat/prefix control being enabled, etc. + *

+ * If used right (by both developers and users), it should be impossible to expose a command accidentally to the + * server. As a rule of thumb, if you have a clickable chat component, always use this prefix. If you're suggesting + * a command (a component that puts text into your text box, or something else), use {@link Settings#prefix}. + */ + String FORCE_COMMAND_PREFIX = String.format("<<%s>>", UUID.randomUUID().toString()); +} diff --git a/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java new file mode 100644 index 00000000..1b0730c6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java @@ -0,0 +1,105 @@ +/* + * 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.exception.CommandUnhandledException; +import baritone.api.utils.command.registry.Registry; + +public class ArgParserManager { + + public static final Registry REGISTRY = new Registry<>(); + + static { + DefaultArgParsers.ALL.forEach(REGISTRY::register); + } + + /** + * @param type The type trying to be parsed + * @return A parser that can parse arguments into this class, if found. + */ + public static IArgParser.Stateless getParserStateless(Class type) { + //noinspection unchecked + return REGISTRY.descendingStream() + .filter(IArgParser.Stateless.class::isInstance) + .map(IArgParser.Stateless.class::cast) + .filter(parser -> parser.getTarget().isAssignableFrom(type)) + .findFirst() + .orElse(null); + } + + /** + * @param type The type trying to be parsed + * @return A parser that can parse arguments into this class, if found. + */ + public static IArgParser.Stated getParserStated(Class type, Class stateKlass) { + //noinspection unchecked + return REGISTRY.descendingStream() + .filter(IArgParser.Stated.class::isInstance) + .map(IArgParser.Stated.class::cast) + .filter(parser -> parser.getTarget().isAssignableFrom(type)) + .filter(parser -> parser.getStateType().isAssignableFrom(stateKlass)) + .map(IArgParser.Stated.class::cast) + .findFirst() + .orElse(null); + } + + /** + * Attempt to parse the specified argument with a stateless {@link IArgParser} that outputs the specified class. + * + * @param type The type to try and parse the argument into. + * @param arg The argument to parse. + * @return An instance of the specified class. + * @throws CommandInvalidTypeException If the parsing failed + */ + public static T parseStateless(Class type, CommandArgument arg) throws CommandInvalidTypeException { + IArgParser.Stateless parser = getParserStateless(type); + if (parser == null) { + throw new CommandNoParserForTypeException(type); + } + try { + return parser.parseArg(arg); + } catch (Exception exc) { + throw new CommandInvalidTypeException(arg, type.getSimpleName()); + } + } + + /** + * Attempt to parse the specified argument with a stated {@link IArgParser} that outputs the specified class. + * + * @param type The type to try and parse the argument into. + * @param arg The argument to parse. + * @param state The state to pass to the {@link IArgParser.Stated}. + * @return An instance of the specified class. + * @throws CommandInvalidTypeException If the parsing failed + * @see IArgParser.Stated + */ + public static T parseStated(Class type, Class stateKlass, CommandArgument arg, S state) throws CommandInvalidTypeException { + IArgParser.Stated parser = getParserStated(type, stateKlass); + if (parser == null) { + throw new CommandNoParserForTypeException(type); + } + try { + return parser.parseArg(arg, state); + } catch (Exception exc) { + throw new CommandInvalidTypeException(arg, type.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..c33f61fb --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java @@ -0,0 +1,123 @@ +/* + * 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.Arrays; +import java.util.List; +import java.util.Locale; + +public class DefaultArgParsers { + + public enum IntArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Integer.class; + } + + @Override + public Integer parseArg(CommandArgument arg) throws RuntimeException { + return Integer.parseInt(arg.value); + } + } + + public enum LongArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Long.class; + } + + @Override + public Long parseArg(CommandArgument arg) throws RuntimeException { + return Long.parseLong(arg.value); + } + } + + public enum FloatArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Float.class; + } + + @Override + public Float parseArg(CommandArgument arg) throws RuntimeException { + String value = arg.value; + if (!value.matches("^([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$")) { + throw new IllegalArgumentException("failed float format check"); + } + return Float.parseFloat(value); + } + } + + public enum DoubleArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Double.class; + } + + @Override + public Double parseArg(CommandArgument arg) throws RuntimeException { + String value = arg.value; + if (!value.matches("^([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$")) { + throw new IllegalArgumentException("failed double format check"); + } + return Double.parseDouble(value); + } + } + + public static class BooleanArgumentParser implements IArgParser.Stateless { + + public static final BooleanArgumentParser INSTANCE = new BooleanArgumentParser(); + public static final List TRUTHY_VALUES = Arrays.asList("1", "true", "yes", "t", "y", "on", "enable"); + public static final List FALSY_VALUES = Arrays.asList("0", "false", "no", "f", "n", "off", "disable"); + + @Override + public Class getTarget() { + return 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 IllegalArgumentException("invalid boolean"); + } + } + } + + public static final List> ALL = Arrays.asList( + IntArgumentParser.INSTANCE, + LongArgumentParser.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..09e2aa2e --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/IArgParser.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.argparser; + +import baritone.api.utils.command.argument.CommandArgument; + +public interface IArgParser { + + /** + * @return the class of this parser. + */ + Class getTarget(); + + /** + * A stateless argument parser is just that. It takes a {@link CommandArgument} and outputs its type. + * + * @see ArgParserManager#REGISTRY + */ + 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 Exception; + } + + /** + * A stated argument parser is similar to a stateless one. It also takes a {@link CommandArgument}, but it also + * takes a second argument that can be any type, referred to as the state. + * + * @see ArgParserManager#REGISTRY + */ + interface Stated extends IArgParser { + + Class getStateType(); + + /** + * @param arg The argument to parse. + * @param state Can be anything. + * @return What it was parsed into. + * @throws RuntimeException if you want the parsing to fail. The exception will be caught and turned into an + * appropriate error. + */ + T parseArg(CommandArgument arg, S state) throws Exception; + } +} 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..a5b044c4 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argument/CommandArgument.java @@ -0,0 +1,170 @@ +/* + * 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.argparser.IArgParser; +import baritone.api.utils.command.exception.CommandInvalidArgumentException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.EnumFacing; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +/** + * A {@link CommandArgument} is an immutable object representing one command argument. It contains data on the index of + * that argument, its value, and the rest of the string that argument was found in + *

+ * You're recommended to use {@link ArgConsumer}s to handle these. + */ +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; + } + + /** + * Gets an enum value from the enum class with the same name as this argument's value + *

+ * For example if you getEnum as an {@link EnumFacing}, and this argument's value is "up", it will return {@link + * EnumFacing#UP} + * + * @param enumClass The enum class to search + * @return An enum constant of that class with the same name as this argument's value + * @throws CommandInvalidTypeException If the constant couldn't be found + * @see ArgConsumer#peekEnum(Class) + * @see ArgConsumer#peekEnum(Class, int) + * @see ArgConsumer#peekEnumOrNull(Class) + * @see ArgConsumer#peekEnumOrNull(Class, int) + * @see ArgConsumer#getEnum(Class) + * @see ArgConsumer#getEnumOrNull(Class) + */ + public > E getEnum(Class enumClass) throws CommandInvalidTypeException { + return Stream.of(enumClass.getEnumConstants()) + .filter(e -> e.name().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(() -> new CommandInvalidTypeException(this, enumClass.getSimpleName())); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse this argument into the specified class + * + * @param type The class to parse this argument into + * @return An instance of the specified type + * @throws CommandInvalidTypeException If the parsing failed + */ + public T getAs(Class type) throws CommandInvalidTypeException { + return ArgParserManager.parseStateless(type, this); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse this argument into the specified class + * + * @param type The class to parse this argument into + * @return If the parser succeeded + */ + public boolean is(Class type) { + try { + getAs(type); + return true; + } catch (Throwable t) { + return false; + } + } + + /** + * Tries to use a stated {@link IArgParser} to parse this argument into the specified class + * + * @param type The class to parse this argument into + * @return An instance of the specified type + * @throws CommandInvalidTypeException If the parsing failed + */ + @SuppressWarnings("UnusedReturnValue") + public T getAs(Class type, Class stateType, S state) throws CommandInvalidTypeException { + return ArgParserManager.parseStated(type, stateType, this, state); + } + + /** + * Tries to use a stated {@link IArgParser} to parse this argument into the specified class + * + * @param type The class to parse this argument into + * @return If the parser succeeded + */ + public boolean is(Class type, Class stateType, S state) { + try { + getAs(type, stateType, state); + return true; + } catch (Throwable t) { + return false; + } + } + + /** + * Turn a string into a list of {@link CommandArgument}s. This is needed because of {@link CommandArgument#rawRest} + * + * @param string The string to convert + * @param preserveEmptyLast If the string ends with whitespace, add an empty {@link CommandArgument} to the end This + * is useful for tab completion + * @return A list of {@link CommandArgument}s + */ + 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; + } + + /** + * @see #from(String, boolean) + */ + public static List from(String string) { + return from(string, false); + } + + /** + * Returns an "unknown" {@link CommandArgument}. This shouldn't be used unless you absolutely have no information - + * ESPECIALLY not with {@link CommandInvalidArgumentException}s + * + * @return The unknown {@link CommandArgument} + */ + 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..ae659adc --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/BlockById.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; +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 enum BlockById implements IDatatypeFor { + INSTANCE; + + @Override + public Block get(IDatatypeContext ctx) throws CommandException { + ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); + Block block; + if ((block = Block.REGISTRY.getObject(id)) == Blocks.AIR) { + throw new IllegalArgumentException("no block found by that id"); + } + return block; + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append( + Block.REGISTRY.getKeys() + .stream() + .map(Object::toString) + ) + .filterPrefixNamespaced(ctx.getConsumer().getString()) + .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..3597141f --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/EntityClassById.java @@ -0,0 +1,49 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; +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; + +public enum EntityClassById implements IDatatypeFor> { + INSTANCE; + + @Override + public Class get(IDatatypeContext ctx) throws CommandException { + ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); + Class entity; + if ((entity = EntityList.REGISTRY.getObject(id)) == null) { + throw new IllegalArgumentException("no entity found by that id"); + } + return entity; + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(EntityList.getEntityNameList().stream().map(Object::toString)) + .filterPrefixNamespaced(ctx.getConsumer().getString()) + .sortAlphabetically() + .stream(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/ForBlockOptionalMeta.java b/src/api/java/baritone/api/utils/command/datatypes/ForBlockOptionalMeta.java new file mode 100644 index 00000000..f36826c6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/ForBlockOptionalMeta.java @@ -0,0 +1,37 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.command.exception.CommandException; + +import java.util.stream.Stream; + +public enum ForBlockOptionalMeta implements IDatatypeFor { + INSTANCE; + + @Override + public BlockOptionalMeta get(IDatatypeContext ctx) throws CommandException { + return new BlockOptionalMeta(ctx.getConsumer().getString()); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/ForEnumFacing.java b/src/api/java/baritone/api/utils/command/datatypes/ForEnumFacing.java new file mode 100644 index 00000000..ed6dce31 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/ForEnumFacing.java @@ -0,0 +1,43 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.util.EnumFacing; + +import java.util.Locale; +import java.util.stream.Stream; + +public enum ForEnumFacing implements IDatatypeFor { + INSTANCE; + + @Override + public EnumFacing get(IDatatypeContext ctx) throws CommandException { + return EnumFacing.valueOf(ctx.getConsumer().getString().toUpperCase(Locale.US)); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(Stream.of(EnumFacing.values()) + .map(EnumFacing::getName).map(String::toLowerCase)) + .filterPrefix(ctx.getConsumer().getString()) + .stream(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/ForWaypoints.java b/src/api/java/baritone/api/utils/command/datatypes/ForWaypoints.java new file mode 100644 index 00000000..e60ff0c6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/ForWaypoints.java @@ -0,0 +1,81 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.IBaritone; +import baritone.api.cache.IWaypoint; +import baritone.api.cache.IWaypointCollection; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; + +import java.util.Comparator; +import java.util.stream.Stream; + +public enum ForWaypoints implements IDatatypeFor { + INSTANCE; + + @Override + public IWaypoint[] get(IDatatypeContext ctx) throws CommandException { + final String input = ctx.getConsumer().getString(); + final IWaypoint.Tag tag = IWaypoint.Tag.getByName(input); + + // If the input doesn't resolve to a valid tag, resolve by name + return tag == null + ? getWaypointsByName(ctx.getBaritone(), input) + : getWaypointsByTag(ctx.getBaritone(), tag); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(getWaypointNames(ctx.getBaritone())) + .sortAlphabetically() + .prepend(IWaypoint.Tag.getAllNames()) + .filterPrefix(ctx.getConsumer().getString()) + .stream(); + } + + public static IWaypointCollection waypoints(IBaritone baritone) { + return baritone.getWorldProvider().getCurrentWorld().getWaypoints(); + } + + public static IWaypoint[] getWaypoints(IBaritone baritone) { + return waypoints(baritone).getAllWaypoints().stream() + .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed()) + .toArray(IWaypoint[]::new); + } + + public static String[] getWaypointNames(IBaritone baritone) { + return Stream.of(getWaypoints(baritone)) + .map(IWaypoint::getName) + .filter(name -> !name.isEmpty()) + .toArray(String[]::new); + } + + public static IWaypoint[] getWaypointsByTag(IBaritone baritone, IWaypoint.Tag tag) { + return waypoints(baritone).getByTag(tag).stream() + .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed()) + .toArray(IWaypoint[]::new); + } + + public static IWaypoint[] getWaypointsByName(IBaritone baritone, String name) { + return Stream.of(getWaypoints(baritone)) + .filter(waypoint -> waypoint.getName().equalsIgnoreCase(name)) + .toArray(IWaypoint[]::new); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java new file mode 100644 index 00000000..8c155fb6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java @@ -0,0 +1,56 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.argparser.IArgParser; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +/** + * An {@link IDatatype} is similar to an {@link IArgParser} in the sense that it is capable of consuming an argument + * to transform it into a usable form as the code desires. + *

+ * A fundamental difference is that an {@link IDatatype} is capable of consuming multiple arguments. For example, + * {@link RelativeBlockPos} is an {@link IDatatypePost} which requires at least 3 {@link RelativeCoordinate} arguments + * to be specified. + *

+ * Another difference is that an {@link IDatatype} can be tab-completed, providing comprehensive auto completion + * that can substitute based on existing input or provide possibilities for the next piece of input. + * + * @see IDatatypeContext + * @see IDatatypeFor + * @see IDatatypePost + */ +public interface IDatatype { + + /** + * Attempts to complete missing or partial input provided through the {@link ArgConsumer} provided by + * {@link IDatatypeContext#getConsumer()} in order to aide the user in executing commands. + *

+ * One benefit over datatypes over {@link IArgParser}s is that instead of each command trying to guess what values + * the datatype will accept, or simply not tab completing at all, datatypes that support tab completion can provide + * accurate information using the same methods used to parse arguments in the first place. + * + * @param ctx The argument consumer to tab complete + * @return A stream representing the strings that can be tab completed. DO NOT INCLUDE SPACES IN ANY STRINGS. + * @see ArgConsumer#tabCompleteDatatype(IDatatype) + */ + Stream tabComplete(IDatatypeContext ctx) throws CommandException; +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatypeContext.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatypeContext.java new file mode 100644 index 00000000..1452bf4a --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypeContext.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.IBaritone; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +/** + * Provides an {@link IDatatype} with contextual information so + * that it can perform the desired operation on the target level. + * + * @see IDatatype + * + * @author Brady + * @since 9/26/2019 + */ +public interface IDatatypeContext { + + /** + * Provides the {@link IBaritone} instance that is associated with the action relating to datatype handling. + * + * @return The context {@link IBaritone} instance. + */ + IBaritone getBaritone(); + + /** + * Provides the {@link ArgConsumer} to fetch input information from. + * + * @return The context {@link ArgConsumer}. + */ + ArgConsumer getConsumer(); +} 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..4f0f4200 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypeFor.java @@ -0,0 +1,44 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; + +import java.util.function.Supplier; + +/** + * An {@link IDatatype} which acts as a {@link Supplier}, in essence. The only difference + * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that + * implementations of {@link IDatatype} are singletons. + */ +public interface IDatatypeFor extends IDatatype { + + /** + * Consumes the desired amount of arguments from the specified {@link IDatatypeContext}, and + * then returns the parsed value. This method will more than likely return a {@link IllegalArgumentException} + * if the expected input does not conform to a parseable value. As far as a {@link CommandException} being + * thrown is concerned, see the note below for specifics. + * + * @see IDatatypeContext + * + * @param ctx The context + * @return The parsed data-type + * @throws CommandException If there was an issue parsing using another type or arguments could not be polled. + */ + T get(IDatatypeContext ctx) throws CommandException; +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java new file mode 100644 index 00000000..2e4cc47a --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java @@ -0,0 +1,41 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; + +import java.util.function.Function; + +/** + * An {@link IDatatype} which acts as a {@link Function}, in essence. The only difference + * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that + * implementations of {@link IDatatype} are singletons. + */ +public interface IDatatypePost extends IDatatype { + + /** + * Takes the expected input and transforms it based on the value held by {@code original}. If {@code original} + * is null, it is expected that the implementation of this method has a case to handle it, such that a + * {@link NullPointerException} will never be thrown as a result. + * + * @param ctx The datatype context + * @param original The transformable value + * @return The transformed value + */ + T apply(IDatatypeContext ctx, O original) throws CommandException; +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatypePostFunction.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePostFunction.java new file mode 100644 index 00000000..a3de089e --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePostFunction.java @@ -0,0 +1,29 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; + +/** + * @author Brady + * @since 9/26/2019 + */ +public interface IDatatypePostFunction { + + T apply(O original) throws CommandException; +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/NearbyPlayer.java b/src/api/java/baritone/api/utils/command/datatypes/NearbyPlayer.java new file mode 100644 index 00000000..78450e1a --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/NearbyPlayer.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.datatypes; + +import baritone.api.IBaritone; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.entity.player.EntityPlayer; + +import java.util.List; +import java.util.stream.Stream; + +/** + * An {@link IDatatype} used to resolve nearby players, those within + * render distance of the target {@link IBaritone} instance. + */ +public enum NearbyPlayer implements IDatatypeFor { + INSTANCE; + + @Override + public EntityPlayer get(IDatatypeContext ctx) throws CommandException { + final String username = ctx.getConsumer().getString(); + return getPlayers(ctx).stream() + .filter(s -> s.getName().equalsIgnoreCase(username)) + .findFirst().orElse(null); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(getPlayers(ctx).stream().map(EntityPlayer::getName)) + .filterPrefix(ctx.getConsumer().getString()) + .sortAlphabetically() + .stream(); + } + + private static List getPlayers(IDatatypeContext ctx) { + return ctx.getBaritone().getPlayerContext().world().playerEntities; + } +} 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..8459db88 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeBlockPos.java @@ -0,0 +1,57 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +public enum RelativeBlockPos implements IDatatypePost { + INSTANCE; + + @Override + public BetterBlockPos apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { + if (origin == null) { + origin = BetterBlockPos.ORIGIN; + } + + final ArgConsumer consumer = ctx.getConsumer(); + return new BetterBlockPos( + consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x), + consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y), + consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z) + ); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + final ArgConsumer consumer = ctx.getConsumer(); + if (consumer.hasAny() && !consumer.has(4)) { + while (consumer.has(2)) { + if (consumer.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { + break; + } + consumer.get(); + } + return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); + } + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java new file mode 100644 index 00000000..84a29f63 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public enum RelativeCoordinate implements IDatatypePost { + INSTANCE; + + private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$"); + + @Override + public Double apply(IDatatypeContext ctx, Double origin) throws CommandException { + if (origin == null) { + origin = 0.0D; + } + + Matcher matcher = PATTERN.matcher(ctx.getConsumer().getString()); + if (!matcher.matches()) { + throw new IllegalArgumentException("pattern doesn't match"); + } + + boolean isRelative = !matcher.group(1).isEmpty(); + double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2)); + + if (isRelative) { + return origin + offset; + } + return offset; + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + final ArgConsumer consumer = ctx.getConsumer(); + if (!consumer.has(2) && consumer.getString().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..71a9ca58 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeFile.java @@ -0,0 +1,103 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystems; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Stream; + +import static baritone.api.utils.Helper.HELPER; + +public enum RelativeFile implements IDatatypePost { + INSTANCE; + + @Override + public File apply(IDatatypeContext ctx, File original) throws CommandException { + if (original == null) { + original = new File("./"); + } + + Path path; + try { + path = FileSystems.getDefault().getPath(ctx.getConsumer().getString()); + } catch (InvalidPathException e) { + throw new IllegalArgumentException("invalid path"); + } + return getCanonicalFileUnchecked(original.toPath().resolve(path).toFile()); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + return Stream.empty(); + } + + /** + * Seriously + * + * @param file File + * @return Canonical file of file + * @author LoganDark + */ + private static File getCanonicalFileUnchecked(File file) { + try { + return file.getCanonicalFile(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static Stream tabComplete(ArgConsumer consumer, File base0) throws CommandException { + // I will not make the caller deal with this, seriously + // Tab complete code is beautiful and I'm not going to bloat it with dumb ass checked exception bullshit -LoganDark + + // lol owned -Brady + + File base = getCanonicalFileUnchecked(base0); + String currentPathStringThing = consumer.getString(); + Path currentPath = FileSystems.getDefault().getPath(currentPathStringThing); + Path basePath = currentPath.isAbsolute() ? currentPath.getRoot() : base.toPath(); + boolean useParent = !currentPathStringThing.isEmpty() && !currentPathStringThing.endsWith(File.separator); + File currentFile = currentPath.isAbsolute() ? currentPath.toFile() : new File(base, currentPathStringThing); + return Stream.of(Objects.requireNonNull(getCanonicalFileUnchecked( + useParent + ? currentFile.getParentFile() + : currentFile + ).listFiles())) + .map(f -> (currentPath.isAbsolute() ? f : basePath.relativize(f.toPath()).toString()) + + (f.isDirectory() ? File.separator : "")) + .filter(s -> s.toLowerCase(Locale.US).startsWith(currentPathStringThing.toLowerCase(Locale.US))) + .filter(s -> !s.contains(" ")); + } + + public static File gameDir() { + File gameDir = HELPER.mc.gameDir.getAbsoluteFile(); + if (gameDir.getName().equals(".")) { + return gameDir.getParentFile(); + } + return gameDir; + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java new file mode 100644 index 00000000..d47e05ce --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java @@ -0,0 +1,79 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.math.MathHelper; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public enum RelativeGoal implements IDatatypePost { + INSTANCE; + + @Override + public Goal apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { + if (origin == null) { + origin = BetterBlockPos.ORIGIN; + } + final ArgConsumer consumer = ctx.getConsumer(); + + List> coords = new ArrayList<>(); + final ArgConsumer copy = consumer.copy(); // This is a hack and should be fixed in the future probably + for (int i = 0; i < 3; i++) { + if (copy.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) != null) { + coords.add(o -> consumer.getDatatypePost(RelativeCoordinate.INSTANCE, o)); + copy.get(); // Consume so we actually decrement the remaining arguments + } + } + + switch (coords.size()) { + case 0: + return new GoalBlock(origin); + case 1: + return new GoalYLevel( + MathHelper.floor(coords.get(0).apply((double) origin.y)) + ); + case 2: + return new GoalXZ( + MathHelper.floor(coords.get(0).apply((double) origin.x)), + MathHelper.floor(coords.get(1).apply((double) origin.z)) + ); + case 3: + return new GoalBlock( + MathHelper.floor(coords.get(0).apply((double) origin.x)), + MathHelper.floor(coords.get(1).apply((double) origin.y)), + MathHelper.floor(coords.get(2).apply((double) origin.z)) + ); + default: + throw new IllegalStateException("Unexpected coords size: " + coords.size()); + } + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + return ctx.getConsumer().tabCompleteDatatype(RelativeCoordinate.INSTANCE); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java new file mode 100644 index 00000000..cd1c0be1 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.math.MathHelper; + +import java.util.stream.Stream; + +public enum RelativeGoalBlock implements IDatatypePost { + INSTANCE; + + @Override + public GoalBlock apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { + if (origin == null) { + origin = BetterBlockPos.ORIGIN; + } + + final ArgConsumer consumer = ctx.getConsumer(); + return new GoalBlock( + MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)), + MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)), + MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z)) + ); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + final ArgConsumer consumer = ctx.getConsumer(); + if (consumer.hasAtMost(3)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); + } + 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..b8bd0cd6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalXZ.java @@ -0,0 +1,52 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.math.MathHelper; + +import java.util.stream.Stream; + +public enum RelativeGoalXZ implements IDatatypePost { + INSTANCE; + + @Override + public GoalXZ apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { + if (origin == null) { + origin = BetterBlockPos.ORIGIN; + } + + final ArgConsumer consumer = ctx.getConsumer(); + return new GoalXZ( + MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)), + MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)) + ); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + final ArgConsumer consumer = ctx.getConsumer(); + if (consumer.hasAtMost(2)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); + } + 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..a8a9b232 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalYLevel.java @@ -0,0 +1,50 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.GoalYLevel; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.math.MathHelper; + +import java.util.stream.Stream; + +public enum RelativeGoalYLevel implements IDatatypePost { + INSTANCE; + + @Override + public GoalYLevel apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException { + if (origin == null) { + origin = BetterBlockPos.ORIGIN; + } + + return new GoalYLevel( + MathHelper.floor(ctx.getConsumer().getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)) + ); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) { + final ArgConsumer consumer = ctx.getConsumer(); + if (consumer.hasAtMost(1)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE); + } + return Stream.empty(); + } +} 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..002d328e --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandErrorMessageException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +public abstract class CommandErrorMessageException extends CommandException { + + protected CommandErrorMessageException(String reason) { + super(reason); + } +} 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..9fdc3f73 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +public abstract class CommandException extends Exception implements ICommandException { + + protected CommandException(String reason) { + super(reason); + } +} 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..62706705 --- /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..4e00c1f7 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandInvalidStateException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..5e5b81cd --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandInvalidTypeException.java @@ -0,0 +1,39 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..f4f2d818 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNoParserForTypeException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +public class CommandNoParserForTypeException extends CommandUnhandledException { + + public CommandNoParserForTypeException(Class klass) { + super(String.format("Could not find a handler for type %s", klass.getSimpleName())); + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java b/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java new file mode 100644 index 00000000..655652d6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..e8904cbc --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNotFoundException.java @@ -0,0 +1,40 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..24fc799f --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandTooManyArgumentsException.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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..6d647a20 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandUnhandledException.java @@ -0,0 +1,46 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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 class CommandUnhandledException extends RuntimeException implements ICommandException { + + public CommandUnhandledException(String message) { + super(message); + } + + public CommandUnhandledException(Throwable cause) { + super(cause); + } + + @Override + public void handle(Command command, List args) { + HELPER.logDirect("An unhandled exception occurred." + + "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues", + TextFormatting.RED); + + this.printStackTrace(); + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/ICommandException.java b/src/api/java/baritone/api/utils/command/exception/ICommandException.java new file mode 100644 index 00000000..379e97b7 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/ICommandException.java @@ -0,0 +1,55 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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; + +/** + * The base for a Baritone Command Exception, checked or unchecked. Provides a + * {@link #handle(Command, List)} method that is used to provide useful output + * to the user for diagnosing issues that may have occurred during execution. + *

+ * Anything implementing this interface should be assignable to {@link Exception}. + * + * @author Brady + * @since 9/20/2019 + */ +public interface ICommandException { + + /** + * @see Exception#getMessage() + * @return The exception details + */ + String getMessage(); + + /** + * Called when this exception is thrown, to handle the exception. + * + * @param command The command that threw it. + * @param args The arguments the command was called with. + */ + default void handle(Command command, List args) { + HELPER.logDirect(this.getMessage(), TextFormatting.RED); + } +} diff --git a/src/api/java/baritone/api/utils/command/execution/ICommandExecution.java b/src/api/java/baritone/api/utils/command/execution/ICommandExecution.java new file mode 100644 index 00000000..4ca5b193 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/execution/ICommandExecution.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.execution; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.Tuple; + +import java.util.List; +import java.util.stream.Stream; + +/** + * @author Brady + * @since 9/28/2019 + */ +public interface ICommandExecution { + + /** + * @return The label that was used to target the {@link Command} + */ + String getLabel(); + + /** + * @return The arguments to be passed to the {@link Command} + */ + ArgConsumer getArguments(); + + /** + * Executes the target command for this {@link ICommandExecution}. This method should never + * {@code throw} any exception, anything that is thrown during the target command execution + * should be safely handled. + */ + void execute(); + + /** + * Forwards this {@link ICommandExecution} to the target {@link Command} to perform a tab-completion. + * If the tab-completion operation is a failure, then {@link Stream#empty()} will be returned. + * + * @return The tab-completed arguments, if possible. + */ + Stream tabComplete(); + + static Tuple> expand(String string, boolean preserveEmptyLast) { + String label = string.split("\\s", 2)[0]; + List args = CommandArgument.from(string.substring(label.length()), preserveEmptyLast); + return new Tuple<>(label, args); + } + + static Tuple> expand(String string) { + return expand(string, false); + } +} 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..e92855d1 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/arguments/ArgConsumer.java @@ -0,0 +1,818 @@ +/* + * 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.IBaritone; +import baritone.api.utils.Helper; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argparser.IArgParser; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.datatypes.IDatatype; +import baritone.api.utils.command.datatypes.IDatatypeContext; +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 baritone.api.utils.command.manager.ICommandManager; +import net.minecraft.util.EnumFacing; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +/** + * The {@link ArgConsumer} is how {@link Command}s read the arguments passed to them. This class has many benefits: + * + *

+ */ +public class ArgConsumer { + + /** + * The parent {@link ICommandManager} for this {@link ArgConsumer}. Used by {@link #context}. + */ + private final ICommandManager manager; + + /** + * The {@link IDatatypeContext} instance for this {@link ArgConsumer}, passed to + * datatypes when an operation is performed upon them. + * + * @see IDatatype + * @see IDatatypeContext + */ + private final IDatatypeContext context; + + /** + * The list of arguments in this ArgConsumer + */ + public final LinkedList args; + + /** + * The list of consumed arguments for this ArgConsumer. The most recently consumed argument is the last one + */ + public final Deque consumed; + + private ArgConsumer(ICommandManager manager, Deque args, Deque consumed) { + this.manager = manager; + this.context = this.new Context(); + this.args = new LinkedList<>(args); + this.consumed = new LinkedList<>(consumed); + } + + public ArgConsumer(ICommandManager manager, List args) { + this(manager, new LinkedList<>(args), new LinkedList<>()); + } + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are at least {@code num} arguments left in this {@link ArgConsumer} + * @see #hasAny() + * @see #hasAtMost(int) + * @see #hasExactly(int) + */ + public boolean has(int num) { + return args.size() >= num; + } + + /** + * @return {@code true} if there is at least 1 argument left in this {@link ArgConsumer} + * @see #has(int) + * @see #hasAtMostOne() + * @see #hasExactlyOne() + */ + public boolean hasAny() { + return has(1); + } + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are at most {@code num} arguments left in this {@link ArgConsumer} + * @see #has(int) + * @see #hasAtMost(int) + * @see #hasExactly(int) + */ + public boolean hasAtMost(int num) { + return args.size() <= num; + } + + /** + * @return {@code true} if there is at most 1 argument left in this {@link ArgConsumer} + * @see #hasAny() + * @see #hasAtMostOne() + * @see #hasExactlyOne() + */ + public boolean hasAtMostOne() { + return hasAtMost(1); + } + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are exactly {@code num} arguments left in this {@link ArgConsumer} + * @see #has(int) + * @see #hasAtMost(int) + */ + public boolean hasExactly(int num) { + return args.size() == num; + } + + /** + * @return {@code true} if there is exactly 1 argument left in this {@link ArgConsumer} + * @see #hasAny() + * @see #hasAtMostOne() + */ + public boolean hasExactlyOne() { + return hasExactly(1); + } + + /** + * @param index The index to peek + * @return The argument at index {@code index} in this {@link ArgConsumer}, with 0 being the next one. This does not + * mutate the {@link ArgConsumer} + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #peekString(int) + * @see #peekAs(Class, int) + * @see #get() + */ + public CommandArgument peek(int index) throws CommandNotEnoughArgumentsException { + requireMin(index + 1); + return args.get(index); + } + + /** + * @return The next argument in this {@link ArgConsumer}. This does not mutate the {@link ArgConsumer} + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peek(int) + * @see #peekString() + * @see #peekAs(Class) + * @see #get() + */ + public CommandArgument peek() throws CommandNotEnoughArgumentsException { + return peek(0); + } + + /** + * @param index The index to peek + * @param type The type to check for + * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next + * argument + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #getAs(Class) + */ + public boolean is(Class type, int index) throws CommandNotEnoughArgumentsException { + return peek(index).is(type); + } + + /** + * @param type The type to check for + * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next + * argument + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peek() + * @see #getAs(Class) + */ + public boolean is(Class type) throws CommandNotEnoughArgumentsException { + return is(type, 0); + } + + /** + * @param index The index to peek + * @return The value of the argument at index {@code index} in this {@link ArgConsumer}, with 0 being the next one + * This does not mutate the {@link ArgConsumer} + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #peekString() + */ + public String peekString(int index) throws CommandNotEnoughArgumentsException { + return peek(index).value; + } + + /** + * @return The value of the next argument in this {@link ArgConsumer}. This does not mutate the {@link ArgConsumer} + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peekString(int) + * @see #getString() + */ + public String peekString() throws CommandNotEnoughArgumentsException { + return peekString(0); + } + + /** + * @param index The index to peek + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value + * @throws java.util.NoSuchElementException If the constant couldn't be found + * @see #peekEnumOrNull(Class) + * @see #getEnum(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E peekEnum(Class enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peek(index).getEnum(enumClass); + } + + /** + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value + * @throws CommandInvalidTypeException If the constant couldn't be found + * @see #peekEnumOrNull(Class) + * @see #getEnum(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E peekEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peekEnum(enumClass, 0); + } + + /** + * @param index The index to peek + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value. If no constant could be found, null + * @see #peekEnum(Class) + * @see #getEnumOrNull(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E peekEnumOrNull(Class enumClass, int index) throws CommandNotEnoughArgumentsException { + try { + return peekEnum(enumClass, index); + } catch (CommandInvalidTypeException e) { + return null; + } + } + + /** + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value. If no constant could be found, null + * @see #peekEnum(Class) + * @see #getEnumOrNull(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E peekEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException { + return peekEnumOrNull(enumClass, 0); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified + * class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @param index The index to peek + * @return An instance of the specified type + * @throws CommandInvalidTypeException If the parsing failed + * @see IArgParser + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAsOrNull(Class, int) + */ + public T peekAs(Class type, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peek(index).getAs(type); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @return An instance of the specified type + * @throws CommandInvalidTypeException If the parsing failed + * @see IArgParser + * @see #peekAs(Class, int) + * @see #peekAsOrDefault(Class, Object) + * @see #peekAsOrNull(Class) + */ + public T peekAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peekAs(type, 0); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified + * class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @param def The value to return if the argument can't be parsed + * @param index The index to peek + * @return An instance of the specified type, or {@code def} if it couldn't be parsed + * @see IArgParser + * @see #peekAsOrDefault(Class, Object) + * @see #peekAs(Class, int) + * @see #peekAsOrNull(Class, int) + */ + public T peekAsOrDefault(Class type, T def, int index) throws CommandNotEnoughArgumentsException { + try { + return peekAs(type, index); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @param def The value to return if the argument can't be parsed + * @return An instance of the specified type, or {@code def} if it couldn't be parsed + * @see IArgParser + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAs(Class) + * @see #peekAsOrNull(Class) + */ + public T peekAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException { + return peekAsOrDefault(type, def, 0); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified + * class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @param index The index to peek + * @return An instance of the specified type, or {@code null} if it couldn't be parsed + * @see IArgParser + * @see #peekAsOrNull(Class) + * @see #peekAs(Class, int) + * @see #peekAsOrDefault(Class, Object, int) + */ + public T peekAsOrNull(Class type, int index) throws CommandNotEnoughArgumentsException { + return peekAsOrDefault(type, null, index); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @return An instance of the specified type, or {@code null} if it couldn't be parsed + * @see IArgParser + * @see #peekAsOrNull(Class, int) + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object) + */ + public T peekAsOrNull(Class type) throws CommandNotEnoughArgumentsException { + return peekAsOrNull(type, 0); + } + + public T peekDatatype(IDatatypeFor datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypeFor(datatype); + } + + public T peekDatatype(IDatatypePost datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return this.peekDatatype(datatype, null); + } + + public T peekDatatype(IDatatypePost datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypePost(datatype, original); + } + + public T peekDatatypeOrNull(IDatatypeFor datatype) { + return copy().getDatatypeForOrNull(datatype); + } + + public T peekDatatypeOrNull(IDatatypePost datatype) { + return copy().getDatatypePostOrNull(datatype, null); + } + + public > T peekDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypePost(datatype, original); + } + + public > T peekDatatypePostOrDefault(D datatype, O original, T def) { + return copy().getDatatypePostOrDefault(datatype, original, def); + } + + public > T peekDatatypePostOrNull(D datatype, O original) { + return peekDatatypePostOrDefault(datatype, original, null); + } + + /** + * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + *

+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. + * + * @param datatype The datatype to get + * @return The datatype instance + * @see IDatatype + * @see IDatatypeFor + */ + public > T peekDatatypeFor(Class datatype) { + return copy().peekDatatypeFor(datatype); + } + + /** + * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + *

+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. + * + * @param datatype The datatype to get + * @param def The default value + * @return The datatype instance, or {@code def} if it throws an exception + * @see IDatatype + * @see IDatatypeFor + */ + public > T peekDatatypeForOrDefault(Class datatype, T def) { + return copy().peekDatatypeForOrDefault(datatype, def); + } + + /** + * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + *

+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method. + * + * @param datatype The datatype to get + * @return The datatype instance, or {@code null} if it throws an exception + * @see IDatatype + * @see IDatatypeFor + */ + public > T peekDatatypeForOrNull(Class datatype) { + return peekDatatypeForOrDefault(datatype, null); + } + + /** + * Gets the next argument and returns it. This consumes the first argument so that subsequent calls will return + * later arguments + * + * @return The next argument + * @throws CommandNotEnoughArgumentsException If there's less than one argument left + */ + public CommandArgument get() throws CommandNotEnoughArgumentsException { + requireMin(1); + CommandArgument arg = args.removeFirst(); + consumed.add(arg); + return arg; + } + + /** + * Gets the value of the next argument and returns it. This consumes the first argument so that subsequent calls + * will return later arguments + * + * @return The value of the next argument + * @throws CommandNotEnoughArgumentsException If there's less than one argument left + */ + public String getString() throws CommandNotEnoughArgumentsException { + return get().value; + } + + /** + * Gets an enum value from the enum class with the same name as the next argument's value + *

+ * For example if you getEnum as an {@link EnumFacing}, and the next argument's value is "up", this will return + * {@link EnumFacing#UP} + * + * @param enumClass The enum class to search + * @return An enum constant of that class with the same name as the next argument's value + * @throws CommandInvalidTypeException If the constant couldn't be found + * @see #peekEnum(Class) + * @see #getEnumOrNull(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E getEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return get().getEnum(enumClass); + } + + /** + * Gets an enum value from the enum class with the same name as the next argument's value + *

+ * For example if you getEnum as an {@link EnumFacing}, and the next argument's value is "up", this will return + * {@link EnumFacing#UP} + * + * @param enumClass The enum class to search + * @param def The default value + * @return An enum constant of that class with the same name as the next argument's value, or {@code def} if it + * couldn't be found + * @see #getEnum(Class) + * @see #getEnumOrNull(Class) + * @see #peekEnumOrNull(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E getEnumOrDefault(Class enumClass, E def) throws CommandNotEnoughArgumentsException { + try { + peekEnum(enumClass); + return getEnum(enumClass); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + /** + * Gets an enum value from the enum class with the same name as the next argument's value + *

+ * For example if you getEnum as an {@link EnumFacing}, and the next argument's value is "up", this will return + * {@link EnumFacing#UP} + * + * @param enumClass The enum class to search + * @return An enum constant of that class with the same name as the next argument's value, or {@code null} if it + * couldn't be found + * @see #getEnum(Class) + * @see #getEnumOrDefault(Class, Enum) + * @see #peekEnumOrNull(Class) + * @see CommandArgument#getEnum(Class) + */ + public > E getEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException { + return getEnumOrDefault(enumClass, null); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @return An instance of the specified type + * @throws CommandInvalidTypeException If the parsing failed + * @see IArgParser + * @see #get() + * @see #getAsOrDefault(Class, Object) + * @see #getAsOrNull(Class) + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAsOrNull(Class, int) + */ + public T getAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return get().getAs(type); + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @param def The default value + * @return An instance of the specified type, or {@code def} if it couldn't be parsed + * @see IArgParser + * @see #get() + * @see #getAs(Class) + * @see #getAsOrNull(Class) + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAsOrNull(Class, int) + */ + public T getAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException { + try { + T val = peek().getAs(type); + get(); + return val; + } catch (CommandInvalidTypeException e) { + return def; + } + } + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link ArgConsumer}. + * + * @param type The type to peek as + * @return An instance of the specified type, or {@code null} if it couldn't be parsed + * @see IArgParser + * @see #get() + * @see #getAs(Class) + * @see #getAsOrDefault(Class, Object) + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAsOrNull(Class, int) + */ + public T getAsOrNull(Class type) throws CommandNotEnoughArgumentsException { + return getAsOrDefault(type, null); + } + + public > T getDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + try { + return datatype.apply(this.context, original); + } catch (Exception e) { + e.printStackTrace(); + throw new CommandInvalidTypeException(hasAny() ? peek() : consumed(), datatype.getClass().getSimpleName()); + } + } + + public > T getDatatypePostOrDefault(D datatype, O original, T _default) { + final List argsSnapshot = new ArrayList<>(this.args); + final List consumedSnapshot = new ArrayList<>(this.consumed); + try { + return this.getDatatypePost(datatype, original); + } catch (Exception e) { + this.args.clear(); + this.args.addAll(argsSnapshot); + this.consumed.clear(); + this.consumed.addAll(consumedSnapshot); + return _default; + } + } + + public > T getDatatypePostOrNull(D datatype, O original) { + return this.getDatatypePostOrDefault(datatype, original, null); + } + + public > T getDatatypeFor(D datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + try { + return datatype.get(this.context); + } catch (Exception e) { + throw new CommandInvalidTypeException(hasAny() ? peek() : consumed(), datatype.getClass().getSimpleName()); + } + } + + public > T getDatatypeForOrDefault(D datatype, T def) { + final List argsSnapshot = new ArrayList<>(this.args); + final List consumedSnapshot = new ArrayList<>(this.consumed); + try { + return this.getDatatypeFor(datatype); + } catch (Exception e) { + this.args.clear(); + this.args.addAll(argsSnapshot); + this.consumed.clear(); + this.consumed.addAll(consumedSnapshot); + return def; + } + } + + public > T getDatatypeForOrNull(D datatype) { + return this.getDatatypeForOrDefault(datatype, null); + } + + public Stream tabCompleteDatatype(T datatype) { + try { + return datatype.tabComplete(this.context); + } catch (Exception e) { + e.printStackTrace(); + } + return Stream.empty(); + } + + /** + * Returns the "raw rest" of the string. For example, from a string arg1 arg2  arg3, split + * into three {@link CommandArgument}s {@code "arg1"}, {@code "arg2"}, and {@code "arg3"}: + * + *

    + *
  • {@code rawRest()} would return arg1 arg2  arg3
  • + *
  • After calling {@link #get()}, {@code rawRest()} would return arg2  arg3 (note the + * double space - it is preserved!)
  • + *
  • After calling {@link #get()} again, {@code rawRest()} would return {@code "arg3"}
  • + *
  • After calling {@link #get()} one last time, {@code rawRest()} would return {@code ""}
  • + *
+ * + * @return The "raw rest" of the string. + */ + public String rawRest() { + return args.size() > 0 ? args.getFirst().rawRest : ""; + } + + /** + * @param min The minimum amount of arguments to require. + * @throws CommandNotEnoughArgumentsException If there are less than {@code min} arguments left. + * @see #requireMax(int) + * @see #requireExactly(int) + */ + public void requireMin(int min) throws CommandNotEnoughArgumentsException { + if (args.size() < min) { + throw new CommandNotEnoughArgumentsException(min + consumed.size()); + } + } + + /** + * @param max The maximum amount of arguments allowed. + * @throws CommandTooManyArgumentsException If there are more than {@code max} arguments left. + * @see #requireMin(int) + * @see #requireExactly(int) + */ + public void requireMax(int max) throws CommandTooManyArgumentsException { + if (args.size() > max) { + throw new CommandTooManyArgumentsException(max + consumed.size()); + } + } + + /** + * @param args The exact amount of arguments to require. + * @throws CommandNotEnoughArgumentsException If there are less than {@code args} arguments left. + * @throws CommandTooManyArgumentsException If there are more than {@code args} arguments left. + * @see #requireMin(int) + * @see #requireMax(int) + */ + public void requireExactly(int args) throws CommandException { + requireMin(args); + requireMax(args); + } + + /** + * @return If this {@link ArgConsumer} has consumed at least one argument. + * @see #consumed() + * @see #consumedString() + */ + public boolean hasConsumed() { + return !consumed.isEmpty(); + } + + /** + * @return The last argument this {@link ArgConsumer} has consumed, or the {@link CommandArgument#unknown() unknown} + * argument if no arguments have been consumed yet. + * @see #consumedString() + * @see #hasConsumed() + */ + public CommandArgument consumed() { + return consumed.size() > 0 ? consumed.getLast() : CommandArgument.unknown(); + } + + /** + * @return The value of thelast argument this {@link ArgConsumer} has consumed, or an empty string if no arguments + * have been consumed yet + * @see #consumed() + * @see #hasConsumed() + */ + public String consumedString() { + return consumed().value; + } + + /** + * @return A copy of this {@link ArgConsumer}. It has the same arguments (both consumed and not), but does not + * affect or mutate this instance. Useful for the various {@code peek} functions + */ + public ArgConsumer copy() { + return new ArgConsumer(manager, args, consumed); + } + + private final class Context implements IDatatypeContext { + + @Override + public final IBaritone getBaritone() { + return ArgConsumer.this.manager.getBaritone(); + } + + @Override + public final ArgConsumer getConsumer() { + return ArgConsumer.this; + } + } +} 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..da5ac436 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java @@ -0,0 +1,184 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.helpers.pagination; + +import baritone.api.utils.Helper; +import baritone.api.utils.command.exception.CommandException; +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.Arrays; +import java.util.List; +import java.util.function.Function; + +public class Paginator implements Helper { + + public final List entries; + public int pageSize = 8; + public int page = 1; + + public Paginator(List entries) { + this.entries = entries; + } + + public Paginator(E... entries) { + this.entries = Arrays.asList(entries); + } + + public Paginator setPageSize(int pageSize) { + this.pageSize = pageSize; + return this; + } + + public int getMaxPage() { + return (entries.size() - 1) / pageSize + 1; + } + + public boolean validPage(int page) { + return page > 0 && page <= getMaxPage(); + } + + public Paginator skipPages(int pages) { + page += pages; + return this; + } + + public void display(Function transform, String commandPrefix) { + int offset = (page - 1) * pageSize; + for (int i = offset; i < offset + pageSize; i++) { + if (i < entries.size()) { + logDirect(transform.apply(entries.get(i))); + } else { + logDirect("--", TextFormatting.DARK_GRAY); + } + } + boolean hasPrevPage = commandPrefix != null && validPage(page - 1); + boolean hasNextPage = commandPrefix != null && validPage(page + 1); + ITextComponent prevPageComponent = new TextComponentString("<<"); + if (hasPrevPage) { + prevPageComponent.getStyle() + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format("%s %d", commandPrefix, page - 1) + )) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to view previous page") + )); + } else { + prevPageComponent.getStyle().setColor(TextFormatting.DARK_GRAY); + } + ITextComponent nextPageComponent = new TextComponentString(">>"); + if (hasNextPage) { + nextPageComponent.getStyle() + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format("%s %d", commandPrefix, page + 1) + )) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to view next page") + )); + } else { + nextPageComponent.getStyle().setColor(TextFormatting.DARK_GRAY); + } + ITextComponent pagerComponent = new TextComponentString(""); + pagerComponent.getStyle().setColor(TextFormatting.GRAY); + pagerComponent.appendSibling(prevPageComponent); + pagerComponent.appendText(" | "); + pagerComponent.appendSibling(nextPageComponent); + pagerComponent.appendText(String.format(" %d/%d", page, getMaxPage())); + logDirect(pagerComponent); + } + + public void display(Function transform) { + display(transform, null); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Runnable pre, Function transform, String commandPrefix) throws CommandException { + int page = 1; + consumer.requireMax(1); + if (consumer.hasAny()) { + page = consumer.getAs(Integer.class); + if (!pagi.validPage(page)) { + throw new CommandInvalidTypeException( + consumer.consumed(), + String.format( + "a valid page (1-%d)", + pagi.getMaxPage() + ), + consumer.consumed().value + ); + } + } + pagi.skipPages(page - pagi.page); + if (pre != null) { + pre.run(); + } + pagi.display(transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, List elems, Runnable pre, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, new Paginator<>(elems), pre, transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, T[] elems, Runnable pre, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, Arrays.asList(elems), pre, transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, pagi, null, transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, List elems, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, new Paginator<>(elems), null, transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, T[] elems, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, Arrays.asList(elems), null, transform, commandPrefix); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Runnable pre, Function transform) throws CommandException { + paginate(consumer, pagi, pre, transform, null); + } + + public static void paginate(ArgConsumer consumer, List elems, Runnable pre, Function transform) throws CommandException { + paginate(consumer, new Paginator<>(elems), pre, transform, null); + } + + public static void paginate(ArgConsumer consumer, T[] elems, Runnable pre, Function transform) throws CommandException { + paginate(consumer, Arrays.asList(elems), pre, transform, null); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Function transform) throws CommandException { + paginate(consumer, pagi, null, transform, null); + } + + public static void paginate(ArgConsumer consumer, List elems, Function transform) throws CommandException { + paginate(consumer, new Paginator<>(elems), null, transform, null); + } + + public static void paginate(ArgConsumer consumer, T[] elems, Function transform) throws CommandException { + paginate(consumer, Arrays.asList(elems), 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..db63b2d5 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java @@ -0,0 +1,289 @@ +/* + * 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.event.events.TabCompleteEvent; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.command.datatypes.IDatatype; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.manager.ICommandManager; +import net.minecraft.util.ResourceLocation; + +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; + +/** + * The {@link TabCompleteHelper} is a single-use object that helps you handle tab completion. It includes helper + * methods for appending and prepending streams, sorting, filtering by prefix, and so on. + *

+ * The recommended way to use this class is: + *

    + *
  • Create a new instance with the empty constructor
  • + *
  • Use {@code append}, {@code prepend} or {@code add} methods to add completions
  • + *
  • Sort using {@link #sort(Comparator)} or {@link #sortAlphabetically()} and then filter by prefix using + * {@link #filterPrefix(String)}
  • + *
  • Get the stream using {@link #stream()}
  • + *
  • Pass it up to whatever's calling your tab complete function (i.e. + * {@link ICommandManager#tabComplete(String)} or {@link ArgConsumer#tabCompleteDatatype(IDatatype)})
  • + *
+ *

+ * For advanced users: if you're intercepting {@link TabCompleteEvent}s directly, use {@link #build()} instead for an + * array. + */ +public class TabCompleteHelper { + + private Stream stream; + + public TabCompleteHelper(String[] base) { + stream = Stream.of(base); + } + + public TabCompleteHelper(List base) { + stream = base.stream(); + } + + public TabCompleteHelper() { + stream = Stream.empty(); + } + + /** + * Appends the specified stream to this {@link TabCompleteHelper} and returns it for chaining + * + * @param source The stream to append + * @return This {@link TabCompleteHelper} after having appended the stream + * @see #append(String...) + * @see #append(Class) + */ + public TabCompleteHelper append(Stream source) { + stream = Stream.concat(stream, source); + return this; + } + + /** + * Appends the specified strings to this {@link TabCompleteHelper} and returns it for chaining + * + * @param source The stream to append + * @return This {@link TabCompleteHelper} after having appended the strings + * @see #append(Stream) + * @see #append(Class) + */ + public TabCompleteHelper append(String... source) { + return append(Stream.of(source)); + } + + /** + * Appends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining + * + * @param num The enum to append the values of + * @return This {@link TabCompleteHelper} after having appended the values + * @see #append(Stream) + * @see #append(String...) + */ + public TabCompleteHelper append(Class> num) { + return append( + Stream.of(num.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + ); + } + + /** + * Prepends the specified stream to this {@link TabCompleteHelper} and returns it for chaining + * + * @param source The stream to prepend + * @return This {@link TabCompleteHelper} after having prepended the stream + * @see #prepend(String...) + * @see #prepend(Class) + */ + public TabCompleteHelper prepend(Stream source) { + stream = Stream.concat(source, stream); + return this; + } + + /** + * Prepends the specified strings to this {@link TabCompleteHelper} and returns it for chaining + * + * @param source The stream to prepend + * @return This {@link TabCompleteHelper} after having prepended the strings + * @see #prepend(Stream) + * @see #prepend(Class) + */ + public TabCompleteHelper prepend(String... source) { + return prepend(Stream.of(source)); + } + + /** + * Prepends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining + * + * @param num The enum to prepend the values of + * @return This {@link TabCompleteHelper} after having prepended the values + * @see #prepend(Stream) + * @see #prepend(String...) + */ + public TabCompleteHelper prepend(Class> num) { + return prepend( + Stream.of(num.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + ); + } + + /** + * Apply the specified {@code transform} to every element currently in this {@link TabCompleteHelper} and + * return this object for chaining + * + * @param transform The transform to apply + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper map(Function transform) { + stream = stream.map(transform); + return this; + } + + /** + * Apply the specified {@code filter} to every element currently in this {@link TabCompleteHelper} and return + * this object for chaining + * + * @param filter The filter to apply + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper filter(Predicate filter) { + stream = stream.filter(filter); + return this; + } + + /** + * Apply the specified {@code sort} to every element currently in this {@link TabCompleteHelper} and return + * this object for chaining + * + * @param comparator The comparator to use + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper sort(Comparator comparator) { + stream = stream.sorted(comparator); + return this; + } + + /** + * Sort every element currently in this {@link TabCompleteHelper} alphabetically and return this object for + * chaining + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper sortAlphabetically() { + return sort(String.CASE_INSENSITIVE_ORDER); + } + + /** + * Filter out any element that doesn't start with {@code prefix} and return this object for chaining + * + * @param prefix The prefix to filter for + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper filterPrefix(String prefix) { + return filter(x -> x.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))); + } + + /** + * Filter out any element that doesn't start with {@code prefix} and return this object for chaining + *

+ * Assumes every element in this {@link TabCompleteHelper} is a {@link ResourceLocation} + * + * @param prefix The prefix to filter for + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper filterPrefixNamespaced(String prefix) { + return filterPrefix(new ResourceLocation(prefix).toString()); + } + + /** + * @return An array containing every element in this {@link TabCompleteHelper} + * @see #stream() + */ + public String[] build() { + return stream.toArray(String[]::new); + } + + /** + * @return A stream containing every element in this {@link TabCompleteHelper} + * @see #build() + */ + public Stream stream() { + return stream; + } + + /** + * Appends every command in the specified {@link ICommandManager} to this {@link TabCompleteHelper} + * + * @param manager A command manager + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addCommands(ICommandManager manager) { + return append(manager.getRegistry().descendingStream() + .flatMap(command -> command.names.stream()) + .distinct() + ); + } + + /** + * Appends every setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addSettings() { + return append( + BaritoneAPI.getSettings().allSettings.stream() + .map(Settings.Setting::getName) + .filter(s -> !s.equalsIgnoreCase("logger")) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + /** + * Appends every modified setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addModifiedSettings() { + return append( + SettingsUtil.modifiedSettings(BaritoneAPI.getSettings()).stream() + .map(Settings.Setting::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + /** + * Appends every {@link Boolean} setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + 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/ICommandManager.java b/src/api/java/baritone/api/utils/command/manager/ICommandManager.java new file mode 100644 index 00000000..63682433 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/manager/ICommandManager.java @@ -0,0 +1,52 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.manager; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.registry.Registry; +import net.minecraft.util.Tuple; + +import java.util.List; +import java.util.stream.Stream; + +/** + * @author Brady + * @since 9/21/2019 + */ +public interface ICommandManager { + + IBaritone getBaritone(); + + Registry getRegistry(); + + /** + * @param name The command name to search for. + * @return The command, if found. + */ + Command getCommand(String name); + + boolean execute(String string); + + boolean execute(Tuple> expanded); + + Stream tabComplete(Tuple> expanded); + + Stream tabComplete(String prefix); +} 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..bac50dcf --- /dev/null +++ b/src/api/java/baritone/api/utils/command/registry/Registry.java @@ -0,0 +1,135 @@ +/* + * 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.*; +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. + */ +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 HashSet containing every entry currently registered. Entries are added to this set when something is registered + * and removed from the set when they are unregistered. An entry being present in this set indicates that it is + * currently registered, can be removed, and should not be reregistered until it is removed. + */ + private final Set registered = new HashSet<>(); + /** + * 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.contains(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.add(entry); + 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/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java b/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java index 5bbbc007..13e7e686 100644 --- a/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java +++ b/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java @@ -20,5 +20,6 @@ package baritone.api.utils.interfaces; import net.minecraft.util.math.BlockPos; public interface IGoalRenderPos { + BlockPos getGoalPos(); } diff --git a/src/launch/java/baritone/launch/mixins/MixinBitArray.java b/src/launch/java/baritone/launch/mixins/MixinBitArray.java new file mode 100644 index 00000000..9383c9aa --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinBitArray.java @@ -0,0 +1,91 @@ +/* + * 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.launch.mixins; + +import baritone.utils.accessor.IBitArray; +import net.minecraft.util.BitArray; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(BitArray.class) +public abstract class MixinBitArray implements IBitArray { + + @Shadow + @Final + private long[] longArray; + + @Shadow + @Final + private int bitsPerEntry; + + @Shadow + @Final + private long maxEntryValue; + + @Shadow + @Final + private int arraySize; + + /** + * why did mojang divide by 64 instead of shifting right by 6 (2^6=64)? + * why did mojang modulo by 64 instead of ANDing with 63? + * also removed validation check + * + * @author LoganDark + */ + @Override + @Unique + public int getAtFast(int index) { + final int b = bitsPerEntry; + final long mev = maxEntryValue; + final int i = index * b; + final int j = i >> 6; + final int l = i & 63; + final int k = ((index + 1) * b - 1) >> 6; + + if (j == k) { + return (int) (this.longArray[j] >>> l & mev); + } else { + return (int) ((this.longArray[j] >>> l | longArray[k] << (64 - l)) & mev); + } + } + + @Override + @Unique + public int[] toArray() { + int[] out = new int[arraySize]; + + for (int idx = 0, kl = bitsPerEntry - 1; idx < arraySize; idx++, kl += bitsPerEntry) { + final int i = idx * bitsPerEntry; + final int j = i >> 6; + final int l = i & 63; + final int k = kl >> 6; + final long jl = longArray[j] >>> l; + + if (j == k) { + out[idx] = (int) (jl & maxEntryValue); + } else { + out[idx] = (int) ((jl | longArray[k] << (64 - l)) & maxEntryValue); + } + } + + return out; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.java b/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.java new file mode 100644 index 00000000..8c08b03f --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.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.launch.mixins; + +import baritone.utils.accessor.IBitArray; +import baritone.utils.accessor.IBlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BitArray; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.IBlockStatePalette; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(BlockStateContainer.class) +public abstract class MixinBlockStateContainer implements IBlockStateContainer { + + @Shadow + protected BitArray storage; + + @Shadow + protected IBlockStatePalette palette; + + @Override + @Accessor + public abstract BitArray getStorage(); + + @Override + @Accessor + public abstract IBlockStatePalette getPalette(); + + @Override + @Unique + public IBlockState getFast(int index) { + return palette.getBlockState(((IBitArray) storage).getAtFast(index)); + } + + @Override + public IBlockState getAtPalette(int index) { + return palette.getBlockState(index); + } + + @Override + public int[] storageArray() { + return ((IBitArray) storage).toArray(); + } +} 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..797b6210 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.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..9883c176 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiChat.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.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..9f91beaa --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java @@ -0,0 +1,33 @@ +/* + * 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.launch.mixins; + +import baritone.api.accessor.IGuiScreen; +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 IGuiScreen { + + @Override + @Invoker("openWebLink") + public abstract void openLink(URI url); +} diff --git a/src/launch/java/baritone/launch/mixins/MixinItemStack.java b/src/launch/java/baritone/launch/mixins/MixinItemStack.java new file mode 100644 index 00000000..2ad4c187 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinItemStack.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.launch.mixins; + +import baritone.api.accessor.IItemStack; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +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; + +@Mixin(ItemStack.class) +public abstract class MixinItemStack implements IItemStack { + + @Shadow + @Final + private Item item; + + @Shadow + private int itemDamage; + + @Unique + private int baritoneHash; + + private void recalculateHash() { + baritoneHash = item == null ? -1 : item.hashCode() + itemDamage; + } + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onInit(CallbackInfo ci) { + recalculateHash(); + } + + @Inject( + method = "setItemDamage", + at = @At("TAIL") + ) + private void onItemDamageSet(CallbackInfo ci) { + recalculateHash(); + } + + @Override + public int getBaritoneHash() { + return baritoneHash; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java index 6ee1a79b..9dc835cd 100644 --- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java +++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java @@ -17,7 +17,6 @@ package baritone.launch.mixins; -import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.BlockInteractEvent; @@ -59,7 +58,7 @@ public class MixinMinecraft { at = @At("RETURN") ) private void postInit(CallbackInfo ci) { - ((Baritone) BaritoneAPI.getProvider().getPrimaryBaritone()).init(); + BaritoneAPI.getProvider().getPrimaryBaritone(); } @Inject( diff --git a/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java b/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java index c0294355..88c4abec 100644 --- a/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java +++ b/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java @@ -26,6 +26,7 @@ import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(PlayerControllerMP.class) public abstract class MixinPlayerControllerMP implements IPlayerControllerMP { + @Accessor @Override public abstract void setIsHittingBlock(boolean isHittingBlock); diff --git a/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java b/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java new file mode 100644 index 00000000..fef47bbe --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import com.google.common.collect.ImmutableMap; +import net.minecraft.block.properties.IProperty; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net.minecraft.block.state.BlockStateContainer$StateImplementation") +public abstract class MixinStateImplementation { + + @Shadow + @Final + private ImmutableMap, Comparable> properties; + + /** + * Block states are fucking immutable + */ + @Unique + private int hashCode; + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onInit(CallbackInfo ci) { + hashCode = properties.hashCode(); + } + + /** + * Cache this instead of using the fucking map every time + * + * @author LoganDark + * @reason Regular IBlockState generates a new hash every fucking time. This is not needed when scanning millions + * per second + */ + @Override + @Overwrite + public int hashCode() { + return hashCode; + } +} 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..cdbae3b4 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java @@ -0,0 +1,130 @@ +/* + * 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.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.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; + +@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().getPrimaryBaritone(); + + 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().getPrimaryBaritone(); + + if (baritone == null) { + 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 669b64cb..82c1dd7f 100644 --- a/src/launch/resources/mixins.baritone.json +++ b/src/launch/resources/mixins.baritone.json @@ -9,6 +9,9 @@ }, "client": [ "MixinAnvilChunkLoader", + "MixinBitArray", + "MixinBlockStateContainer", + "MixinChatTabCompleter", "MixinChunkProviderClient", "MixinChunkProviderServer", "MixinChunkRenderContainer", @@ -16,12 +19,17 @@ "MixinEntityLivingBase", "MixinEntityPlayerSP", "MixinEntityRenderer", + "MixinGuiChat", + "MixinGuiScreen", + "MixinItemStack", "MixinMinecraft", "MixinNetHandlerPlayClient", "MixinNetworkManager", "MixinPlayerControllerMP", "MixinRenderChunk", "MixinRenderList", + "MixinStateImplementation", + "MixinTabCompleter", "MixinVboRenderList", "MixinWorldClient" ] diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index a4b2d436..cfd658b1 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -21,14 +21,15 @@ 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.Helper; import baritone.api.utils.IPlayerContext; import baritone.behavior.*; import baritone.cache.WorldProvider; import baritone.event.GameEventHandler; import baritone.process.*; +import baritone.selection.SelectionManager; import baritone.utils.*; +import baritone.utils.command.manager.CommandManager; import baritone.utils.player.PrimaryPlayerContext; import net.minecraft.client.Minecraft; @@ -60,11 +61,6 @@ public class Baritone implements IBaritone { } } - /** - * Whether or not {@link Baritone#init()} has been called yet - */ - private boolean initialized; - private GameEventHandler gameEventHandler; private PathingBehavior pathingBehavior; @@ -83,6 +79,8 @@ public class Baritone implements IBaritone { private FarmProcess farmProcess; private PathingControlManager pathingControlManager; + private SelectionManager selectionManager; + private CommandManager commandManager; private IPlayerContext playerContext; private WorldProvider worldProvider; @@ -91,13 +89,6 @@ public class Baritone implements IBaritone { Baritone() { this.gameEventHandler = new GameEventHandler(this); - } - - @Override - public synchronized void init() { - if (initialized) { - return; - } // Define this before behaviors try and get it, or else it will be null and the builds will fail! this.playerContext = PrimaryPlayerContext.INSTANCE; @@ -109,7 +100,6 @@ public class Baritone implements IBaritone { memoryBehavior = new MemoryBehavior(this); inventoryBehavior = new InventoryBehavior(this); inputOverrideHandler = new InputOverrideHandler(this); - new ExampleBaritoneControl(this); } this.pathingControlManager = new PathingControlManager(this); @@ -125,12 +115,12 @@ public class Baritone implements IBaritone { } this.worldProvider = new WorldProvider(); + this.selectionManager = new SelectionManager(this); + this.commandManager = new CommandManager(this); if (BaritoneAutoTest.ENABLE_AUTO_TEST) { this.gameEventHandler.registerEventListener(BaritoneAutoTest.INSTANCE); } - - this.initialized = true; } @Override @@ -203,6 +193,11 @@ public class Baritone implements IBaritone { return this.pathingBehavior; } + @Override + public SelectionManager getSelectionManager() { + return selectionManager; + } + @Override public WorldProvider getWorldProvider() { return this.worldProvider; @@ -213,6 +208,11 @@ public class Baritone implements IBaritone { return this.gameEventHandler; } + @Override + public CommandManager getCommandManager() { + return this.commandManager; + } + @Override public void openClick() { new Thread(() -> { @@ -234,4 +234,4 @@ public class Baritone implements IBaritone { public static Executor getExecutor() { return threadPool; } -} \ No newline at end of file +} diff --git a/src/main/java/baritone/BaritoneProvider.java b/src/main/java/baritone/BaritoneProvider.java index 44715f4c..416f1cae 100644 --- a/src/main/java/baritone/BaritoneProvider.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -20,6 +20,7 @@ package baritone; import baritone.api.IBaritone; import baritone.api.IBaritoneProvider; import baritone.api.cache.IWorldScanner; +import baritone.utils.command.BaritoneChatControl; import baritone.cache.WorldScanner; import java.util.Collections; @@ -31,8 +32,16 @@ import java.util.List; */ public final class BaritoneProvider implements IBaritoneProvider { - private final Baritone primary = new Baritone(); - private final List all = Collections.singletonList(primary); + private final Baritone primary; + private final List all; + + { + this.primary = new Baritone(); + this.all = Collections.singletonList(this.primary); + + // Setup chat control, just for the primary instance + new BaritoneChatControl(this.primary); + } @Override public IBaritone getPrimaryBaritone() { diff --git a/src/main/java/baritone/behavior/InventoryBehavior.java b/src/main/java/baritone/behavior/InventoryBehavior.java index eface5c0..63e6c91e 100644 --- a/src/main/java/baritone/behavior/InventoryBehavior.java +++ b/src/main/java/baritone/behavior/InventoryBehavior.java @@ -35,6 +35,7 @@ import java.util.Random; import java.util.function.Predicate; public final class InventoryBehavior extends Behavior { + public InventoryBehavior(Baritone baritone) { super(baritone); } @@ -132,7 +133,7 @@ public final class InventoryBehavior extends Behavior { } public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) { - IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z); + IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z)); if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && maybe.equals(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player())))) { return true; // gotem } diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index 5caee6a3..66ab10f3 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -160,7 +160,7 @@ public final class MemoryBehavior extends Behavior { @Override public void onBlockInteract(BlockInteractEvent event) { if (event.getType() == BlockInteractEvent.Type.USE && BlockStateInterface.getBlock(ctx, event.getPos()) instanceof BlockBed) { - baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, event.getPos())); + baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, BetterBlockPos.from(event.getPos()))); } } @@ -255,6 +255,7 @@ public final class MemoryBehavior extends Behavior { } public static class EnderChestMemory { + private static final Map memory = new HashMap<>(); private final Path enderChest; private List contents; diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 7219175e..86fd9505 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -55,6 +55,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 +109,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } private void tickPath() { + pausedThisTick = false; if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; if (unpausedLastTick) { @@ -115,6 +117,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); } unpausedLastTick = false; + pausedThisTick = true; return; } unpausedLastTick = true; @@ -279,6 +282,11 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return goal; } + @Override + public boolean isPathing() { + return hasPath() && !pausedThisTick; + } + @Override public PathExecutor getCurrent() { return current; diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 237c460b..e7405390 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -288,6 +288,7 @@ public final class CachedWorld implements ICachedWorld, Helper { } private class PackerThread implements Runnable { + public void run() { while (true) { // TODO: Add CachedWorld unloading to remove the redundancy of having this diff --git a/src/main/java/baritone/cache/ChunkPacker.java b/src/main/java/baritone/cache/ChunkPacker.java index 864f23d6..bfeeabd7 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -87,6 +87,7 @@ public final class ChunkPacker { //System.out.println("Chunk packing took " + (end - start) + "ms for " + chunk.x + "," + chunk.z); IBlockState[] blocks = new IBlockState[256]; + // @formatter:off for (int z = 0; z < 16; z++) { https://www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html for (int x = 0; x < 16; x++) { @@ -100,6 +101,7 @@ public final class ChunkPacker { blocks[z << 4 | x] = Blocks.AIR.getDefaultState(); } } + // @formatter:on return new CachedChunk(chunk.x, chunk.z, bitSet, blocks, specialBlocks, System.currentTimeMillis()); } diff --git a/src/main/java/baritone/cache/WaypointCollection.java b/src/main/java/baritone/cache/WaypointCollection.java index 18b13b89..222adfdf 100644 --- a/src/main/java/baritone/cache/WaypointCollection.java +++ b/src/main/java/baritone/cache/WaypointCollection.java @@ -20,7 +20,7 @@ package baritone.cache; import baritone.api.cache.IWaypoint; import baritone.api.cache.IWaypointCollection; import baritone.api.cache.Waypoint; -import net.minecraft.util.math.BlockPos; +import baritone.api.utils.BetterBlockPos; import java.io.*; import java.nio.file.Files; @@ -86,7 +86,7 @@ public class WaypointCollection implements IWaypointCollection { int x = in.readInt(); int y = in.readInt(); int z = in.readInt(); - this.waypoints.get(tag).add(new Waypoint(name, tag, new BlockPos(x, y, z), creationTimestamp)); + this.waypoints.get(tag).add(new Waypoint(name, tag, new BetterBlockPos(x, y, z), creationTimestamp)); } } catch (IOException ignored) {} } diff --git a/src/main/java/baritone/cache/WorldScanner.java b/src/main/java/baritone/cache/WorldScanner.java index d6cf69c3..d31c17ab 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -17,15 +17,18 @@ package baritone.cache; +import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldScanner; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; -import net.minecraft.block.Block; +import baritone.utils.accessor.IBlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.chunk.BlockStateContainer; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import java.util.*; @@ -38,14 +41,13 @@ 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, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) { ArrayList res = new ArrayList<>(); - if (blocks.isEmpty()) { + + if (filter.blocks().isEmpty()) { return res; } + ChunkProviderClient chunkProvider = (ChunkProviderClient) ctx.world().getChunkProvider(); int maxSearchRadiusSq = maxSearchRadius * maxSearchRadius; @@ -75,7 +77,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,8 +93,8 @@ public enum WorldScanner implements IWorldScanner { } @Override - public List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) { - if (blocks.isEmpty()) { + public List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold) { + if (filter.blocks().isEmpty()) { return Collections.emptyList(); } @@ -105,11 +107,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, BlockOptionalMetaLookup 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++) { @@ -119,32 +121,52 @@ public enum WorldScanner implements IWorldScanner { continue; } int yReal = y0 << 4; - BlockStateContainer bsc = extendedblockstorage.getData(); - // the mapping of BlockStateContainer.getIndex from xyz to index is y << 8 | z << 4 | x; - // for better cache locality, iterate in that order - for (int y = 0; y < 16; y++) { - 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())) { - int yy = yReal | y; - if (result.size() >= max) { - if (Math.abs(yy - playerY) < yLevelThreshold) { - foundWithinY = true; - } else { - if (foundWithinY) { - // have found within Y in this chunk, so don't need to consider outside Y - // TODO continue iteration to one more sorted Y coordinate block - return true; - } - } + IBlockStateContainer bsc = (IBlockStateContainer) extendedblockstorage.getData(); + // storageArray uses an optimized algorithm that's faster than getAt + // creating this array and then using getAtPalette is faster than even getFast(int index) + int[] storage = bsc.storageArray(); + final int imax = 1 << 12; + for (int i = 0; i < imax; i++) { + IBlockState state = bsc.getAtPalette(storage[i]); + if (filter.has(state)) { + int y = yReal | ((i >> 8) & 15); + if (result.size() >= max) { + if (Math.abs(y - playerY) < yLevelThreshold) { + foundWithinY = true; + } else { + if (foundWithinY) { + // have found within Y in this chunk, so don't need to consider outside Y + // TODO continue iteration to one more sorted Y coordinate block + return true; } - result.add(new BlockPos(chunkX | x, yy, chunkZ | z)); } } + result.add(new BlockPos(chunkX | (i & 15), y, chunkZ | ((i >> 4) & 15))); } } } return foundWithinY; } + + public int repack(IPlayerContext ctx) { + 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 (chunk != null && !chunk.isEmpty()) { + queued++; + cachedWorld.queueForPacking(chunk); + } + } + } + + return queued; + } } 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/pathing/calc/openset/LinkedListOpenSet.java b/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java index 6de8290d..2f7a0af5 100644 --- a/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java +++ b/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java @@ -27,6 +27,7 @@ import baritone.pathing.calc.PathNode; * @author leijurv */ class LinkedListOpenSet implements IOpenSet { + private Node first = null; @Override @@ -83,6 +84,7 @@ class LinkedListOpenSet implements IOpenSet { } public static class Node { //wrapper with next + private Node nextOpen; private PathNode val; } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 5bd19bc2..b8739d1c 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -177,7 +177,7 @@ public interface MovementHelper extends ActionCosts, Helper { return block.isPassable(null, null); } - static boolean isReplacable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { + static boolean isReplaceable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case // all other overrides just return true or false @@ -207,6 +207,11 @@ public interface MovementHelper extends ActionCosts, Helper { return state.getMaterial().isReplaceable(); } + @Deprecated + static boolean isReplacable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { + return isReplaceable(x, y, z, state, bsi); + } + static boolean isDoorPassable(IPlayerContext ctx, BlockPos doorPos, BlockPos playerPos) { if (playerPos.equals(doorPos)) { return false; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index cbc8cdd8..66827a35 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -73,7 +73,7 @@ public class MovementAscend extends Movement { if (additionalPlacementCost >= COST_INF) { return COST_INF; } - if (!MovementHelper.isReplacable(destX, y, destZ, toPlace, context.bsi)) { + if (!MovementHelper.isReplaceable(destX, y, destZ, toPlace, context.bsi)) { return COST_INF; } boolean foundPlaceOption = false; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index 8e660aff..41a183f8 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -152,7 +152,7 @@ public class MovementParkour extends Movement { return; } IBlockState toReplace = context.get(destX, y - 1, destZ); - if (!MovementHelper.isReplacable(destX, y - 1, destZ, toReplace, context.bsi)) { + if (!MovementHelper.isReplaceable(destX, y - 1, destZ, toReplace, context.bsi)) { return; } if (!checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 4eab7144..c1f5ed01 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -111,7 +111,7 @@ public class MovementTraverse extends Movement { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { return COST_INF; } - if (MovementHelper.isReplacable(destX, y - 1, destZ, destOn, context.bsi)) { + if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) { boolean throughWater = MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock()); if (MovementHelper.isWater(destOn.getBlock()) && throughWater) { // this happens when assume walk on water is true and this is a traverse in water, which isn't allowed diff --git a/src/main/java/baritone/pathing/path/SplicedPath.java b/src/main/java/baritone/pathing/path/SplicedPath.java index 686a15f0..1ba497d8 100644 --- a/src/main/java/baritone/pathing/path/SplicedPath.java +++ b/src/main/java/baritone/pathing/path/SplicedPath.java @@ -26,6 +26,7 @@ import baritone.utils.pathing.PathBase; import java.util.*; public class SplicedPath extends PathBase { + private final List path; private final List movements; diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 310a7a8a..ab1400f8 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -33,7 +33,7 @@ import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import baritone.utils.PathingCommandContext; -import baritone.utils.schematic.AirSchematic; +import baritone.utils.schematic.FillSchematic; import baritone.utils.schematic.MapArtSchematic; import baritone.utils.schematic.Schematic; import baritone.utils.schematic.schematica.SchematicaHelper; @@ -68,6 +68,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil private int ticks; private boolean paused; private int layer; + private List approxPlaceable; public BuilderProcess(Baritone baritone) { super(baritone); @@ -104,6 +105,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; @@ -113,6 +119,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil e.printStackTrace(); return false; } + //noinspection ConstantConditions if (tag == null) { return false; } @@ -139,7 +146,12 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil int widthX = Math.abs(corner1.getX() - corner2.getX()) + 1; int heightY = Math.abs(corner1.getY() - corner2.getY()) + 1; int lengthZ = Math.abs(corner1.getZ() - corner2.getZ()) + 1; - build("clear area", new AirSchematic(widthX, heightY, lengthZ), origin); + build("clear area", new FillSchematic(widthX, heightY, lengthZ, Blocks.AIR.getDefaultState()), origin); + } + + @Override + public List getApproxPlaceable() { + return new ArrayList<>(approxPlaceable); } private static ISchematic parse(NBTTagCompound schematic) { @@ -151,14 +163,14 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return schematic != null; } - public IBlockState placeAt(int x, int y, int z) { + public IBlockState placeAt(int x, int y, int z, IBlockState current) { if (!isActive()) { return null; } - if (!schematic.inSchematic(x - origin.getX(), y - origin.getY(), z - origin.getZ())) { + if (!schematic.inSchematic(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current)) { return null; } - IBlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ()); + IBlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current, this.approxPlaceable); if (state.getBlock() == Blocks.AIR) { return null; } @@ -177,7 +189,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (dy == -1 && x == pathStart.x && z == pathStart.z) { continue; // dont mine what we're supported by, but not directly standing on } - IBlockState desired = bcc.getSchematic(x, y, z); + IBlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } @@ -195,7 +207,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return Optional.empty(); } - public class Placement { + public static class Placement { + private final int hotbarSelection; private final BlockPos placeAgainst; private final EnumFacing side; @@ -209,7 +222,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } } - private Optional searchForPlacables(BuilderCalculationContext bcc, List desirableOnHotbar) { + private Optional searchForPlaceables(BuilderCalculationContext bcc, List desirableOnHotbar) { BetterBlockPos center = ctx.playerFeet(); for (int dx = -5; dx <= 5; dx++) { for (int dy = -5; dy <= 1; dy++) { @@ -217,12 +230,12 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; - IBlockState desired = bcc.getSchematic(x, y, z); + IBlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } IBlockState curr = bcc.bsi.get0(x, y, z); - if (MovementHelper.isReplacable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { + if (MovementHelper.isReplaceable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() == Blocks.AIR) { continue; } @@ -242,7 +255,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil for (EnumFacing against : EnumFacing.values()) { BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).offset(against); IBlockState placeAgainstState = bsi.get0(placeAgainstPos); - if (MovementHelper.isReplacable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { + if (MovementHelper.isReplaceable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { continue; } if (!ctx.world().mayPlace(toPlace.getBlock(), new BetterBlockPos(x, y, z), false, against, null)) { @@ -316,6 +329,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + approxPlaceable = approxPlaceable(36); if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { ticks = 5; } else { @@ -343,13 +357,13 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } schematic = new ISchematic() { @Override - public IBlockState desiredState(int x, int y, int z) { - return realSchematic.desiredState(x, y, z); + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return realSchematic.desiredState(x, y, z, current, BuilderProcess.this.approxPlaceable); } @Override - public boolean inSchematic(int x, int y, int z) { - return ISchematic.super.inSchematic(x, y, z) && y >= minYInclusive && y <= maxYInclusive; + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + return ISchematic.super.inSchematic(x, y, z, currentState) && y >= minYInclusive && y <= maxYInclusive; } @Override @@ -411,7 +425,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } List desirableOnHotbar = new ArrayList<>(); - Optional toPlace = searchForPlacables(bcc, desirableOnHotbar); + Optional toPlace = searchForPlaceables(bcc, desirableOnHotbar); if (toPlace.isPresent() && isSafeToCancel && ctx.player().onGround && ticks <= 0) { Rotation rot = toPlace.get().rot; baritone.getLookBehavior().updateTarget(rot, true); @@ -423,14 +437,13 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } - List approxPlacable = placable(36); if (Baritone.settings().allowInventory.value) { ArrayList usefulSlots = new ArrayList<>(); List noValidHotbarOption = new ArrayList<>(); outer: for (IBlockState desired : desirableOnHotbar) { for (int i = 0; i < 9; i++) { - if (valid(approxPlacable.get(i), desired)) { + if (valid(approxPlaceable.get(i), desired)) { usefulSlots.add(i); continue outer; } @@ -441,7 +454,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil outer: for (int i = 9; i < 36; i++) { for (IBlockState desired : noValidHotbarOption) { - if (valid(approxPlacable.get(i), desired)) { + if (valid(approxPlaceable.get(i), desired)) { baritone.getInventoryBehavior().attemptToPutOnHotbar(i, usefulSlots::contains); break outer; } @@ -449,9 +462,9 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } } - Goal goal = assemble(bcc, approxPlacable.subList(0, 9)); + Goal goal = assemble(bcc, approxPlaceable.subList(0, 9)); if (goal == null) { - goal = assemble(bcc, approxPlacable); // we're far away, so assume that we have our whole inventory to recalculate placable properly + goal = assemble(bcc, approxPlaceable); // we're far away, so assume that we have our whole inventory to recalculate placeable properly if (goal == null) { logDirect("Unable to do it. Pausing. resume to resume, cancel to cancel"); paused = true; @@ -493,7 +506,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; - IBlockState desired = bcc.getSchematic(x, y, z); + IBlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired != null) { // we care about this position BetterBlockPos pos = new BetterBlockPos(x, y, z); @@ -515,15 +528,16 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil for (int y = 0; y < schematic.heightY(); y++) { for (int z = 0; z < schematic.lengthZ(); z++) { for (int x = 0; x < schematic.widthX(); x++) { - if (!schematic.inSchematic(x, y, z)) { - continue; - } int blockX = x + origin.getX(); int blockY = y + origin.getY(); int blockZ = z + origin.getZ(); + IBlockState current = bcc.bsi.get0(blockX, blockY, blockZ); + if (!schematic.inSchematic(x, y, z, current)) { + continue; + } if (bcc.bsi.worldContainsLoadedChunk(blockX, blockZ)) { // check if its in render distance, not if its in cache // we can directly observe this block, it is in render distance - if (valid(bcc.bsi.get0(blockX, blockY, blockZ), schematic.desiredState(x, y, z))) { + if (valid(bcc.bsi.get0(blockX, blockY, blockZ), schematic.desiredState(x, y, z, current, this.approxPlaceable))) { observedCompleted.add(BetterBlockPos.longHash(blockX, blockY, blockZ)); } else { incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); @@ -548,15 +562,15 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } } - private Goal assemble(BuilderCalculationContext bcc, List approxPlacable) { - List placable = new ArrayList<>(); + private Goal assemble(BuilderCalculationContext bcc, List approxPlaceable) { + List placeable = new ArrayList<>(); List breakable = new ArrayList<>(); List sourceLiquids = new ArrayList<>(); incorrectPositions.forEach(pos -> { IBlockState state = bcc.bsi.get0(pos); if (state.getBlock() instanceof BlockAir) { - if (approxPlacable.contains(bcc.getSchematic(pos.x, pos.y, pos.z))) { - placable.add(pos); + if (approxPlaceable.contains(bcc.getSchematic(pos.x, pos.y, pos.z, state))) { + placeable.add(pos); } } else { if (state.getBlock() instanceof BlockLiquid) { @@ -574,8 +588,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil List toBreak = new ArrayList<>(); breakable.forEach(pos -> toBreak.add(breakGoal(pos, bcc))); List toPlace = new ArrayList<>(); - placable.forEach(pos -> { - if (!placable.contains(pos.down()) && !placable.contains(pos.down(2))) { + placeable.forEach(pos -> { + if (!placeable.contains(pos.down()) && !placeable.contains(pos.down(2))) { toPlace.add(placementGoal(pos, bcc)); } }); @@ -591,6 +605,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class JankyGoalComposite implements Goal { + private final Goal primary; private final Goal fallback; @@ -638,8 +653,10 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return new GoalPlace(pos); } boolean allowSameLevel = ctx.world().getBlockState(pos.up()).getBlock() != Blocks.AIR; + IBlockState current = ctx.world().getBlockState(pos); for (EnumFacing facing : Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP) { - if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && ctx.world().mayPlace(bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ()).getBlock(), pos, false, facing, null)) { + //noinspection ConstantConditions + if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && ctx.world().mayPlace(bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current).getBlock(), pos, false, facing, null)) { return new GoalAdjacent(pos, pos.offset(facing), allowSameLevel); } } @@ -662,6 +679,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class GoalAdjacent extends GoalGetToBlock { + private boolean allowSameLevel; private BlockPos no; @@ -694,6 +712,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class GoalPlace extends GoalBlock { + public GoalPlace(BlockPos placeAt) { super(placeAt.up()); } @@ -720,7 +739,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return paused ? "Builder Paused" : "Building " + name; } - private List placable(int size) { + private List approxPlaceable(int size) { List result = new ArrayList<>(); for (int i = 0; i < size; i++) { ItemStack stack = ctx.player().inventory.mainInventory.get(i); @@ -744,7 +763,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public class BuilderCalculationContext extends CalculationContext { - private final List placable; + + private final List placeable; private final ISchematic schematic; private final int originX; private final int originY; @@ -752,7 +772,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil public BuilderCalculationContext() { super(BuilderProcess.this.baritone, true); // wew lad - this.placable = placable(9); + this.placeable = approxPlaceable(9); this.schematic = BuilderProcess.this.schematic; this.originX = origin.getX(); this.originY = origin.getY(); @@ -762,9 +782,9 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil this.backtrackCostFavoringCoefficient = 1; } - private IBlockState getSchematic(int x, int y, int z) { - if (schematic.inSchematic(x - originX, y - originY, z - originZ)) { - return schematic.desiredState(x - originX, y - originY, z - originZ); + private IBlockState getSchematic(int x, int y, int z, IBlockState current) { + if (schematic.inSchematic(x - originX, y - originY, z - originZ, current)) { + return schematic.desiredState(x - originX, y - originY, z - originZ, current, BuilderProcess.this.approxPlaceable); } else { return null; } @@ -775,7 +795,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (isPossiblyProtected(x, y, z) || !worldBorder.canPlaceAt(x, z)) { // make calculation fail properly if we can't build return COST_INF; } - IBlockState sch = getSchematic(x, y, z); + IBlockState sch = getSchematic(x, y, z, bsi.get0(x, y, z)); if (sch != null) { // TODO this can return true even when allowPlace is off.... is that an issue? if (sch.getBlock() == Blocks.AIR) { @@ -783,7 +803,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil // this won't be a schematic block, this will be a throwaway return placeBlockCost * 2; // we're going to have to break it eventually } - if (placable.contains(sch)) { + if (placeable.contains(sch)) { return 0; // thats right we gonna make it FREE to place a block where it should go in a structure // no place block penalty at all 😎 // i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too @@ -809,7 +829,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (!allowBreak || isPossiblyProtected(x, y, z)) { return COST_INF; } - IBlockState sch = getSchematic(x, y, z); + IBlockState sch = getSchematic(x, y, z, bsi.get0(x, y, z)); if (sch != null) { if (sch.getBlock() == Blocks.AIR) { // it should be air diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java index 3fa3d413..220ae3b1 100644 --- a/src/main/java/baritone/process/ExploreProcess.java +++ b/src/main/java/baritone/process/ExploreProcess.java @@ -173,6 +173,7 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl } private interface IChunkFilter { + Status isAlreadyExplored(int chunkX, int chunkZ); int countRemain(); @@ -205,6 +206,7 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl } private class JsonChunkFilter implements IChunkFilter { + private final boolean invert; // if true, the list is interpreted as a list of chunks that are NOT explored, if false, the list is interpreted as a list of chunks that ARE explored private final LongOpenHashSet inFilter; private final MyChunkPos[] positions; @@ -257,6 +259,7 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl } private class EitherChunk implements IChunkFilter { + private final IChunkFilter a; private final IChunkFilter b; diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index 4defff5d..af30b25d 100644 --- a/src/main/java/baritone/process/FollowProcess.java +++ b/src/main/java/baritone/process/FollowProcess.java @@ -29,7 +29,6 @@ import baritone.utils.BaritoneProcessHelper; import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; -import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -83,11 +82,11 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo private void scanWorld() { cache = Stream.of(ctx.world().loadedEntityList, ctx.world().playerEntities) - .flatMap(List::stream) - .filter(this::followable) - .filter(this.filter) - .distinct() - .collect(Collectors.toCollection(ArrayList::new)); + .flatMap(List::stream) + .filter(this::followable) + .filter(this.filter) + .distinct() + .collect(Collectors.toList()); } @Override diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 4fb4d540..5c1d7783 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.BlockOptionalMetaLookup; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; @@ -32,7 +33,10 @@ import net.minecraft.init.Blocks; import net.minecraft.inventory.ContainerPlayer; import net.minecraft.util.math.BlockPos; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; public final class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { @@ -171,7 +175,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 BlockOptionalMetaLookup(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..4043e627 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -22,10 +22,7 @@ import baritone.api.pathing.goals.*; import baritone.api.process.IMineProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; -import baritone.api.utils.BlockUtils; -import baritone.api.utils.IPlayerContext; -import baritone.api.utils.Rotation; -import baritone.api.utils.RotationUtils; +import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.cache.CachedChunk; import baritone.cache.WorldScanner; @@ -40,7 +37,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Blocks; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -59,7 +55,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro private static final int ORE_LOCATIONS_COUNT = 64; - private List mining; + private BlockOptionalMetaLookup filter; private List knownOreLocations; private List blacklist; // inaccessible private BlockPos branchPoint; @@ -73,28 +69,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.has(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 +143,19 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro @Override public void onLostControl() { - mine(0, (Block[]) null); + mine(0, (BlockOptionalMetaLookup) 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 +191,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 +212,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.has(state); } private Goal coalesce(BlockPos loc, List locs) { @@ -284,22 +281,15 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } } - public static List droppedItemsScan(List mining, World world) { + public static List droppedItemsScan(BlockOptionalMetaLookup 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())) { + if (filter.has(ei.getItem())) { ret.add(new BlockPos(entity)); } } @@ -307,30 +297,46 @@ 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, BlockOptionalMetaLookup 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)) { + List untracked = new ArrayList<>(); + for (BlockOptionalMeta bom : filter.blocks()) { + Block block = bom.getBlock(); + if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { + BetterBlockPos pf = ctx.baritone.getPlayerContext().playerFeet(); + // 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)); + locs.addAll(ctx.worldData.getCachedWorld().getLocationsOf( + BlockUtils.blockToString(block), + Baritone.settings().maxCachedWorldScanCount.value, + pf.x, + pf.z, + 2 + )); } else { - uninteresting.add(m); + untracked.add(block); } } - 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); + + if (!untracked.isEmpty() || (Baritone.settings().extendCacheOnThreshold.value && locs.size() < max)) { + 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 +346,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.has(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 +355,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, BlockOptionalMetaLookup 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.has(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 +373,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.has(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 +400,23 @@ 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 BlockOptionalMetaLookup(blocks)); } @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, BlockOptionalMetaLookup filter) { + this.filter = filter; + if (filter != null && !Baritone.settings().allowBreak.value) { logDirect("Unable to mine when allowBreak is false!"); - mining = null; + this.mine(quantity, (BlockOptionalMetaLookup) null); + return; } 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/selection/Selection.java b/src/main/java/baritone/selection/Selection.java new file mode 100644 index 00000000..58e069de --- /dev/null +++ b/src/main/java/baritone/selection/Selection.java @@ -0,0 +1,130 @@ +package baritone.selection; + +import baritone.api.selection.ISelection; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3i; + +public class Selection implements ISelection { + + private final BetterBlockPos pos1; + private final BetterBlockPos pos2; + private final BetterBlockPos min; + private final BetterBlockPos max; + private final Vec3i size; + private final AxisAlignedBB aabb; + + public Selection(BetterBlockPos pos1, BetterBlockPos pos2) { + this.pos1 = pos1; + this.pos2 = pos2; + + this.min = new BetterBlockPos( + Math.min(pos1.x, pos2.x), + Math.min(pos1.y, pos2.y), + Math.min(pos1.z, pos2.z) + ); + + this.max = new BetterBlockPos( + Math.max(pos1.x, pos2.x), + Math.max(pos1.y, pos2.y), + Math.max(pos1.z, pos2.z) + ); + + this.size = new Vec3i( + max.x - min.x + 1, + max.y - min.y + 1, + max.z - min.z + 1 + ); + + this.aabb = new AxisAlignedBB(this.min, this.max.add(1, 1, 1)); + } + + @Override + public BetterBlockPos pos1() { + return pos1; + } + + @Override + public BetterBlockPos pos2() { + return pos2; + } + + @Override + public BetterBlockPos min() { + return min; + } + + @Override + public BetterBlockPos max() { + return max; + } + + @Override + public Vec3i size() { + return size; + } + + @Override + public AxisAlignedBB aabb() { + return aabb; + } + + @Override + public int hashCode() { + return pos1.hashCode() ^ pos2.hashCode(); + } + + @Override + public String toString() { + return String.format("Selection{pos1=%s,pos2=%s}", pos1, pos2); + } + + /** + * Since it might not be immediately obvious what this does, let me explain. + *

+ * Let's say you specify EnumFacing.UP, this functions returns if pos2 is the highest BlockPos. + * If you specify EnumFacing.DOWN, it returns if pos2 is the lowest BlockPos. + * + * @param facing The direction to check. + * @return {@code true} if pos2 is further in that direction than pos1, {@code false} if it isn't, and something + * else if they're both at the same position on that axis (it really doesn't matter) + */ + private boolean isPos2(EnumFacing facing) { + boolean negative = facing.getAxisDirection().getOffset() < 0; + + switch (facing.getAxis()) { + case X: + return (pos2.x > pos1.x) ^ negative; + case Y: + return (pos2.y > pos1.y) ^ negative; + case Z: + return (pos2.z > pos1.z) ^ negative; + default: + throw new IllegalStateException("Bad EnumFacing.Axis"); + } + } + + @Override + public ISelection expand(EnumFacing direction, int blocks) { + if (isPos2(direction)) { + return new Selection(pos1, pos2.offset(direction, blocks)); + } else { + return new Selection(pos1.offset(direction, blocks), pos2); + } + } + + @Override + public ISelection contract(EnumFacing direction, int blocks) { + if (isPos2(direction)) { + return new Selection(pos1.offset(direction, blocks), pos2); + } else { + return new Selection(pos1, pos2.offset(direction, blocks)); + } + } + + @Override + public ISelection shift(EnumFacing direction, int blocks) { + return new Selection(pos1.offset(direction, blocks), pos2.offset(direction, blocks)); + } +} diff --git a/src/main/java/baritone/selection/SelectionManager.java b/src/main/java/baritone/selection/SelectionManager.java new file mode 100644 index 00000000..c7f21300 --- /dev/null +++ b/src/main/java/baritone/selection/SelectionManager.java @@ -0,0 +1,118 @@ +package baritone.selection; + +import baritone.Baritone; +import baritone.api.selection.ISelection; +import baritone.api.selection.ISelectionManager; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; + +import java.util.LinkedList; +import java.util.ListIterator; + +public class SelectionManager implements ISelectionManager { + + private final LinkedList selections = new LinkedList<>(); + private ISelection[] selectionsArr = new ISelection[0]; + + public SelectionManager(Baritone baritone) { + new SelectionRenderer(baritone, this); + } + + private void resetSelectionsArr() { + selectionsArr = selections.toArray(new ISelection[0]); + } + + @Override + public synchronized ISelection addSelection(ISelection selection) { + selections.add(selection); + resetSelectionsArr(); + return selection; + } + + @Override + public ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2) { + return addSelection(new Selection(pos1, pos2)); + } + + @Override + public synchronized ISelection removeSelection(ISelection selection) { + selections.remove(selection); + resetSelectionsArr(); + return selection; + } + + @Override + public synchronized ISelection[] removeAllSelections() { + ISelection[] selectionsArr = getSelections(); + selections.clear(); + resetSelectionsArr(); + return selectionsArr; + } + + @Override + public ISelection[] getSelections() { + return selectionsArr; + } + + @Override + public synchronized ISelection getOnlySelection() { + if (selections.size() == 1) { + return selections.peekFirst(); + } + + return null; + } + + @Override + public ISelection getLastSelection() { + return selections.peekLast(); + } + + @Override + public synchronized ISelection expand(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.expand(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } + + @Override + public synchronized ISelection contract(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.contract(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } + + @Override + public synchronized ISelection shift(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.shift(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } +} diff --git a/src/main/java/baritone/selection/SelectionRenderer.java b/src/main/java/baritone/selection/SelectionRenderer.java new file mode 100644 index 00000000..89104d03 --- /dev/null +++ b/src/main/java/baritone/selection/SelectionRenderer.java @@ -0,0 +1,57 @@ +package baritone.selection; + +import baritone.Baritone; +import baritone.api.event.events.RenderEvent; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.selection.ISelection; +import baritone.utils.IRenderer; +import net.minecraft.util.math.AxisAlignedBB; + +public class SelectionRenderer implements IRenderer, AbstractGameEventListener { + + public static final double SELECTION_BOX_EXPANSION = .005D; + + private final SelectionManager manager; + + SelectionRenderer(Baritone baritone, SelectionManager manager) { + this.manager = manager; + baritone.getGameEventHandler().registerEventListener(this); + } + + public static void renderSelections(ISelection[] selections) { + float opacity = settings.selectionOpacity.value; + boolean ignoreDepth = settings.renderSelectionIgnoreDepth.value; + float lineWidth = settings.selectionLineWidth.value; + + if (!settings.renderSelection.value) { + return; + } + + IRenderer.startLines(settings.colorSelection.value, opacity, lineWidth, ignoreDepth); + + for (ISelection selection : selections) { + IRenderer.drawAABB(selection.aabb(), SELECTION_BOX_EXPANSION); + } + + if (settings.renderSelectionCorners.value) { + IRenderer.glColor(settings.colorSelectionPos1.value, opacity); + + for (ISelection selection : selections) { + IRenderer.drawAABB(new AxisAlignedBB(selection.pos1(), selection.pos1().add(1, 1, 1))); + } + + IRenderer.glColor(settings.colorSelectionPos2.value, opacity); + + for (ISelection selection : selections) { + IRenderer.drawAABB(new AxisAlignedBB(selection.pos2(), selection.pos2().add(1, 1, 1))); + } + } + + IRenderer.endLines(ignoreDepth); + } + + @Override + public void onRenderPass(RenderEvent event) { + renderSelections(manager.getSelections()); + } +} diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index 06588c12..440bfb93 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -25,6 +25,7 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.math.RayTraceResult; public class BlockPlaceHelper implements Helper { + private final IPlayerContext ctx; private int rightClickTimer; diff --git a/src/main/java/baritone/utils/GuiClick.java b/src/main/java/baritone/utils/GuiClick.java index ce5c28a0..fc97605f 100644 --- a/src/main/java/baritone/utils/GuiClick.java +++ b/src/main/java/baritone/utils/GuiClick.java @@ -22,6 +22,7 @@ import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.GoalBlock; import baritone.api.pathing.goals.GoalTwoBlocks; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.Entity; @@ -29,6 +30,10 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; +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 org.lwjgl.BufferUtils; import org.lwjgl.input.Mouse; import org.lwjgl.util.glu.GLU; @@ -39,6 +44,7 @@ import java.nio.IntBuffer; import java.util.Collections; import static org.lwjgl.opengl.GL11.*; +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class GuiClick extends GuiScreen { @@ -75,7 +81,16 @@ public class GuiClick extends GuiScreen { protected void mouseReleased(int mouseX, int mouseY, int mouseButton) { if (mouseButton == 0) { if (clickStart != null && !clickStart.equals(currentMouseOver)) { - ((Baritone) BaritoneAPI.getProvider().getPrimaryBaritone()).getBuilderProcess().clearArea(clickStart, currentMouseOver); + BaritoneAPI.getProvider().getPrimaryBaritone().getSelectionManager().removeAllSelections(); + BaritoneAPI.getProvider().getPrimaryBaritone().getSelectionManager().addSelection(BetterBlockPos.from(clickStart), BetterBlockPos.from(currentMouseOver)); + ITextComponent component = new TextComponentString("Selection made! For usage: " + Baritone.settings().prefix.value + "help sel"); + component.getStyle() + .setColor(TextFormatting.WHITE) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + "help sel" + )); + Helper.HELPER.logDirect(component); clickStart = null; } else { BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalTwoBlocks(currentMouseOver)); @@ -110,7 +125,7 @@ public class GuiClick extends GuiScreen { GlStateManager.disableDepth(); BetterBlockPos a = new BetterBlockPos(currentMouseOver); BetterBlockPos b = new BetterBlockPos(clickStart); - PathRenderer.drawAABB(new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); + IRenderer.drawAABB(new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); GlStateManager.enableDepth(); GlStateManager.depthMask(true); diff --git a/src/main/java/baritone/utils/IRenderer.java b/src/main/java/baritone/utils/IRenderer.java new file mode 100644 index 00000000..0078f227 --- /dev/null +++ b/src/main/java/baritone/utils/IRenderer.java @@ -0,0 +1,112 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils; + +import baritone.api.BaritoneAPI; +import baritone.api.Settings; +import baritone.api.utils.Helper; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.math.AxisAlignedBB; + +import java.awt.*; + +import static org.lwjgl.opengl.GL11.*; + +public interface IRenderer { + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + RenderManager renderManager = Helper.mc.getRenderManager(); + Settings settings = BaritoneAPI.getSettings(); + + static void glColor(Color color, float alpha) { + float[] colorComponents = color.getColorComponents(null); + GlStateManager.color(colorComponents[0], colorComponents[1], colorComponents[2], alpha); + } + + static void startLines(Color color, float alpha, float lineWidth, boolean ignoreDepth) { + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + glColor(color, alpha); + GlStateManager.glLineWidth(lineWidth); + GlStateManager.disableTexture2D(); + GlStateManager.depthMask(false); + + if (ignoreDepth) { + GlStateManager.disableDepth(); + } + } + + static void startLines(Color color, float lineWidth, boolean ignoreDepth) { + startLines(color, .4f, lineWidth, ignoreDepth); + } + + static void endLines(boolean ignoredDepth) { + if (ignoredDepth) { + GlStateManager.enableDepth(); + } + + GlStateManager.depthMask(true); + GlStateManager.enableTexture2D(); + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + } + + static void drawAABB(AxisAlignedBB aabb) { + AxisAlignedBB toDraw = aabb.offset(-renderManager.viewerPosX, -renderManager.viewerPosY, -renderManager.viewerPosZ); + + buffer.begin(GL_LINES, DefaultVertexFormats.POSITION); + // bottom + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + // top + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + // corners + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + tessellator.draw(); + } + + static void drawAABB(AxisAlignedBB aabb, double expand) { + drawAABB(aabb.grow(expand, expand, expand)); + } +} diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 3a32a18c..47a06854 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -115,4 +115,4 @@ public final class InputOverrideHandler extends Behavior implements IInputOverri public BlockBreakHelper getBlockBreakHelper() { return blockBreakHelper; } -} \ No newline at end of file +} diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index b0157247..cc11f4e6 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -17,7 +17,6 @@ package baritone.utils; -import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.event.events.RenderEvent; import baritone.api.pathing.calc.IPath; @@ -28,9 +27,7 @@ import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.behavior.PathingBehavior; import baritone.pathing.path.PathExecutor; import net.minecraft.block.state.IBlockState; -import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.tileentity.TileEntityBeaconRenderer; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.entity.Entity; @@ -50,18 +47,15 @@ import static org.lwjgl.opengl.GL11.*; * @author Brady * @since 8/9/2018 */ -public final class PathRenderer implements Helper { - - private static final Tessellator TESSELLATOR = Tessellator.getInstance(); - private static final BufferBuilder BUFFER = TESSELLATOR.getBuffer(); +public final class PathRenderer implements IRenderer { private PathRenderer() {} public static void render(RenderEvent event, PathingBehavior behavior) { float partialTicks = event.getPartialTicks(); Goal goal = behavior.getGoal(); - if (mc.currentScreen instanceof GuiClick) { - ((GuiClick) mc.currentScreen).onRender(); + if (Helper.mc.currentScreen instanceof GuiClick) { + ((GuiClick) Helper.mc.currentScreen).onRender(); } int thisPlayerDimension = behavior.baritone.getPlayerContext().world().provider.getDimensionType().getId(); @@ -72,7 +66,7 @@ public final class PathRenderer implements Helper { return; } - Entity renderView = mc.getRenderViewEntity(); + Entity renderView = Helper.mc.getRenderViewEntity(); if (renderView.world != BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world()) { System.out.println("I have no idea what's going on"); @@ -81,19 +75,20 @@ public final class PathRenderer implements Helper { return; } - if (goal != null && Baritone.settings().renderGoal.value) { - drawDankLitGoalBox(renderView, goal, partialTicks, Baritone.settings().colorGoalBox.value); + if (goal != null && settings.renderGoal.value) { + drawDankLitGoalBox(renderView, goal, partialTicks, settings.colorGoalBox.value); } - if (!Baritone.settings().renderPath.value) { + if (!settings.renderPath.value) { return; } + PathExecutor current = behavior.getCurrent(); // this should prevent most race conditions? PathExecutor next = behavior.getNext(); // like, now it's not possible for current!=null to be true, then suddenly false because of another thread - if (current != null && Baritone.settings().renderSelectionBoxes.value) { - drawManySelectionBoxes(renderView, current.toBreak(), Baritone.settings().colorBlocksToBreak.value); - drawManySelectionBoxes(renderView, current.toPlace(), Baritone.settings().colorBlocksToPlace.value); - drawManySelectionBoxes(renderView, current.toWalkInto(), Baritone.settings().colorBlocksToWalkInto.value); + if (current != null && settings.renderSelectionBoxes.value) { + drawManySelectionBoxes(renderView, current.toBreak(), settings.colorBlocksToBreak.value); + drawManySelectionBoxes(renderView, current.toPlace(), settings.colorBlocksToPlace.value); + drawManySelectionBoxes(renderView, current.toWalkInto(), settings.colorBlocksToWalkInto.value); } //drawManySelectionBoxes(player, Collections.singletonList(behavior.pathStart()), partialTicks, Color.WHITE); @@ -101,63 +96,51 @@ public final class PathRenderer implements Helper { // Render the current path, if there is one if (current != null && current.getPath() != null) { int renderBegin = Math.max(current.getPosition() - 3, 0); - drawPath(current.getPath(), renderBegin, renderView, partialTicks, Baritone.settings().colorCurrentPath.value, Baritone.settings().fadePath.value, 10, 20); + drawPath(current.getPath(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20); } + if (next != null && next.getPath() != null) { - drawPath(next.getPath(), 0, renderView, partialTicks, Baritone.settings().colorNextPath.value, Baritone.settings().fadePath.value, 10, 20); + drawPath(next.getPath(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20); } // If there is a path calculation currently running, render the path calculation process behavior.getInProgress().ifPresent(currentlyRunning -> { currentlyRunning.bestPathSoFar().ifPresent(p -> { - drawPath(p, 0, renderView, partialTicks, Baritone.settings().colorBestPathSoFar.value, Baritone.settings().fadePath.value, 10, 20); + drawPath(p, 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20); }); - currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { - drawPath(mr, 0, renderView, partialTicks, Baritone.settings().colorMostRecentConsidered.value, Baritone.settings().fadePath.value, 10, 20); - drawManySelectionBoxes(renderView, Collections.singletonList(mr.getDest()), Baritone.settings().colorMostRecentConsidered.value); + currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { + drawPath(mr, 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20); + drawManySelectionBoxes(renderView, Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value); }); }); } - public static void drawPath(IPath path, int startIndex, Entity player, float partialTicks, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); - GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.value); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderPathIgnoreDepth.value) { - GlStateManager.disableDepth(); - } - List positions = path.positions(); - int next; - Tessellator tessellator = Tessellator.getInstance(); + public static void drawPath(IPath path, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { + IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); + int fadeStart = fadeStart0 + startIndex; int fadeEnd = fadeEnd0 + startIndex; - for (int i = startIndex; i < positions.size() - 1; i = next) { - BetterBlockPos start = positions.get(i); - next = i + 1; - BetterBlockPos end = positions.get(next); + List positions = path.positions(); + for (int i = startIndex, next; i < positions.size() - 1; i = next) { + BetterBlockPos start = positions.get(i); + BetterBlockPos end = positions.get(next = i + 1); int dirX = end.x - start.x; int dirY = end.y - start.y; int dirZ = end.z - start.z; - while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && (dirX == positions.get(next + 1).x - end.x && dirY == positions.get(next + 1).y - end.y && dirZ == positions.get(next + 1).z - end.z)) { - next++; - end = positions.get(next); - } - double x1 = start.x; - double y1 = start.y; - double z1 = start.z; - double x2 = end.x; - double y2 = end.y; - double z2 = end.z; - if (fadeOut) { + while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && + (dirX == positions.get(next + 1).x - end.x && + dirY == positions.get(next + 1).y - end.y && + dirZ == positions.get(next + 1).z - end.z)) { + end = positions.get(++next); + } + + if (fadeOut) { float alpha; + if (i <= fadeStart) { alpha = 0.4F; } else { @@ -166,109 +149,65 @@ public final class PathRenderer implements Helper { } alpha = 0.4F * (1.0F - (float) (i - fadeStart) / (float) (fadeEnd - fadeStart)); } - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], alpha); + + IRenderer.glColor(color, alpha); } - drawLine(x1, y1, z1, x2, y2, z2); + + drawLine(start.x, start.y, start.z, end.x, end.y, end.z); + tessellator.draw(); } - if (Baritone.settings().renderPathIgnoreDepth.value) { - GlStateManager.enableDepth(); - } - //GlStateManager.color(0.0f, 0.0f, 0.0f, 0.4f); - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.enableLighting(); - GlStateManager.disableBlend(); + + IRenderer.endLines(settings.renderPathIgnoreDepth.value); } - public static void drawLine(double bp1x, double bp1y, double bp1z, double bp2x, double bp2y, double bp2z) { - double d0 = mc.getRenderManager().viewerPosX; - double d1 = mc.getRenderManager().viewerPosY; - double d2 = mc.getRenderManager().viewerPosZ; - BUFFER.begin(GL_LINE_STRIP, 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(); + public static void drawLine(double x1, double y1, double z1, double x2, double y2, double z2) { + double vpX = renderManager.viewerPosX; + double vpY = renderManager.viewerPosY; + double vpZ = renderManager.viewerPosZ; + boolean renderPathAsFrickinThingy = !settings.renderPathAsLine.value; + + buffer.begin(renderPathAsFrickinThingy ? GL_LINE_STRIP : GL_LINES, DefaultVertexFormats.POSITION); + buffer.pos(x1 + 0.5D - vpX, y1 + 0.5D - vpY, z1 + 0.5D - vpZ).endVertex(); + buffer.pos(x2 + 0.5D - vpX, y2 + 0.5D - vpY, z2 + 0.5D - vpZ).endVertex(); + + if (renderPathAsFrickinThingy) { + buffer.pos(x2 + 0.5D - vpX, y2 + 0.53D - vpY, z2 + 0.5D - vpZ).endVertex(); + buffer.pos(x1 + 0.5D - vpX, y1 + 0.53D - vpY, z1 + 0.5D - vpZ).endVertex(); + buffer.pos(x1 + 0.5D - vpX, y1 + 0.5D - vpY, z1 + 0.5D - vpZ).endVertex(); + } } public static void drawManySelectionBoxes(Entity player, Collection positions, Color color) { - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); - GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.value); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.value) { - GlStateManager.disableDepth(); - } - + IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderSelectionBoxesIgnoreDepth.value); //BlockPos blockpos = movingObjectPositionIn.getBlockPos(); BlockStateInterface bsi = new BlockStateInterface(BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext()); // TODO this assumes same dimension between primary baritone and render view? is this safe? + positions.forEach(pos -> { IBlockState state = bsi.get0(pos); AxisAlignedBB toDraw; + if (state.getBlock().equals(Blocks.AIR)) { toDraw = Blocks.DIRT.getDefaultState().getSelectedBoundingBox(player.world, pos); } else { toDraw = state.getSelectedBoundingBox(player.world, pos); } - drawAABB(toDraw); + + IRenderer.drawAABB(toDraw, .002D); }); - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.value) { - GlStateManager.enableDepth(); - } - - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); - } - - public static void drawAABB(AxisAlignedBB aabb) { - float expand = 0.002F; - AxisAlignedBB toDraw = aabb.expand(expand, expand, expand).offset(-mc.getRenderManager().viewerPosX, -mc.getRenderManager().viewerPosY, -mc.getRenderManager().viewerPosZ); - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - TESSELLATOR.draw(); - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - TESSELLATOR.draw(); - BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); - TESSELLATOR.draw(); + IRenderer.endLines(settings.renderSelectionBoxesIgnoreDepth.value); } public static void drawDankLitGoalBox(Entity player, Goal goal, float partialTicks, Color color) { - double renderPosX = mc.getRenderManager().viewerPosX; - double renderPosY = mc.getRenderManager().viewerPosY; - double renderPosZ = mc.getRenderManager().viewerPosZ; - double minX; - double maxX; - double minZ; - double maxZ; - double minY; - double maxY; - double y1; - double y2; + double renderPosX = renderManager.viewerPosX; + double renderPosY = renderManager.viewerPosY; + double renderPosZ = renderManager.viewerPosZ; + double minX, maxX; + double minZ, maxZ; + double minY, maxY; + double y1, y2; double y = MathHelper.cos((float) (((float) ((System.nanoTime() / 100000L) % 20000L)) / 20000F * Math.PI * 2)); if (goal instanceof IGoalRenderPos) { BlockPos goalPos = ((IGoalRenderPos) goal).getGoalPos(); @@ -291,12 +230,12 @@ public final class PathRenderer implements Helper { } else if (goal instanceof GoalXZ) { GoalXZ goalPos = (GoalXZ) goal; - if (Baritone.settings().renderGoalXZBeacon.value) { + if (settings.renderGoalXZBeacon.value) { glPushAttrib(GL_LIGHTING_BIT); - mc.getTextureManager().bindTexture(TileEntityBeaconRenderer.TEXTURE_BEACON_BEAM); + Helper.mc.getTextureManager().bindTexture(TileEntityBeaconRenderer.TEXTURE_BEACON_BEAM); - if (Baritone.settings().renderGoalIgnoreDepth.value) { + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.disableDepth(); } @@ -312,7 +251,7 @@ public final class PathRenderer implements Helper { color.getColorComponents(null) ); - if (Baritone.settings().renderGoalIgnoreDepth.value) { + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.enableDepth(); } @@ -334,12 +273,15 @@ public final class PathRenderer implements Helper { drawDankLitGoalBox(player, g, partialTicks, color); } return; + } else if (goal instanceof GoalInverted) { + drawDankLitGoalBox(player, ((GoalInverted) goal).origin, partialTicks, settings.colorInvertedGoalBox.value); + return; } else if (goal instanceof GoalYLevel) { GoalYLevel goalpos = (GoalYLevel) goal; - minX = player.posX - Baritone.settings().yLevelBoxSize.value - renderPosX; - minZ = player.posZ - Baritone.settings().yLevelBoxSize.value - renderPosZ; - maxX = player.posX + Baritone.settings().yLevelBoxSize.value - renderPosX; - maxZ = player.posZ + Baritone.settings().yLevelBoxSize.value - renderPosZ; + minX = player.posX - settings.yLevelBoxSize.value - renderPosX; + minZ = player.posZ - settings.yLevelBoxSize.value - renderPosZ; + maxX = player.posX + settings.yLevelBoxSize.value - renderPosX; + maxZ = player.posZ + settings.yLevelBoxSize.value - renderPosZ; minY = ((GoalYLevel) goal).level - renderPosY; maxY = minY + 2; y1 = 1 + y + goalpos.level - renderPosY; @@ -348,46 +290,33 @@ public final class PathRenderer implements Helper { return; } - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.6F); - GlStateManager.glLineWidth(Baritone.settings().goalRenderLineWidthPixels.value); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderGoalIgnoreDepth.value) { - GlStateManager.disableDepth(); - } + IRenderer.startLines(color, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); renderHorizontalQuad(minX, maxX, minZ, maxZ, y1); renderHorizontalQuad(minX, maxX, minZ, maxZ, y2); - BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, minY, minZ).endVertex(); - BUFFER.pos(minX, maxY, minZ).endVertex(); - BUFFER.pos(maxX, minY, minZ).endVertex(); - BUFFER.pos(maxX, maxY, minZ).endVertex(); - BUFFER.pos(maxX, minY, maxZ).endVertex(); - BUFFER.pos(maxX, maxY, maxZ).endVertex(); - BUFFER.pos(minX, minY, maxZ).endVertex(); - BUFFER.pos(minX, maxY, maxZ).endVertex(); - TESSELLATOR.draw(); + buffer.begin(GL_LINES, DefaultVertexFormats.POSITION); + buffer.pos(minX, minY, minZ).endVertex(); + buffer.pos(minX, maxY, minZ).endVertex(); + buffer.pos(maxX, minY, minZ).endVertex(); + buffer.pos(maxX, maxY, minZ).endVertex(); + buffer.pos(maxX, minY, maxZ).endVertex(); + buffer.pos(maxX, maxY, maxZ).endVertex(); + buffer.pos(minX, minY, maxZ).endVertex(); + buffer.pos(minX, maxY, maxZ).endVertex(); + tessellator.draw(); - if (Baritone.settings().renderGoalIgnoreDepth.value) { - GlStateManager.enableDepth(); - } - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); + IRenderer.endLines(settings.renderGoalIgnoreDepth.value); } private static void renderHorizontalQuad(double minX, double maxX, double minZ, double maxZ, double y) { if (y != 0) { - BUFFER.begin(GL_LINE_LOOP, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, y, minZ).endVertex(); - BUFFER.pos(maxX, y, minZ).endVertex(); - BUFFER.pos(maxX, y, maxZ).endVertex(); - BUFFER.pos(minX, y, maxZ).endVertex(); - TESSELLATOR.draw(); + buffer.begin(GL_LINE_LOOP, DefaultVertexFormats.POSITION); + buffer.pos(minX, y, minZ).endVertex(); + buffer.pos(maxX, y, minZ).endVertex(); + buffer.pos(maxX, y, maxZ).endVertex(); + buffer.pos(minX, y, maxZ).endVertex(); + tessellator.draw(); } } } diff --git a/src/main/java/baritone/utils/PathingCommandContext.java b/src/main/java/baritone/utils/PathingCommandContext.java index 1e8bfe6a..c43166e9 100644 --- a/src/main/java/baritone/utils/PathingCommandContext.java +++ b/src/main/java/baritone/utils/PathingCommandContext.java @@ -23,6 +23,7 @@ import baritone.api.process.PathingCommandType; import baritone.pathing.movement.CalculationContext; public class PathingCommandContext extends PathingCommand { + public final CalculationContext desiredCalcContext; public PathingCommandContext(Goal goal, PathingCommandType commandType, CalculationContext context) { diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index 14d921d0..a83e53a1 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -32,6 +32,7 @@ import net.minecraft.util.math.BlockPos; import java.util.*; public class PathingControlManager implements IPathingControlManager { + private final Baritone baritone; private final HashSet processes; // unGh private final List active; diff --git a/src/main/java/baritone/utils/PlayerMovementInput.java b/src/main/java/baritone/utils/PlayerMovementInput.java index f41e0f62..22e2063a 100644 --- a/src/main/java/baritone/utils/PlayerMovementInput.java +++ b/src/main/java/baritone/utils/PlayerMovementInput.java @@ -21,6 +21,7 @@ import baritone.api.utils.input.Input; import net.minecraft.util.MovementInput; public class PlayerMovementInput extends MovementInput { + private final InputOverrideHandler handler; PlayerMovementInput(InputOverrideHandler handler) { diff --git a/src/main/java/baritone/utils/ToolSet.java b/src/main/java/baritone/utils/ToolSet.java index 860f0577..90f06bb0 100644 --- a/src/main/java/baritone/utils/ToolSet.java +++ b/src/main/java/baritone/utils/ToolSet.java @@ -38,6 +38,7 @@ import java.util.function.Function; * @author Avery, Brady, leijurv */ public class ToolSet { + /** * A cache mapping a {@link Block} to how long it will take to break * with this toolset, given the optimum tool is used. @@ -118,7 +119,7 @@ public class ToolSet { } else if (speed == highestSpeed) { int cost = getMaterialCost(itemStack); if ((cost < lowestCost && (silkTouch || !bestSilkTouch)) || - (preferSilkTouch && !bestSilkTouch && silkTouch)) { + (preferSilkTouch && !bestSilkTouch && silkTouch)) { highestSpeed = speed; best = i; lowestCost = cost; diff --git a/src/main/java/baritone/utils/accessor/IBitArray.java b/src/main/java/baritone/utils/accessor/IBitArray.java new file mode 100644 index 00000000..81501036 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IBitArray.java @@ -0,0 +1,8 @@ +package baritone.utils.accessor; + +public interface IBitArray { + + int getAtFast(int index); + + int[] toArray(); +} diff --git a/src/main/java/baritone/utils/accessor/IBlockStateContainer.java b/src/main/java/baritone/utils/accessor/IBlockStateContainer.java new file mode 100644 index 00000000..7c47a9b8 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IBlockStateContainer.java @@ -0,0 +1,18 @@ +package baritone.utils.accessor; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BitArray; +import net.minecraft.world.chunk.IBlockStatePalette; + +public interface IBlockStateContainer { + + IBlockStatePalette getPalette(); + + BitArray getStorage(); + + IBlockState getFast(int index); + + IBlockState getAtPalette(int index); + + int[] storageArray(); +} diff --git a/src/main/java/baritone/utils/accessor/IChunkProviderClient.java b/src/main/java/baritone/utils/accessor/IChunkProviderClient.java index 19f14685..e5fde40b 100644 --- a/src/main/java/baritone/utils/accessor/IChunkProviderClient.java +++ b/src/main/java/baritone/utils/accessor/IChunkProviderClient.java @@ -21,5 +21,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import net.minecraft.world.chunk.Chunk; public interface IChunkProviderClient { + Long2ObjectMap loadedChunks(); } diff --git a/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java b/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java index 58959d6d..354b9eef 100644 --- a/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java +++ b/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java @@ -20,6 +20,7 @@ package baritone.utils.accessor; import net.minecraft.util.math.BlockPos; public interface IPlayerControllerMP { + void setIsHittingBlock(boolean isHittingBlock); BlockPos getCurrentBlock(); 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..e187c3e6 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/ITabCompleter.java @@ -0,0 +1,10 @@ +package baritone.utils.accessor; + +public interface ITabCompleter { + + String getPrefix(); + + void setPrefix(String prefix); + + boolean onGuiChatSetCompletions(String[] newCompl); +} diff --git a/src/main/java/baritone/utils/command/BaritoneChatControl.java b/src/main/java/baritone/utils/command/BaritoneChatControl.java new file mode 100644 index 00000000..facc24da --- /dev/null +++ b/src/main/java/baritone/utils/command/BaritoneChatControl.java @@ -0,0 +1,198 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command; + +import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; +import baritone.api.Settings; +import baritone.api.accessor.IGuiScreen; +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.SettingsUtil; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.exception.CommandNotEnoughArgumentsException; +import baritone.api.utils.command.exception.CommandNotFoundException; +import baritone.api.utils.command.execution.ICommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.manager.ICommandManager; +import net.minecraft.util.Tuple; +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.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class BaritoneChatControl implements Helper, AbstractGameEventListener { + + private static final Settings settings = BaritoneAPI.getSettings(); + private final ICommandManager manager; + + public BaritoneChatControl(IBaritone baritone) { + this.manager = baritone.getCommandManager(); + 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 ((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(ICommandExecution.expand(commandStr).getFirst()).handle(null, null); + } + } else if ((settings.chatControl.value || settings.chatControlAnyway.value) && runCommand(msg)) { + event.cancel(); + } + } + + private void logRanCommand(String command, String rest) { + if (settings.echoCommands.value) { + String msg = command + rest; + String toDisplay = settings.censorRanCommands.value ? command + " ..." : msg; + ITextComponent component = new TextComponentString(String.format("> %s", toDisplay)); + component.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 + )); + logDirect(component); + } + } + + public boolean runCommand(String msg) { + if (msg.trim().equalsIgnoreCase("damn")) { + logDirect("daniel"); + return false; + } else if (msg.trim().equalsIgnoreCase("orderpizza")) { + try { + ((IGuiScreen) mc.currentScreen).openLink(new URI("https://www.dominos.com/en/pages/order/")); + } catch (NullPointerException | URISyntaxException ignored) {} + return false; + } + if (msg.isEmpty()) { + return this.runCommand("help"); + } + Tuple> pair = ICommandExecution.expand(msg); + String command = pair.getFirst(); + String rest = msg.substring(pair.getFirst().length()); + ArgConsumer argc = new ArgConsumer(this.manager, pair.getSecond()); + if (!argc.hasAny()) { + Settings.Setting setting = settings.byLowerName.get(command.toLowerCase(Locale.US)); + if (setting != null) { + logRanCommand(command, rest); + if (setting.getValueClass() == Boolean.class) { + this.manager.execute(String.format("set toggle %s", setting.getName())); + } else { + this.manager.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.getFirst())) { + logRanCommand(command, rest); + try { + this.manager.execute(String.format("set %s %s", setting.getName(), argc.getString())); + } catch (CommandNotEnoughArgumentsException ignored) {} // The operation is safe + return true; + } + } + } + + // If the command exists, then handle echoing the input + if (this.manager.getCommand(pair.getFirst()) != null) { + logRanCommand(command, rest); + } + + return this.manager.execute(pair); + } + + @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) { + try { + List args = CommandArgument.from(msg, true); + ArgConsumer argc = new ArgConsumer(this.manager, args); + if (argc.hasAtMost(2)) { + if (argc.hasExactly(1)) { + return new TabCompleteHelper() + .addCommands(this.manager) + .addSettings() + .filterPrefix(argc.getString()) + .stream(); + } + Settings.Setting setting = settings.byLowerName.get(argc.getString().toLowerCase(Locale.US)); + if (setting != null) { + if (setting.getValueClass() == Boolean.class) { + TabCompleteHelper helper = new TabCompleteHelper(); + if ((Boolean) setting.value) { + helper.append("true", "false"); + } else { + helper.append("false", "true"); + } + return helper.filterPrefix(argc.getString()).stream(); + } else { + return Stream.of(SettingsUtil.settingValueToString(setting)); + } + } + } + return this.manager.tabComplete(msg); + } catch (CommandNotEnoughArgumentsException ignored) { // Shouldn't happen, the operation is safe + return Stream.empty(); + } + } +} diff --git a/src/main/java/baritone/utils/command/defaults/AxisCommand.java b/src/main/java/baritone/utils/command/defaults/AxisCommand.java new file mode 100644 index 00000000..ae52bfb2 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/AxisCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalAxis; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class AxisCommand extends Command { + + public AxisCommand(IBaritone baritone) { + super(baritone, Arrays.asList("axis", "highway")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + 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) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Set a goal to the axes"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/BlacklistCommand.java b/src/main/java/baritone/utils/command/defaults/BlacklistCommand.java new file mode 100644 index 00000000..21551782 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/BlacklistCommand.java @@ -0,0 +1,70 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.process.IGetToBlockProcess; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class BlacklistCommand extends Command { + + public BlacklistCommand(IBaritone baritone) { + super(baritone, "blacklist"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + 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) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Blacklist closest block"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "While going to a block this command blacklists the closest block so that Baritone won't attempt to get to it.", + "", + "Usage:", + "> blacklist" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/BuildCommand.java b/src/main/java/baritone/utils/command/defaults/BuildCommand.java new file mode 100644 index 00000000..f4537113 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/BuildCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeBlockPos; +import baritone.api.utils.command.datatypes.RelativeFile; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.client.Minecraft; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +public class BuildCommand extends Command { + + private static final File schematicsDir = new File(Minecraft.getMinecraft().gameDir, "schematics"); + + public BuildCommand(IBaritone baritone) { + super(baritone, "build"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + File file = args.getDatatypePost(RelativeFile.INSTANCE, schematicsDir).getAbsoluteFile(); + if (!file.getName().toLowerCase(Locale.US).endsWith(".schematic")) { + file = new File(file.getAbsolutePath() + ".schematic"); + } + BetterBlockPos origin = ctx.playerFeet(); + BetterBlockPos buildOrigin; + if (args.hasAny()) { + args.requireMax(3); + buildOrigin = args.getDatatypePost(RelativeBlockPos.INSTANCE, origin); + } else { + args.requireMax(0); + buildOrigin = origin; + } + boolean success = baritone.getBuilderProcess().build(file.getName(), file, buildOrigin); + if (!success) { + throw new CommandInvalidStateException("Couldn't load the schematic"); + } + logDirect(String.format("Successfully loaded schematic for building\nOrigin: %s", buildOrigin)); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return RelativeFile.tabComplete(args, schematicsDir); + } else if (args.has(2)) { + args.get(); + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Build a schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Build a schematic from a file.", + "", + "Usage:", + "> build - Loads and builds '.schematic'", + "> build - Custom position" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/CancelCommand.java b/src/main/java/baritone/utils/command/defaults/CancelCommand.java new file mode 100644 index 00000000..a4ef4cc7 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/CancelCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class CancelCommand extends Command { + + public CancelCommand(IBaritone baritone) { + super(baritone, Arrays.asList("cancel", "stop")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getPathingBehavior().cancelEverything(); + logDirect("ok canceled"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Cancel what Baritone is currently doing"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The cancel command tells Baritone to stop whatever it's currently doing.", + "", + "Usage:", + "> cancel" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ChestsCommand.java b/src/main/java/baritone/utils/command/defaults/ChestsCommand.java new file mode 100644 index 00000000..d4101a62 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ChestsCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.cache.IRememberedInventory; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +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.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +public class ChestsCommand extends Command { + + public ChestsCommand(IBaritone baritone) { + super(baritone, "chests"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + Set> entries = + ctx.worldData().getContainerMemory().getRememberedInventories().entrySet(); + if (entries.isEmpty()) { + throw new CommandInvalidStateException("No remembered inventories"); + } + for (Map.Entry entry : entries) { + // betterblockpos has censoring + BetterBlockPos pos = new BetterBlockPos(entry.getKey()); + IRememberedInventory inv = entry.getValue(); + logDirect(pos.toString()); + for (ItemStack item : inv.getContents()) { + ITextComponent component = item.getTextComponent(); + component.appendText(String.format(" x %d", item.getCount())); + logDirect(component); + } + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Display remembered inventories"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The chests command lists remembered inventories, I guess?", + "", + "Usage:", + "> chests" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ClearareaCommand.java b/src/main/java/baritone/utils/command/defaults/ClearareaCommand.java new file mode 100644 index 00000000..39f8809d --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ClearareaCommand.java @@ -0,0 +1,80 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ClearareaCommand extends Command { + + public ClearareaCommand(IBaritone baritone) { + super(baritone, "cleararea"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + BetterBlockPos pos1 = ctx.playerFeet(); + BetterBlockPos pos2; + if (args.hasAny()) { + args.requireMax(3); + pos2 = args.getDatatypePost(RelativeBlockPos.INSTANCE, 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) { + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + + @Override + public String getShortDesc() { + return "Clear an area of all blocks"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/ClickCommand.java b/src/main/java/baritone/utils/command/defaults/ClickCommand.java new file mode 100644 index 00000000..e5180b2f --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ClickCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ClickCommand extends Command { + + public ClickCommand(IBaritone baritone) { + super(baritone, "click"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.openClick(); + logDirect("aight dude"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Open click"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Opens click dude", + "", + "Usage:", + "> click" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ComeCommand.java b/src/main/java/baritone/utils/command/defaults/ComeCommand.java new file mode 100644 index 00000000..bbefeb43 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ComeCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +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.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ComeCommand extends Command { + + public ComeCommand(IBaritone baritone) { + super(baritone, "come"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + Entity entity = mc.getRenderViewEntity(); + if (entity == null) { + 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) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Start heading towards your camera"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The come command tells Baritone to head towards your camera.", + "", + "This can be useful in hacked clients where freecam doesn't move your player position.", + "", + "Usage:", + "> come" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/CommandAlias.java b/src/main/java/baritone/utils/command/defaults/CommandAlias.java new file mode 100644 index 00000000..cbd8ac77 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/CommandAlias.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.utils.command.defaults; + +import baritone.api.IBaritone; +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 CommandAlias extends Command { + + private final String shortDesc; + public final String target; + + public CommandAlias(IBaritone baritone, List names, String shortDesc, String target) { + super(baritone, names); + this.shortDesc = shortDesc; + this.target = target; + } + + public CommandAlias(IBaritone baritone, String name, String shortDesc, String target) { + super(baritone, name); + this.shortDesc = shortDesc; + this.target = target; + } + + @Override + protected void executed(String label, ArgConsumer args) { + this.baritone.getCommandManager().execute(String.format("%s %s", target, args.rawRest())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return this.baritone.getCommandManager().tabComplete(String.format("%s %s", target, args.rawRest())); + } + + @Override + public String getShortDesc() { + return shortDesc; + } + + @Override + public List getLongDesc() { + return Collections.singletonList(String.format("This command is an alias, for: %s ...", target)); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/DefaultCommands.java b/src/main/java/baritone/utils/command/defaults/DefaultCommands.java new file mode 100644 index 00000000..73ddda92 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/DefaultCommands.java @@ -0,0 +1,75 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; + +import java.util.*; + +public final class DefaultCommands { + + private DefaultCommands() {} + + public static List createAll(IBaritone baritone) { + Objects.requireNonNull(baritone); + List commands = new ArrayList<>(Arrays.asList( + new HelpCommand(baritone), + new SetCommand(baritone), + new CommandAlias(baritone, Arrays.asList("modified", "mod", "baritone", "modifiedsettings"), "List modified settings", "set modified"), + new CommandAlias(baritone, "reset", "Reset all settings or just one", "set reset"), + new GoalCommand(baritone), + new PathCommand(baritone), + new ProcCommand(baritone), + new VersionCommand(baritone), + new RepackCommand(baritone), + new BuildCommand(baritone), + new SchematicaCommand(baritone), + new ComeCommand(baritone), + new AxisCommand(baritone), + new CancelCommand(baritone), + new ForceCancelCommand(baritone), + new GcCommand(baritone), + new InvertCommand(baritone), + new ClearareaCommand(baritone), + new TunnelCommand(baritone), + new RenderCommand(baritone), + new FarmCommand(baritone), + new ChestsCommand(baritone), + new FollowCommand(baritone), + new ExploreFilterCommand(baritone), + new ReloadAllCommand(baritone), + new SaveAllCommand(baritone), + new ExploreCommand(baritone), + new BlacklistCommand(baritone), + new FindCommand(baritone), + new MineCommand(baritone), + new ClickCommand(baritone), + new ThisWayCommand(baritone), + new WaypointsCommand(baritone), + new CommandAlias(baritone, "sethome", "Sets your home waypoint", "waypoints save home"), + new CommandAlias(baritone, "home", "Set goal to your home waypoint", "waypoints goal home"), + new SelCommand(baritone) + )); + PauseResumeCommands prc = new PauseResumeCommands(baritone); + commands.add(prc.pauseCommand); + commands.add(prc.resumeCommand); + commands.add(prc.pausedCommand); + return Collections.unmodifiableList(commands); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ExploreCommand.java b/src/main/java/baritone/utils/command/defaults/ExploreCommand.java new file mode 100644 index 00000000..dc156d0e --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ExploreCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeGoalXZ; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ExploreCommand extends Command { + + public ExploreCommand(IBaritone baritone) { + super(baritone, "explore"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + if (args.hasAny()) { + args.requireExactly(2); + } else { + args.requireMax(0); + } + GoalXZ goal = args.hasAny() + ? args.getDatatypePost(RelativeGoalXZ.INSTANCE, 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) { + if (args.hasAtMost(2)) { + return args.tabCompleteDatatype(RelativeGoalXZ.INSTANCE); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Explore things"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/ExploreFilterCommand.java b/src/main/java/baritone/utils/command/defaults/ExploreFilterCommand.java new file mode 100644 index 00000000..4d75d1b4 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ExploreFilterCommand.java @@ -0,0 +1,91 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeFile; +import baritone.api.utils.command.exception.CommandException; +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.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ExploreFilterCommand extends Command { + + public ExploreFilterCommand(IBaritone baritone) { + super(baritone, "explorefilter"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(2); + File file = args.getDatatypePost(RelativeFile.INSTANCE, mc.gameDir.getAbsoluteFile().getParentFile()); + boolean invert = false; + if (args.hasAny()) { + if (args.getString().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 IllegalStateException(e); + } + logDirect(String.format("Explore filter applied. Inverted: %s", Boolean.toString(invert))); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return RelativeFile.tabComplete(args, RelativeFile.gameDir()); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Explore chunks from a json"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/FarmCommand.java b/src/main/java/baritone/utils/command/defaults/FarmCommand.java new file mode 100644 index 00000000..1f803ec6 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FarmCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class FarmCommand extends Command { + + public FarmCommand(IBaritone baritone) { + super(baritone, "farm"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getFarmProcess().farm(); + logDirect("Farming"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Farm nearby crops"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The farm command starts farming nearby plants. It harvests mature crops and plants new ones.", + "", + "Usage:", + "> farm" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/FindCommand.java b/src/main/java/baritone/utils/command/defaults/FindCommand.java new file mode 100644 index 00000000..8848db81 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FindCommand.java @@ -0,0 +1,80 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.BlockById; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.block.Block; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class FindCommand extends Command { + + public FindCommand(IBaritone baritone) { + super(baritone, "find"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + List toFind = new ArrayList<>(); + while (args.hasAny()) { + toFind.add(args.getDatatypeFor(BlockById.INSTANCE)); + } + 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) { + return args.tabCompleteDatatype(BlockById.INSTANCE); + } + + @Override + public String getShortDesc() { + return "Find positions of a certain block"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "", + "", + "Usage:", + "> " + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/FollowCommand.java b/src/main/java/baritone/utils/command/defaults/FollowCommand.java new file mode 100644 index 00000000..e3e0c3d1 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FollowCommand.java @@ -0,0 +1,155 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.EntityClassById; +import baritone.api.utils.command.datatypes.IDatatypeFor; +import baritone.api.utils.command.datatypes.NearbyPlayer; +import baritone.api.utils.command.exception.CommandException; +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.*; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FollowCommand extends Command { + + public FollowCommand(IBaritone baritone) { + super(baritone, "follow"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMin(1); + FollowGroup group; + FollowList list; + List entities = new ArrayList<>(); + List> classes = new ArrayList<>(); + if (args.hasExactlyOne()) { + baritone.getFollowProcess().follow((group = args.getEnum(FollowGroup.class)).filter); + } else { + args.requireMin(2); + group = null; + list = args.getEnum(FollowList.class); + while (args.hasAny()) { + 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 (group != null) { + 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) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(FollowGroup.class) + .append(FollowList.class) + .filterPrefix(args.getString()) + .stream(); + } else { + IDatatypeFor followType; + try { + followType = args.getEnum(FollowList.class).datatype; + } catch (NullPointerException e) { + return Stream.empty(); + } + while (args.has(2)) { + if (args.peekDatatypeOrNull(followType) == null) { + return Stream.empty(); + } + args.get(); + } + return args.tabCompleteDatatype(followType); + } + } + + @Override + public String getShortDesc() { + return "Follow entity things"; + } + + @Override + public List getLongDesc() { + return Arrays.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.INSTANCE), + PLAYER(NearbyPlayer.INSTANCE); + + final IDatatypeFor datatype; + + FollowList(IDatatypeFor datatype) { + this.datatype = datatype; + } + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ForceCancelCommand.java b/src/main/java/baritone/utils/command/defaults/ForceCancelCommand.java new file mode 100644 index 00000000..3de8a056 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ForceCancelCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.behavior.IPathingBehavior; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ForceCancelCommand extends Command { + + public ForceCancelCommand(IBaritone baritone) { + super(baritone, "forcecancel"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); + pathingBehavior.cancelEverything(); + pathingBehavior.forceCancel(); + logDirect("ok force canceled"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Force cancel"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Like cancel, but more forceful.", + "", + "Usage:", + "> forcecancel" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/GcCommand.java b/src/main/java/baritone/utils/command/defaults/GcCommand.java new file mode 100644 index 00000000..c2e7952c --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/GcCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class GcCommand extends Command { + + public GcCommand(IBaritone baritone) { + super(baritone, "gc"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + System.gc(); + logDirect("ok called System.gc()"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Call System.gc()"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Calls System.gc().", + "", + "Usage:", + "> gc" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/GoalCommand.java b/src/main/java/baritone/utils/command/defaults/GoalCommand.java new file mode 100644 index 00000000..3f760111 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/GoalCommand.java @@ -0,0 +1,102 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class GoalCommand extends Command { + + public GoalCommand(IBaritone baritone) { + super(baritone, "goal"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + ICustomGoalProcess goalProcess = baritone.getCustomGoalProcess(); + if (args.hasAny() && Arrays.asList("reset", "clear", "none").contains(args.peekString())) { + args.requireMax(1); + if (goalProcess.getGoal() != null) { + 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.getDatatypePost(RelativeGoal.INSTANCE, origin); + goalProcess.setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + TabCompleteHelper helper = new TabCompleteHelper(); + if (args.hasExactlyOne()) { + helper.append("reset", "clear", "none", "~"); + } else { + if (args.hasAtMost(3)) { + while (args.has(2)) { + if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { + break; + } + args.get(); + if (!args.has(2)) { + helper.append("~"); + } + } + } + } + return helper.filterPrefix(args.getString()).stream(); + } + + @Override + public String getShortDesc() { + return "Set or clear the goal"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/HelpCommand.java b/src/main/java/baritone/utils/command/defaults/HelpCommand.java new file mode 100644 index 00000000..a9d0e104 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/HelpCommand.java @@ -0,0 +1,125 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +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 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.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class HelpCommand extends Command { + + public HelpCommand(IBaritone baritone) { + super(baritone, Arrays.asList("help", "?")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(1); + if (!args.hasAny() || args.is(Integer.class)) { + Paginator.paginate( + args, new Paginator<>( + this.baritone.getCommandManager().getRegistry().descendingStream() + .filter(command -> !command.hiddenFromHelp()) + .collect(Collectors.toList()) + ), + () -> logDirect("All Baritone commands (clickable):"), + command -> { + String names = String.join("/", command.names); + String name = command.names.get(0); + ITextComponent shortDescComponent = new TextComponentString(" - " + command.getShortDesc()); + shortDescComponent.getStyle().setColor(TextFormatting.DARK_GRAY); + ITextComponent namesComponent = new TextComponentString(names); + namesComponent.getStyle().setColor(TextFormatting.WHITE); + ITextComponent hoverComponent = new TextComponentString(""); + hoverComponent.getStyle().setColor(TextFormatting.GRAY); + hoverComponent.appendSibling(namesComponent); + hoverComponent.appendText("\n" + command.getShortDesc()); + hoverComponent.appendText("\n\nClick to view full help"); + String clickCommand = FORCE_COMMAND_PREFIX + String.format("%s %s", label, command.names.get(0)); + ITextComponent component = new TextComponentString(name); + component.getStyle().setColor(TextFormatting.GRAY); + component.appendSibling(shortDescComponent); + component.getStyle() + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent)) + .setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, clickCommand)); + return component; + }, + FORCE_COMMAND_PREFIX + label + ); + } else { + String commandName = args.getString().toLowerCase(); + Command command = this.baritone.getCommandManager().getCommand(commandName); + if (command == null) { + throw new CommandNotFoundException(commandName); + } + logDirect(String.format("%s - %s", String.join(" / ", command.names), command.getShortDesc())); + logDirect(""); + command.getLongDesc().forEach(this::logDirect); + logDirect(""); + ITextComponent returnComponent = new TextComponentString("Click to return to the help menu"); + returnComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + label + )); + logDirect(returnComponent); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .addCommands(this.baritone.getCommandManager()) + .filterPrefix(args.getString()) + .stream(); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View all commands or help on specific ones"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/InvertCommand.java b/src/main/java/baritone/utils/command/defaults/InvertCommand.java new file mode 100644 index 00000000..f52e6226 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/InvertCommand.java @@ -0,0 +1,75 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class InvertCommand extends Command { + + public InvertCommand(IBaritone baritone) { + super(baritone, "invert"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); + Goal goal; + if ((goal = customGoalProcess.getGoal()) == null) { + throw new CommandInvalidStateException("No goal"); + } + if (goal instanceof GoalInverted) { + goal = ((GoalInverted) goal).origin; + } else { + goal = new GoalInverted(goal); + } + customGoalProcess.setGoalAndPath(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Run away from the current goal"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/MineCommand.java b/src/main/java/baritone/utils/command/defaults/MineCommand.java new file mode 100644 index 00000000..e77c3bd7 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/MineCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.BlockById; +import baritone.api.utils.command.datatypes.ForBlockOptionalMeta; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.cache.WorldScanner; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class MineCommand extends Command { + + public MineCommand(IBaritone baritone) { + super(baritone, "mine"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + int quantity = args.getAsOrDefault(Integer.class, 0); + args.requireMin(1); + List boms = new ArrayList<>(); + while (args.hasAny()) { + boms.add(args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE)); + } + WorldScanner.INSTANCE.repack(ctx); + logDirect(String.format("Mining %s", boms.toString())); + baritone.getMineProcess().mine(quantity, boms.toArray(new BlockOptionalMeta[0])); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return args.tabCompleteDatatype(BlockById.INSTANCE); + } + + @Override + public String getShortDesc() { + return "Mine some blocks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The mine command allows you to tell Baritone to search for and mine individual blocks.", + "", + "The specified blocks can be ores (which are commonly cached), or any other block.", + "", + "Also see the legitMine settings (see #set l legitMine).", + "", + "Usage:", + "> mine diamond_ore - Mines all diamonds it can find.", + "> mine redstone_ore lit_redstone_ore - Mines redstone ore.", + "> mine log:0 - Mines only oak logs." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/PathCommand.java b/src/main/java/baritone/utils/command/defaults/PathCommand.java new file mode 100644 index 00000000..4cd766ea --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/PathCommand.java @@ -0,0 +1,94 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.cache.WorldScanner; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class PathCommand extends Command { + + public PathCommand(IBaritone baritone) { + super(baritone, Arrays.asList("path", "goto")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); + Goal goal; + if (args.hasAny()) { + args.requireMax(3); + goal = args.getDatatypePost(RelativeGoal.INSTANCE, ctx.playerFeet()); + } else if ((goal = customGoalProcess.getGoal()) == null) { + throw new CommandInvalidStateException("No goal"); + } + args.requireMax(0); + WorldScanner.INSTANCE.repack(ctx); + customGoalProcess.setGoalAndPath(goal); + logDirect("Now pathing"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasAny() && !args.has(4)) { + while (args.has(2)) { + if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { + break; + } + args.get(); + if (!args.has(2)) { + return new TabCompleteHelper() + .append("~") + .filterPrefix(args.getString()) + .stream(); + } + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Start heading towards a goal"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/PauseResumeCommands.java b/src/main/java/baritone/utils/command/defaults/PauseResumeCommands.java new file mode 100644 index 00000000..bc3c392a --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/PauseResumeCommands.java @@ -0,0 +1,172 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +/** + * 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 { + + Command pauseCommand; + Command resumeCommand; + Command pausedCommand; + + public PauseResumeCommands(IBaritone baritone) { + // array for mutability, non-field so reflection can't touch it + final boolean[] paused = {false}; + baritone.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(baritone, "pause") { + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + if (paused[0]) { + throw new CommandInvalidStateException("Already paused"); + } + paused[0] = true; + logDirect("Paused"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Pauses Baritone until you use resume"; + } + + @Override + public List getLongDesc() { + return Arrays.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(baritone, "resume") { + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + if (!paused[0]) { + throw new CommandInvalidStateException("Not paused"); + } + paused[0] = false; + logDirect("Resumed"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Resumes Baritone after a pause"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The resume command tells Baritone to resume whatever it was doing when you last used pause.", + "", + "Usage:", + "> resume" + ); + } + }; + pausedCommand = new Command(baritone, "paused") { + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + logDirect(String.format("Baritone is %spaused", paused[0] ? "" : "not ")); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Tells you if Baritone is paused"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The paused command tells you if Baritone is currently paused by use of the pause command.", + "", + "Usage:", + "> paused" + ); + } + }; + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ProcCommand.java b/src/main/java/baritone/utils/command/defaults/ProcCommand.java new file mode 100644 index 00000000..40d8e72b --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ProcCommand.java @@ -0,0 +1,85 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +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.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ProcCommand extends Command { + + public ProcCommand(IBaritone baritone) { + super(baritone, "proc"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + IPathingControlManager pathingControlManager = baritone.getPathingControlManager(); + IBaritoneProcess process = pathingControlManager.mostRecentInControl().orElse(null); + if (process == null) { + throw new CommandInvalidStateException("No process in control"); + } + logDirect(String.format( + "Class: %s\n" + + "Priority: %f\n" + + "Temporary: %b\n" + + "Display name: %s\n" + + "Last command: %s", + process.getClass().getTypeName(), + process.priority(), + process.isTemporary(), + process.displayName(), + pathingControlManager + .mostRecentCommand() + .map(PathingCommand::toString) + .orElse("None") + )); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View process state information"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/ReloadAllCommand.java b/src/main/java/baritone/utils/command/defaults/ReloadAllCommand.java new file mode 100644 index 00000000..c16000f7 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ReloadAllCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ReloadAllCommand extends Command { + + public ReloadAllCommand(IBaritone baritone) { + super(baritone, "reloadall"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + ctx.worldData().getCachedWorld().reloadAllFromDisk(); + logDirect("Reloaded"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Reloads Baritone's cache for this world"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The reloadall command reloads Baritone's world cache.", + "", + "Usage:", + "> reloadall" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/RenderCommand.java b/src/main/java/baritone/utils/command/defaults/RenderCommand.java new file mode 100644 index 00000000..7c100c8e --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/RenderCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class RenderCommand extends Command { + + public RenderCommand(IBaritone baritone) { + super(baritone, "render"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + 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) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Fix glitched chunks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The render command fixes glitched chunk rendering without having to reload all of them.", + "", + "Usage:", + "> render" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/RepackCommand.java b/src/main/java/baritone/utils/command/defaults/RepackCommand.java new file mode 100644 index 00000000..bd918f09 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/RepackCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.cache.WorldScanner; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class RepackCommand extends Command { + + public RepackCommand(IBaritone baritone) { + super(baritone, Arrays.asList("repack", "rescan")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + logDirect(String.format("Queued %d chunks for repacking", WorldScanner.INSTANCE.repack(ctx))); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Re-cache chunks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Repack chunks around you. This basically re-caches them.", + "", + "Usage:", + "> repack - Repack chunks." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SaveAllCommand.java b/src/main/java/baritone/utils/command/defaults/SaveAllCommand.java new file mode 100644 index 00000000..8343f753 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/SaveAllCommand.java @@ -0,0 +1,61 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class SaveAllCommand extends Command { + + public SaveAllCommand(IBaritone baritone) { + super(baritone, "saveall"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + ctx.worldData().getCachedWorld().save(); + logDirect("Saved"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Saves Baritone's cache for this world"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The saveall command saves Baritone's world cache.", + "", + "Usage:", + "> saveall" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SchematicaCommand.java b/src/main/java/baritone/utils/command/defaults/SchematicaCommand.java new file mode 100644 index 00000000..91e31daa --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/SchematicaCommand.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class SchematicaCommand extends Command { + + public SchematicaCommand(IBaritone baritone) { + super(baritone, "schematica"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getBuilderProcess().buildOpenSchematic(); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Builds the loaded schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Builds the schematica currently open in Schematica.", + "", + "Usage:", + "> schematica" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SelCommand.java b/src/main/java/baritone/utils/command/defaults/SelCommand.java new file mode 100644 index 00000000..d9899f42 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/SelCommand.java @@ -0,0 +1,337 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.Baritone; +import baritone.api.IBaritone; +import baritone.api.event.events.RenderEvent; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.schematic.*; +import baritone.api.selection.ISelection; +import baritone.api.selection.ISelectionManager; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.BlockOptionalMetaLookup; +import baritone.api.utils.ISchematic; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.ForBlockOptionalMeta; +import baritone.api.utils.command.datatypes.ForEnumFacing; +import baritone.api.utils.command.datatypes.RelativeBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.utils.IRenderer; +import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +import java.awt.*; +import java.util.List; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; + +public class SelCommand extends Command { + + private ISelectionManager manager = baritone.getSelectionManager(); + private BetterBlockPos pos1 = null; + + public SelCommand(IBaritone baritone) { + super(baritone, Arrays.asList("sel", "selection", "s")); + baritone.getGameEventHandler().registerEventListener(new AbstractGameEventListener() { + @Override + public void onRenderPass(RenderEvent event) { + if (!Baritone.settings().renderSelectionCorners.value || pos1 == null) { + return; + } + Color color = Baritone.settings().colorSelectionPos1.value; + float opacity = Baritone.settings().selectionOpacity.value; + float lineWidth = Baritone.settings().selectionLineWidth.value; + boolean ignoreDepth = Baritone.settings().renderSelectionIgnoreDepth.value; + IRenderer.startLines(color, opacity, lineWidth, ignoreDepth); + IRenderer.drawAABB(new AxisAlignedBB(pos1, pos1.add(1, 1, 1))); + IRenderer.endLines(ignoreDepth); + } + }); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + Action action = Action.getByName(args.getString()); + if (action == null) { + throw new CommandInvalidTypeException(args.consumed(), "an action"); + } + if (action == Action.POS1 || action == Action.POS2) { + if (action == Action.POS2 && pos1 == null) { + throw new CommandInvalidStateException("Set pos1 first before using pos2"); + } + BetterBlockPos playerPos = mc.getRenderViewEntity() != null ? BetterBlockPos.from(new BlockPos(mc.getRenderViewEntity())) : ctx.playerFeet(); + BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; + args.requireMax(0); + if (action == Action.POS1) { + pos1 = pos; + logDirect("Position 1 has been set"); + } else { + manager.addSelection(pos1, pos); + pos1 = null; + logDirect("Selection added"); + } + } else if (action == Action.CLEAR) { + args.requireMax(0); + pos1 = null; + logDirect(String.format("Removed %d selections", manager.removeAllSelections().length)); + } else if (action == Action.UNDO) { + args.requireMax(0); + if (pos1 != null) { + pos1 = null; + logDirect("Undid pos1"); + } else { + ISelection[] selections = manager.getSelections(); + if (selections.length < 1) { + throw new CommandInvalidStateException("Nothing to undo!"); + } else { + pos1 = manager.removeSelection(selections[selections.length - 1]).pos1(); + logDirect("Undid pos2"); + } + } + } else if (action == Action.SET || action == Action.WALLS || action == Action.SHELL || action == Action.CLEARAREA || action == Action.REPLACE) { + BlockOptionalMeta type = action == Action.CLEARAREA + ? new BlockOptionalMeta(Blocks.AIR) + : args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); + BlockOptionalMetaLookup replaces = null; + if (action == Action.REPLACE) { + args.requireMin(1); + List replacesList = new ArrayList<>(); + replacesList.add(type); + while (args.has(2)) { + replacesList.add(args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE)); + } + type = args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); + replaces = new BlockOptionalMetaLookup(replacesList.toArray(new BlockOptionalMeta[0])); + } else { + args.requireMax(0); + } + ISelection[] selections = manager.getSelections(); + if (selections.length == 0) { + throw new CommandInvalidStateException("No selections"); + } + BetterBlockPos origin = selections[0].min(); + CompositeSchematic composite = new CompositeSchematic(0, 0, 0); + for (ISelection selection : selections) { + BetterBlockPos min = selection.min(); + origin = new BetterBlockPos( + Math.min(origin.x, min.x), + Math.min(origin.y, min.y), + Math.min(origin.z, min.z) + ); + } + for (ISelection selection : selections) { + Vec3i size = selection.size(); + BetterBlockPos min = selection.min(); + ISchematic schematic = new FillSchematic(size.getX(), size.getY(), size.getZ(), type); + if (action == Action.WALLS) { + schematic = new WallsSchematic(schematic); + } else if (action == Action.SHELL) { + schematic = new ShellSchematic(schematic); + } else if (action == Action.REPLACE) { + schematic = new ReplaceSchematic(schematic, replaces); + } + composite.put(schematic, min.x - origin.x, min.y - origin.y, min.z - origin.z); + } + baritone.getBuilderProcess().build("Fill", composite, origin); + logDirect("Filling now"); + } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { + args.requireExactly(3); + TransformTarget transformTarget = TransformTarget.getByName(args.getString()); + if (transformTarget == null) { + throw new CommandInvalidStateException("Invalid transform type"); + } + EnumFacing direction = args.getDatatypeFor(ForEnumFacing.INSTANCE); + int blocks = args.getAs(Integer.class); + ISelection[] selections = manager.getSelections(); + if (selections.length < 1) { + throw new CommandInvalidStateException("No selections found"); + } + selections = transformTarget.transform(selections); + for (ISelection selection : selections) { + if (action == Action.EXPAND) { + manager.expand(selection, direction, blocks); + } else if (action == Action.CONTRACT) { + manager.contract(selection, direction, blocks); + } else { + manager.shift(selection, direction, blocks); + } + } + logDirect(String.format("Transformed %d selections", selections.length)); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(Action.getAllNames()) + .filterPrefix(args.getString()) + .sortAlphabetically() + .stream(); + } else { + Action action = Action.getByName(args.getString()); + if (action != null) { + if (action == Action.POS1 || action == Action.POS2) { + if (args.hasAtMost(3)) { + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + } else if (action == Action.SET || action == Action.WALLS || action == Action.CLEARAREA || action == Action.REPLACE) { + if (args.hasExactlyOne() || action == Action.REPLACE) { + while (args.has(2)) { + args.get(); + } + return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); + } + } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(TransformTarget.getAllNames()) + .filterPrefix(args.getString()) + .sortAlphabetically() + .stream(); + } else { + TransformTarget target = TransformTarget.getByName(args.getString()); + if (target != null && args.hasExactlyOne()) { + return args.tabCompleteDatatype(ForEnumFacing.INSTANCE); + } + } + } + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "WorldEdit-like commands"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The sel command allows you to manipulate Baritone's selections, similarly to WorldEdit.", + "", + "Using these selections, you can clear areas, fill them with blocks, or something else.", + "", + "The expand/contract/shift commands use a kind of selector to choose which selections to target. Supported ones are a/all, n/newest, and o/oldest.", + "", + "Usage:", + "> sel pos1/p1/1 - Set position 1 to your current position.", + "> sel pos1/p1/1 - Set position 1 to a relative position.", + "> sel pos2/p2/2 - Set position 2 to your current position.", + "> sel pos2/p2/2 - Set position 2 to a relative position.", + "", + "> sel clear/c - Clear the selection.", + "> sel undo/u - Undo the last action (setting positions, creating selections, etc.)", + "> sel set/fill/s/f [block] - Completely fill all selections with a block.", + "> sel walls/w [block] - Fill in the walls of the selection with a specified block.", + "> sel shell/shl [block] - The same as walls, but fills in a ceiling and floor too.", + "> sel cleararea/ca - Basically 'set air'.", + "> sel replace/r - Replaces blocks with another block.", + "", + "> sel expand - Expand the targets.", + "> sel contract - Contract the targets.", + "> sel shift - Shift the targets (does not resize)." + ); + } + + enum Action { + POS1("pos1", "p1", "1"), + POS2("pos2", "p2", "2"), + CLEAR("clear", "c"), + UNDO("undo", "u"), + SET("set", "fill", "s", "f"), + WALLS("walls", "w"), + SHELL("shell", "shl"), + CLEARAREA("cleararea", "ca"), + REPLACE("replace", "r"), + EXPAND("expand", "ex"), + CONTRACT("contract", "ct"), + SHIFT("shift", "sh"); + private final String[] names; + + Action(String... names) { + this.names = names; + } + + public static Action getByName(String name) { + for (Action action : Action.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + return null; + } + + public static String[] getAllNames() { + Set names = new HashSet<>(); + for (Action action : Action.values()) { + names.addAll(Arrays.asList(action.names)); + } + return names.toArray(new String[0]); + } + } + + enum TransformTarget { + ALL(sels -> sels, "all", "a"), + NEWEST(sels -> new ISelection[]{sels[sels.length - 1]}, "newest", "n"), + OLDEST(sels -> new ISelection[]{sels[0]}, "oldest", "o"); + private final Function transform; + private final String[] names; + + TransformTarget(Function transform, String... names) { + this.transform = transform; + this.names = names; + } + + public ISelection[] transform(ISelection[] selections) { + return transform.apply(selections); + } + + public static TransformTarget getByName(String name) { + for (TransformTarget target : TransformTarget.values()) { + for (String alias : target.names) { + if (alias.equalsIgnoreCase(name)) { + return target; + } + } + } + return null; + } + + public static String[] getAllNames() { + Set names = new HashSet<>(); + for (TransformTarget target : TransformTarget.values()) { + names.addAll(Arrays.asList(target.names)); + } + return names.toArray(new String[0]); + } + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SetCommand.java b/src/main/java/baritone/utils/command/defaults/SetCommand.java new file mode 100644 index 00000000..48c4580f --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/SetCommand.java @@ -0,0 +1,253 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.Baritone; +import baritone.api.IBaritone; +import baritone.api.Settings; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +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.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.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static baritone.api.utils.SettingsUtil.settingTypeToString; +import static baritone.api.utils.SettingsUtil.settingValueToString; +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class SetCommand extends Command { + + public SetCommand(IBaritone baritone) { + super(baritone, Arrays.asList("set", "setting", "settings")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + String arg = args.hasAny() ? args.getString().toLowerCase(Locale.US) : "list"; + if (Arrays.asList("s", "save").contains(arg)) { + SettingsUtil.save(Baritone.settings()); + logDirect("Settings saved"); + return; + } + boolean viewModified = Arrays.asList("m", "mod", "modified").contains(arg); + boolean viewAll = Arrays.asList("all", "l", "list").contains(arg); + boolean paginate = viewModified || viewAll; + if (paginate) { + String search = args.hasAny() && args.peekAsOrNull(Integer.class) == null ? args.getString() : ""; + args.requireMax(1); + List toPaginate = + (viewModified ? SettingsUtil.modifiedSettings(Baritone.settings()) : Baritone.settings().allSettings).stream() + .filter(s -> !s.getName().equals("logger")) + .filter(s -> s.getName().toLowerCase(Locale.US).contains(search.toLowerCase(Locale.US))) + .sorted((s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(s1.getName(), s2.getName())) + .collect(Collectors.toList()); + Paginator.paginate( + args, + new Paginator<>(toPaginate), + () -> logDirect( + !search.isEmpty() + ? String.format("All %ssettings containing the string '%s':", viewModified ? "modified " : "", search) + : String.format("All %ssettings:", viewModified ? "modified " : "") + ), + setting -> { + ITextComponent typeComponent = new TextComponentString(String.format( + " (%s)", + settingTypeToString(setting) + )); + typeComponent.getStyle().setColor(TextFormatting.DARK_GRAY); + ITextComponent hoverComponent = new TextComponentString(""); + hoverComponent.getStyle().setColor(TextFormatting.GRAY); + hoverComponent.appendText(setting.getName()); + hoverComponent.appendText(String.format("\nType: %s", settingTypeToString(setting))); + hoverComponent.appendText(String.format("\n\nValue:\n%s", settingValueToString(setting))); + String commandSuggestion = Baritone.settings().prefix.value + String.format("set %s ", setting.getName()); + ITextComponent component = new TextComponentString(setting.getName()); + component.getStyle().setColor(TextFormatting.GRAY); + component.appendSibling(typeComponent); + component.getStyle() + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent)) + .setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, commandSuggestion)); + return component; + }, + FORCE_COMMAND_PREFIX + "set " + arg + " " + search + ); + return; + } + args.requireMax(1); + boolean resetting = arg.equalsIgnoreCase("reset"); + boolean toggling = arg.equalsIgnoreCase("toggle"); + boolean doingSomething = resetting || toggling; + if (resetting) { + if (!args.hasAny()) { + 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.peekString().equalsIgnoreCase("all")) { + SettingsUtil.modifiedSettings(Baritone.settings()).forEach(Settings.Setting::reset); + logDirect("All settings have been reset to their default values"); + SettingsUtil.save(Baritone.settings()); + return; + } + } + if (toggling) { + args.requireMin(1); + } + String settingName = doingSomething ? args.getString() : arg; + Settings.Setting setting = Baritone.settings().allSettings.stream() + .filter(s -> s.getName().equalsIgnoreCase(settingName)) + .findFirst() + .orElse(null); + if (setting == null) { + throw new CommandInvalidTypeException(args.consumed(), "a valid setting"); + } + if (!doingSomething && !args.hasAny()) { + 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.getString(); + try { + SettingsUtil.parseAndApply(Baritone.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) + )); + } + ITextComponent oldValueComponent = new TextComponentString(String.format("Old value: %s", oldValue)); + oldValueComponent.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) + )); + logDirect(oldValueComponent); + if ((setting.getName().equals("chatControl") && !(Boolean) setting.value && !Baritone.settings().chatControlAnyway.value) || + setting.getName().equals("chatControlAnyway") && !(Boolean) setting.value && !Baritone.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); + } + } + SettingsUtil.save(Baritone.settings()); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasAny()) { + String arg = args.getString(); + if (args.hasExactlyOne() && !Arrays.asList("s", "save").contains(args.peekString().toLowerCase(Locale.US))) { + if (arg.equalsIgnoreCase("reset")) { + return new TabCompleteHelper() + .addModifiedSettings() + .prepend("all") + .filterPrefix(args.getString()) + .stream(); + } else if (arg.equalsIgnoreCase("toggle")) { + return new TabCompleteHelper() + .addToggleableSettings() + .filterPrefix(args.getString()) + .stream(); + } + Settings.Setting setting = Baritone.settings().byLowerName.get(arg.toLowerCase(Locale.US)); + if (setting != null) { + if (setting.getType() == Boolean.class) { + TabCompleteHelper helper = new TabCompleteHelper(); + if ((Boolean) setting.value) { + helper.append("true", "false"); + } else { + helper.append("false", "true"); + } + return helper.filterPrefix(args.getString()).stream(); + } else { + return Stream.of(settingValueToString(setting)); + } + } + } else if (!args.hasAny()) { + return new TabCompleteHelper() + .addSettings() + .sortAlphabetically() + .prepend("list", "modified", "reset", "toggle", "save") + .filterPrefix(arg) + .stream(); + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View or change settings"; + } + + @Override + public List getLongDesc() { + return Arrays.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", + "> set save - Save all settings (this is automatic tho)" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ThisWayCommand.java b/src/main/java/baritone/utils/command/defaults/ThisWayCommand.java new file mode 100644 index 00000000..4c6f3522 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/ThisWayCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ThisWayCommand extends Command { + + public ThisWayCommand(IBaritone baritone) { + super(baritone, Arrays.asList("thisway", "forward")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireExactly(1); + GoalXZ goal = GoalXZ.fromDirection( + ctx.playerFeetAsVec(), + ctx.player().rotationYawHead, + args.getAs(Double.class) + ); + baritone.getCustomGoalProcess().setGoal(goal); + logDirect(String.format("Goal: %s", goal)); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Travel in your current direction"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Creates a GoalXZ some amount of blocks in the direction you're currently looking", + "", + "Usage:", + "> thisway - makes a GoalXZ distance blocks in front of you" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/TunnelCommand.java b/src/main/java/baritone/utils/command/defaults/TunnelCommand.java new file mode 100644 index 00000000..588287d6 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/TunnelCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalStrictDirection; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class TunnelCommand extends Command { + + public TunnelCommand(IBaritone baritone) { + super(baritone, "tunnel"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + Goal goal = new GoalStrictDirection( + ctx.playerFeet(), + ctx.player().getHorizontalFacing() + ); + baritone.getCustomGoalProcess().setGoalAndPath(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Set a goal to tunnel in your current direction"; + } + + @Override + public List getLongDesc() { + return Arrays.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/main/java/baritone/utils/command/defaults/VersionCommand.java b/src/main/java/baritone/utils/command/defaults/VersionCommand.java new file mode 100644 index 00000000..fe53f523 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/VersionCommand.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.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class VersionCommand extends Command { + + public VersionCommand(IBaritone baritone) { + super(baritone, "version"); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + args.requireMax(0); + String version = getClass().getPackage().getImplementationVersion(); + if (version == null) { + 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) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View the Baritone version"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The version command prints the version of Baritone you're currently running.", + "", + "Usage:", + "> version - View version information, if present" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/WaypointsCommand.java b/src/main/java/baritone/utils/command/defaults/WaypointsCommand.java new file mode 100644 index 00000000..01f5817e --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/WaypointsCommand.java @@ -0,0 +1,331 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.cache.IWaypoint; +import baritone.api.cache.Waypoint; +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.ForWaypoints; +import baritone.api.utils.command.datatypes.RelativeBlockPos; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidStateException; +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.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.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Stream; + +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class WaypointsCommand extends Command { + + public WaypointsCommand(IBaritone baritone) { + super(baritone, Arrays.asList("waypoints", "waypoint", "wp")); + } + + @Override + protected void executed(String label, ArgConsumer args) throws CommandException { + Action action = args.hasAny() ? Action.getByName(args.getString()) : Action.LIST; + if (action == null) { + throw new CommandInvalidTypeException(args.consumed(), "an action"); + } + BiFunction toComponent = (waypoint, _action) -> { + ITextComponent component = new TextComponentString(""); + ITextComponent tagComponent = new TextComponentString(waypoint.getTag().name() + " "); + tagComponent.getStyle().setColor(TextFormatting.GRAY); + String name = waypoint.getName(); + ITextComponent nameComponent = new TextComponentString(!name.isEmpty() ? name : ""); + nameComponent.getStyle().setColor(!name.isEmpty() ? TextFormatting.GRAY : TextFormatting.DARK_GRAY); + ITextComponent timestamp = new TextComponentString(" @ " + new Date(waypoint.getCreationTimestamp())); + timestamp.getStyle().setColor(TextFormatting.DARK_GRAY); + component.appendSibling(tagComponent); + component.appendSibling(nameComponent); + component.appendSibling(timestamp); + component.getStyle() + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to select") + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s %s %s @ %d", + FORCE_COMMAND_PREFIX, + label, + _action.names[0], + waypoint.getTag().getName(), + waypoint.getCreationTimestamp() + )) + ); + return component; + }; + Function transform = waypoint -> + toComponent.apply(waypoint, action == Action.LIST ? Action.INFO : action); + if (action == Action.LIST) { + IWaypoint.Tag tag = args.hasAny() ? IWaypoint.Tag.getByName(args.peekString()) : null; + if (tag != null) { + args.get(); + } + IWaypoint[] waypoints = tag != null + ? ForWaypoints.getWaypointsByTag(this.baritone, tag) + : ForWaypoints.getWaypoints(this.baritone); + if (waypoints.length > 0) { + args.requireMax(1); + Paginator.paginate( + args, + waypoints, + () -> logDirect( + tag != null + ? String.format("All waypoints by tag %s:", tag.name()) + : "All waypoints:" + ), + transform, + String.format( + "%s%s %s%s", + FORCE_COMMAND_PREFIX, + label, + action.names[0], + tag != null ? " " + tag.getName() : "" + ) + ); + } else { + args.requireMax(0); + throw new CommandInvalidStateException( + tag != null + ? "No waypoints found by that tag" + : "No waypoints found" + ); + } + } else if (action == Action.SAVE) { + IWaypoint.Tag tag = IWaypoint.Tag.getByName(args.getString()); + if (tag == null) { + throw new CommandInvalidStateException(String.format("'%s' is not a tag ", args.consumedString())); + } + String name = args.hasAny() ? args.getString() : ""; + BetterBlockPos pos = args.hasAny() + ? args.getDatatypePost(RelativeBlockPos.INSTANCE, ctx.playerFeet()) + : ctx.playerFeet(); + args.requireMax(0); + IWaypoint waypoint = new Waypoint(name, tag, pos); + ForWaypoints.waypoints(this.baritone).addWaypoint(waypoint); + ITextComponent component = new TextComponentString("Waypoint added: "); + component.getStyle().setColor(TextFormatting.GRAY); + component.appendSibling(toComponent.apply(waypoint, Action.INFO)); + logDirect(component); + } else if (action == Action.CLEAR) { + args.requireMax(1); + IWaypoint.Tag tag = IWaypoint.Tag.getByName(args.getString()); + IWaypoint[] waypoints = ForWaypoints.getWaypointsByTag(this.baritone, tag); + for (IWaypoint waypoint : waypoints) { + ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); + } + logDirect(String.format("Cleared %d waypoints", waypoints.length)); + } else { + IWaypoint[] waypoints = args.getDatatypeFor(ForWaypoints.INSTANCE); + IWaypoint waypoint = null; + if (args.hasAny() && args.peekString().equals("@")) { + args.requireExactly(2); + args.get(); + long timestamp = args.getAs(Long.class); + for (IWaypoint iWaypoint : waypoints) { + if (iWaypoint.getCreationTimestamp() == timestamp) { + waypoint = iWaypoint; + break; + } + } + if (waypoint == null) { + throw new CommandInvalidStateException("Timestamp was specified but no waypoint was found"); + } + } else { + switch (waypoints.length) { + case 0: + throw new CommandInvalidStateException("No waypoints found"); + case 1: + waypoint = waypoints[0]; + break; + default: + break; + } + } + if (waypoint == null) { + args.requireMax(1); + Paginator.paginate( + args, + waypoints, + () -> logDirect("Multiple waypoints were found:"), + transform, + String.format( + "%s%s %s %s", + FORCE_COMMAND_PREFIX, + label, + action.names[0], + args.consumedString() + ) + ); + } else { + if (action == Action.INFO) { + logDirect(transform.apply(waypoint)); + logDirect(String.format("Position: %s", waypoint.getLocation())); + ITextComponent deleteComponent = new TextComponentString("Click to delete this waypoint"); + deleteComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s delete %s @ %d", + FORCE_COMMAND_PREFIX, + label, + waypoint.getTag().getName(), + waypoint.getCreationTimestamp() + ) + )); + ITextComponent goalComponent = new TextComponentString("Click to set goal to this waypoint"); + goalComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s goal %s @ %d", + FORCE_COMMAND_PREFIX, + label, + waypoint.getTag().getName(), + waypoint.getCreationTimestamp() + ) + )); + ITextComponent backComponent = new TextComponentString("Click to return to the waypoints list"); + backComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s list", + FORCE_COMMAND_PREFIX, + label + ) + )); + logDirect(deleteComponent); + logDirect(goalComponent); + logDirect(backComponent); + } else if (action == Action.DELETE) { + ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); + logDirect("That waypoint has successfully been deleted"); + } else if (action == Action.GOAL) { + Goal goal = new GoalBlock(waypoint.getLocation()); + baritone.getCustomGoalProcess().setGoal(goal); + logDirect(String.format("Goal: %s", goal)); + } + } + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args) throws CommandException { + if (args.hasAny()) { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(Action.getAllNames()) + .sortAlphabetically() + .filterPrefix(args.getString()) + .stream(); + } else { + Action action = Action.getByName(args.getString()); + if (args.hasExactlyOne()) { + if (action == Action.LIST || action == Action.SAVE || action == Action.CLEAR) { + return new TabCompleteHelper() + .append(IWaypoint.Tag.getAllNames()) + .sortAlphabetically() + .filterPrefix(args.getString()) + .stream(); + } else { + return args.tabCompleteDatatype(ForWaypoints.INSTANCE); + } + } else if (args.has(3) && action == Action.SAVE) { + args.get(); + args.get(); + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Manage waypoints"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The waypoint command allows you to manage Baritone's waypoints.", + "", + "Waypoints can be used to mark positions for later. Waypoints are each given a tag and an optional name.", + "", + "Note that the info, delete, and goal commands let you specify a waypoint by tag. If there is more than one waypoint with a certain tag, then they will let you select which waypoint you mean.", + "", + "Usage:", + "> wp [l/list] - List all waypoints.", + "> wp - Save your current position as an unnamed waypoint with the specified tag.", + "> wp - Save the waypoint with the specified name.", + "> wp - Save the waypoint with the specified name and position.", + "> wp - Show info on a waypoint by tag.", + "> wp - Delete a waypoint by tag.", + "> wp - Set a goal to a waypoint by tag." + ); + } + + private enum Action { + LIST("list", "get", "l"), + CLEAR("clear", "c"), + SAVE("save", "s"), + INFO("info", "show", "i"), + DELETE("delete", "d"), + GOAL("goal", "goto", "g"); + private final String[] names; + + Action(String... names) { + this.names = names; + } + + public static Action getByName(String name) { + for (Action action : Action.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + return null; + } + + public static String[] getAllNames() { + Set names = new HashSet<>(); + for (Action action : Action.values()) { + names.addAll(Arrays.asList(action.names)); + } + return names.toArray(new String[0]); + } + } +} diff --git a/src/main/java/baritone/utils/command/execution/CommandExecution.java b/src/main/java/baritone/utils/command/execution/CommandExecution.java new file mode 100644 index 00000000..1e8fe838 --- /dev/null +++ b/src/main/java/baritone/utils/command/execution/CommandExecution.java @@ -0,0 +1,85 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.execution; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandUnhandledException; +import baritone.api.utils.command.exception.ICommandException; +import baritone.api.utils.command.execution.ICommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.utils.command.manager.CommandManager; + +import java.util.stream.Stream; + +/** + * The default, internal implementation of {@link ICommandExecution}, which is used by {@link CommandManager} + * + * @author LoganDark, Brady + */ +public class CommandExecution implements ICommandExecution { + + /** + * The command itself + */ + private final Command command; + + /** + * The name this command was called with + */ + private final String label; + + /** + * The arg consumer + */ + private final ArgConsumer args; + + public CommandExecution(Command command, String label, ArgConsumer args) { + this.command = command; + this.label = label; + this.args = args; + } + + @Override + public String getLabel() { + return this.label; + } + + @Override + public ArgConsumer getArguments() { + return this.args; + } + + @Override + public void execute() { + try { + command.execute(this); + } catch (Throwable t) { + // Create a handleable exception, wrap if needed + ICommandException exception = t instanceof ICommandException + ? (ICommandException) t + : new CommandUnhandledException(t); + + exception.handle(command, args.args); + } + } + + @Override + public Stream tabComplete() { + return command.tabComplete(this); + } +} diff --git a/src/main/java/baritone/utils/command/manager/CommandManager.java b/src/main/java/baritone/utils/command/manager/CommandManager.java new file mode 100644 index 00000000..e5c5d035 --- /dev/null +++ b/src/main/java/baritone/utils/command/manager/CommandManager.java @@ -0,0 +1,115 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.manager; + +import baritone.Baritone; +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.execution.ICommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.manager.ICommandManager; +import baritone.api.utils.command.registry.Registry; +import baritone.utils.command.defaults.DefaultCommands; +import baritone.utils.command.execution.CommandExecution; +import net.minecraft.util.Tuple; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +/** + * The default, internal implementation of {@link ICommandManager} + * + * @author Brady + * @since 9/21/2019 + */ +public class CommandManager implements ICommandManager { + + private final Registry registry = new Registry<>(); + private final Baritone baritone; + + public CommandManager(Baritone baritone) { + this.baritone = baritone; + DefaultCommands.createAll(baritone).forEach(this.registry::register); + } + + @Override + public IBaritone getBaritone() { + return this.baritone; + } + + @Override + public Registry getRegistry() { + return this.registry; + } + + @Override + public Command getCommand(String name) { + for (Command command : this.registry.entries) { + if (command.names.contains(name.toLowerCase(Locale.US))) { + return command; + } + } + return null; + } + + @Override + public boolean execute(String string) { + return this.execute(ICommandExecution.expand(string)); + } + + @Override + public boolean execute(Tuple> expanded) { + ICommandExecution execution = this.from(expanded); + if (execution != null) { + execution.execute(); + } + return execution != null; + } + + @Override + public Stream tabComplete(Tuple> expanded) { + ICommandExecution execution = this.from(expanded); + return execution == null ? Stream.empty() : execution.tabComplete(); + } + + @Override + public Stream tabComplete(String prefix) { + Tuple> pair = ICommandExecution.expand(prefix, true); + String label = pair.getFirst(); + List args = pair.getSecond(); + if (args.isEmpty()) { + return new TabCompleteHelper() + .addCommands(this.baritone.getCommandManager()) + .filterPrefix(label) + .stream(); + } else { + return tabComplete(pair); + } + } + + private ICommandExecution from(Tuple> expanded) { + String label = expanded.getFirst(); + ArgConsumer args = new ArgConsumer(this, expanded.getSecond()); + + Command command = this.getCommand(label); + return command == null ? null : new CommandExecution(command, label, args); + } +} diff --git a/src/main/java/baritone/utils/pathing/Avoidance.java b/src/main/java/baritone/utils/pathing/Avoidance.java index 11b4a73f..ca377fb0 100644 --- a/src/main/java/baritone/utils/pathing/Avoidance.java +++ b/src/main/java/baritone/utils/pathing/Avoidance.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.List; public class Avoidance { + private final int centerX; private final int centerY; private final int centerZ; diff --git a/src/main/java/baritone/utils/pathing/Favoring.java b/src/main/java/baritone/utils/pathing/Favoring.java index 56ccb006..bbc23c7f 100644 --- a/src/main/java/baritone/utils/pathing/Favoring.java +++ b/src/main/java/baritone/utils/pathing/Favoring.java @@ -25,6 +25,7 @@ import baritone.pathing.movement.CalculationContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; public final class Favoring { + private final Long2DoubleOpenHashMap favorings; public Favoring(IPlayerContext ctx, IPath previous, CalculationContext context) { diff --git a/src/main/java/baritone/utils/pathing/MutableMoveResult.java b/src/main/java/baritone/utils/pathing/MutableMoveResult.java index b6e1ba8a..17fc6ee2 100644 --- a/src/main/java/baritone/utils/pathing/MutableMoveResult.java +++ b/src/main/java/baritone/utils/pathing/MutableMoveResult.java @@ -25,6 +25,7 @@ import baritone.api.pathing.movement.ActionCosts; * @author leijurv */ public final class MutableMoveResult { + public int x; public int y; public int z; diff --git a/src/main/java/baritone/utils/pathing/PathBase.java b/src/main/java/baritone/utils/pathing/PathBase.java index 8bac2dc7..de10fdf3 100644 --- a/src/main/java/baritone/utils/pathing/PathBase.java +++ b/src/main/java/baritone/utils/pathing/PathBase.java @@ -26,6 +26,7 @@ import baritone.utils.BlockStateInterface; import net.minecraft.util.math.BlockPos; public abstract class PathBase implements IPath { + @Override public PathBase cutoffAtLoadedChunks(Object bsi0) { // <-- cursed cursed cursed if (!Baritone.settings().cutoffAtLoadBoundary.value) { diff --git a/src/main/java/baritone/utils/schematic/AirSchematic.java b/src/main/java/baritone/utils/schematic/FillSchematic.java similarity index 77% rename from src/main/java/baritone/utils/schematic/AirSchematic.java rename to src/main/java/baritone/utils/schematic/FillSchematic.java index fd253105..3216f59f 100644 --- a/src/main/java/baritone/utils/schematic/AirSchematic.java +++ b/src/main/java/baritone/utils/schematic/FillSchematic.java @@ -19,23 +19,26 @@ package baritone.utils.schematic; import baritone.api.utils.ISchematic; import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; -public class AirSchematic implements ISchematic { +import java.util.List; + +public class FillSchematic implements ISchematic { private final int widthX; private final int heightY; private final int lengthZ; + private final IBlockState state; - public AirSchematic(int widthX, int heightY, int lengthZ) { + public FillSchematic(int widthX, int heightY, int lengthZ, IBlockState state) { this.widthX = widthX; this.heightY = heightY; this.lengthZ = lengthZ; + this.state = state; } @Override - public IBlockState desiredState(int x, int y, int z) { - return Blocks.AIR.getDefaultState(); + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return state; } @Override diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java index fa1c2d00..32b3292c 100644 --- a/src/main/java/baritone/utils/schematic/MapArtSchematic.java +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -59,8 +59,8 @@ public class MapArtSchematic extends Schematic { } @Override - public boolean inSchematic(int x, int y, int z) { + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { // in map art, we only care about coordinates in or above the art - return super.inSchematic(x, y, z) && y >= heightMap[x][z]; + return super.inSchematic(x, y, z, currentState) && y >= heightMap[x][z]; } } diff --git a/src/main/java/baritone/utils/schematic/Schematic.java b/src/main/java/baritone/utils/schematic/Schematic.java index 55dfb619..47461e60 100644 --- a/src/main/java/baritone/utils/schematic/Schematic.java +++ b/src/main/java/baritone/utils/schematic/Schematic.java @@ -22,7 +22,10 @@ import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; +import java.util.List; + public class Schematic implements ISchematic { + public final int widthX; public final int heightY; public final int lengthZ; @@ -68,7 +71,7 @@ public class Schematic implements ISchematic { } @Override - public IBlockState desiredState(int x, int y, int z) { + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { return states[x][z][y]; } diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java index fd0ace8c..38279031 100644 --- a/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java @@ -18,12 +18,14 @@ package baritone.utils.schematic.schematica; import baritone.api.utils.ISchematic; - import com.github.lunatrius.schematica.client.world.SchematicWorld; import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; +import java.util.List; + public final class SchematicAdapter implements ISchematic { + private final SchematicWorld schematic; public SchematicAdapter(SchematicWorld schematicWorld) { @@ -31,7 +33,7 @@ public final class SchematicAdapter implements ISchematic { } @Override - public IBlockState desiredState(int x, int y, int z) { + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { return schematic.getSchematic().getBlockState(new BlockPos(x, y, z)); } diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java index 63b85670..bacf1e9d 100644 --- a/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java @@ -39,7 +39,7 @@ public enum SchematicaHelper { public static Optional> getOpenSchematic() { return Optional.ofNullable(ClientProxy.schematic) - .map(world -> new Tuple<>(new SchematicAdapter(world), world.position)); + .map(world -> new Tuple<>(new SchematicAdapter(world), world.position)); } } diff --git a/src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.java b/src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.java index 280b38a6..4c9da008 100644 --- a/src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.java +++ b/src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.java @@ -20,6 +20,7 @@ package com.github.lunatrius.core.util.math; import net.minecraft.util.math.BlockPos; public class MBlockPos extends BlockPos { + MBlockPos() { super(6, 6, 6); } diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java b/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java index 7d786dbd..415399df 100644 --- a/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java +++ b/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java @@ -20,5 +20,6 @@ package com.github.lunatrius.schematica; import com.github.lunatrius.schematica.proxy.CommonProxy; public class Schematica { + public static CommonProxy proxy; } diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java b/src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java index 6430dbfc..bbf11428 100644 --- a/src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java +++ b/src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java @@ -21,6 +21,7 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; public interface ISchematic { + IBlockState getBlockState(BlockPos var1); int getWidth(); diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java b/src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java index 80c1b2a2..25e5c280 100644 --- a/src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java +++ b/src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java @@ -21,7 +21,8 @@ import com.github.lunatrius.core.util.math.MBlockPos; import com.github.lunatrius.schematica.api.ISchematic; public class SchematicWorld { - public final MBlockPos position = (MBlockPos)(Object)"cringe"; + + public final MBlockPos position = (MBlockPos) (Object) "cringe"; public ISchematic getSchematic() { throw new LinkageError("LOL"); diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.java b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.java index 191d0110..952f86a1 100644 --- a/src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.java +++ b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.java @@ -20,5 +20,6 @@ package com.github.lunatrius.schematica.proxy; import com.github.lunatrius.schematica.client.world.SchematicWorld; public class ClientProxy extends CommonProxy { + public static SchematicWorld schematic; } diff --git a/src/test/java/baritone/pathing/movement/ActionCostsTest.java b/src/test/java/baritone/pathing/movement/ActionCostsTest.java index cce61e4b..9bfdafb0 100644 --- a/src/test/java/baritone/pathing/movement/ActionCostsTest.java +++ b/src/test/java/baritone/pathing/movement/ActionCostsTest.java @@ -23,6 +23,7 @@ import static baritone.api.pathing.movement.ActionCosts.*; import static org.junit.Assert.assertEquals; public class ActionCostsTest { + @Test public void testFallNBlocksCost() { assertEquals(FALL_N_BLOCKS_COST.length, 257); // Fall 0 blocks through fall 256 blocks diff --git a/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java b/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java index c30b45d1..d05a323b 100644 --- a/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java +++ b/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java @@ -22,6 +22,7 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; public class PathingBlockTypeTest { + @Test public void testBits() { for (PathingBlockType type : PathingBlockType.values()) {