diff --git a/.gitignore b/.gitignore index 270356275..6aeefb22e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ classes/ # IntelliJ Files .idea/ *.iml +*.ipr +*.iws /logs/ # Copyright Files diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 37c1b703a..474ecf7e6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,10 +14,10 @@ appearance, race, religion, or sexual identity and orientation. Examples of behavior that contributes to creating a positive environment include: -* No Anime +* No Anime (including uwu's or owo's) * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism +* Giving and gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members @@ -29,7 +29,7 @@ Examples of unacceptable behavior by participants include: * ~~Trolling, insulting/derogatory comments, and personal or political attacks~~ * Public or private harassment * Publishing others' private information, such as a physical or electronic - address, without explicit permission + address, without explicit permission or consent * Other conduct which could reasonably be considered inappropriate in a professional setting diff --git a/README.md b/README.md index 6a62c32d5..19ec0e879 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ ![Lines of Code](https://tokei.rs/b1/github/cabaletta/baritone?category=code) [![GitHub contributors](https://img.shields.io/github/contributors/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/graphs/contributors/) [![GitHub commits](https://img.shields.io/github/commits-since/cabaletta/baritone/v1.0.0.svg)](https://github.com/cabaletta/baritone/commit/) -[![Impact integration](https://img.shields.io/badge/Impact%20integration-v1.2.8%20/%20v1.3.4-brightgreen.svg)](https://impactdevelopment.github.io/) +[![Impact integration](https://img.shields.io/badge/Impact%20integration-v1.2.8%20/%20v1.3.4%20/%20v1.4.1-brightgreen.svg)](https://impactclient.net/) [![ForgeHax integration](https://img.shields.io/badge/ForgeHax%20%22integration%22-scuffed-yellow.svg)](https://github.com/fr1kin/ForgeHax/) -[![Aristois add-on integration](https://img.shields.io/badge/Aristois%20add--on%20integration-v1.3.4-green.svg)](https://gitlab.com/emc-mods-indrit/baritone_api) +[![Aristois add-on integration](https://img.shields.io/badge/Aristois%20add--on%20integration-v1.3.4%20/%20v1.4.1-green.svg)](https://gitlab.com/emc-mods-indrit/baritone_api) [![WWE integration](https://img.shields.io/badge/WWE%20%22integration%22-master%3F-green.svg)](https://wweclient.com/) [![Future integration](https://img.shields.io/badge/Future%20integration-Soon™%3F%3F%3F-red.svg)](https://futureclient.net/) [![forthebadge](https://forthebadge.com/images/badges/built-with-swag.svg)](http://forthebadge.com/) @@ -29,9 +29,9 @@ A Minecraft pathfinder bot. -Baritone is the pathfinding system used in [Impact](https://impactdevelopment.github.io/) since 4.4. There's a [showcase video](https://www.youtube.com/watch?v=yI8hgW_m6dQ) made by @Adovin#3153 on Baritone's integration into Impact. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a video I made showing off what it can do. +Baritone is the pathfinding system used in [Impact](https://impactclient.net/) since 4.4. There's a [showcase video](https://www.youtube.com/watch?v=yI8hgW_m6dQ) made by @Adovin#3153 on Baritone's integration into Impact. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a video I made showing off what it can do. -The easiest way to install Baritone is to install [Impact](https://impactdevelopment.github.io/), which comes with Baritone. The second easiest way (for 1.12.2 only) is to install the v1.2.* forge api jar from [releases](https://github.com/cabaletta/baritone/releases). Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. +The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone. The second easiest way (for 1.12.2 only) is to install the v1.2.* forge api jar from [releases](https://github.com/cabaletta/baritone/releases). Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. For 1.14.4, [click here](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1). Or [with optifine](https://github.com/cabaletta/baritone/issues/797). diff --git a/SETUP.md b/SETUP.md index 909da5a8f..1fcaf1d2b 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,6 +1,6 @@ # Installation -The easiest way to install Baritone is to install [Impact](https://impactdevelopment.github.io/), which comes with Baritone. +The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone. For 1.14.4, [click here](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1). @@ -103,4 +103,4 @@ $ gradlew build ![Image](https://i.imgur.com/PE6r9iN.png) -- Double click on **build** to run it \ No newline at end of file +- Double click on **build** to run it diff --git a/USAGE.md b/USAGE.md index 869d68c27..25fef11e8 100644 --- a/USAGE.md +++ b/USAGE.md @@ -24,7 +24,7 @@ Some common examples: - `goal clear` to clear the goal - `cancel` or `stop` to stop everything - `goto portal` or `goto ender_chest` or `goto block_type` to go to a block. (in Impact, `.goto` is an alias for `.b goto` for the most part) -- `mine diamond_ore` to mine diamond ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine diamond_ore 64`. +- `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine diamond_ore 64`. - `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it (either at foot level or eye level), and left click and drag to clear all blocks from an area. - `follow playerName` to follow a player. `followplayers` to follow any players in range (combine with Kill Aura for a fun time). `followentities` to follow any entities. `followentity pig` to follow entities of a specific type. - `save waypointName` to save a waypoint. `goto waypointName` to go to it. @@ -39,6 +39,11 @@ Some common examples: - `damn` daniel +New commands: +- `sel` to manage selections +- some others + + For the rest of the commands, you can take a look at the code [here](https://github.com/cabaletta/baritone/blob/master/src/api/java/baritone/api/utils/ExampleBaritoneControl.java). All the settings and documentation are here. If you find HTML easier to read than Javadoc, you can look here. diff --git a/src/api/java/baritone/api/BaritoneAPI.java b/src/api/java/baritone/api/BaritoneAPI.java index 4ea6ef3eb..53937bd80 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 2c892d982..8c5de47ab 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 1b47c4e33..d1bb68679 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -140,6 +140,13 @@ public final class Settings { */ public final Setting allowDiagonalDescend = new Setting<>(false); + /** + * Allow diagonal ascending + *

+ * Actually pretty safe, much safer than diagonal descend tbh + */ + public final Setting allowDiagonalAscend = new Setting<>(false); + /** * Allow mining the block directly beneath its feet *

@@ -171,7 +178,9 @@ public final class Settings { Blocks.CRAFTING_TABLE, Blocks.FURNACE, Blocks.CHEST, - Blocks.TRAPPED_CHEST + Blocks.TRAPPED_CHEST, + Blocks.SIGN, + Blocks.WALL_SIGN ))); /** @@ -241,7 +250,12 @@ public final class Settings { /** * How many degrees to randomize the yaw every tick. Set to 0 to disable */ - public final Setting randomLooking = new Setting<>(2d); + public final Setting randomLooking113 = new Setting<>(2d); + + /** + * How many degrees to randomize the pitch and yaw every tick. Set to 0 to disable + */ + public final Setting randomLooking = new Setting<>(0.01d); /** * This is the big A* setting. @@ -485,15 +499,20 @@ public final class Settings { public final Setting chatControl = new Setting<>(true); /** - * A second override over chatControl to force it on + * Some clients like Impact try to force chatControl to off, so here's a second setting to do it anyway */ - public final Setting removePrefix = new Setting<>(false); + public final Setting chatControlAnyway = new Setting<>(false); /** * Render the path */ public final Setting renderPath = new Setting<>(true); + /** + * Render the path as a line instead of a frickin thingy + */ + public final Setting renderPathAsLine = new Setting<>(false); + /** * Render the goal */ @@ -594,10 +613,41 @@ public final class Settings { public final Setting cachedChunksOpacity = new Setting<>(0.5f); /** - * Whether or not to use the "#" command prefix + * Whether or not to allow you to run Baritone commands with the prefix */ public final Setting prefixControl = new Setting<>(true); + /** + * The command prefix for chat control + */ + public final Setting prefix = new Setting<>("#"); + + /** + * Use a short Baritone prefix [B] instead of [Baritone] when logging to chat + */ + public final Setting shortBaritonePrefix = new Setting<>(false); + + /** + * Echo commands to chat when they are run + */ + public final Setting echoCommands = new Setting<>(true); + + /** + * Censor coordinates in goals and block positions + */ + public final Setting censorCoordinates = new Setting<>(false); + + /** + * Censor arguments to ran commands, to hide, for example, coordinates to #goal + */ + public final Setting censorRanCommands = new Setting<>(false); + + /** + * Always prefer silk touch tools over regular tools. This will not sacrifice speed, but it will always prefer silk + * touch tools over other tools of the same speed. This includes always choosing ANY silk touch tool over your hand. + */ + public final Setting preferSilkTouch = new Setting<>(false); + /** * Don't stop walking forward when you need to break blocks in your way */ @@ -659,7 +709,12 @@ public final class Settings { public final Setting exploreMaintainY = new Setting<>(64); /** - * Replant nether wart while farming + * Replant normal Crops while farming and leave cactus and sugarcane to regrow + */ + public final Setting replantCrops = new Setting<>(true); + + /** + * Replant nether wart while farming. This setting only has an effect when replantCrops is also enabled */ public final Setting replantNetherWart = new Setting<>(false); @@ -721,11 +776,37 @@ public final class Settings { */ public final Setting breakCorrectBlockPenaltyMultiplier = new Setting<>(10d); + /** + * When this setting is true, build a schematic with the highest X coordinate being the origin, instead of the lowest + */ + public final Setting schematicOrientationX = new Setting<>(false); + + /** + * When this setting is true, build a schematic with the highest Y coordinate being the origin, instead of the lowest + */ + public final Setting schematicOrientationY = new Setting<>(false); + + /** + * When this setting is true, build a schematic with the highest Z coordinate being the origin, instead of the lowest + */ + public final Setting schematicOrientationZ = new Setting<>(false); + + /** + * Distance to scan every tick for updates. Expanding this beyond player reach distance (i.e. setting it to 6 or above) + * is only necessary in very large schematics where rescanning the whole thing is costly. + */ + public final Setting builderTickScanRadius = new Setting<>(5); + /** * While mining, should it also consider dropped items of the correct type as a pathing destination (as well as ore blocks)? */ public final Setting mineScanDroppedItems = new Setting<>(true); + /** + * Trim incorrect positions too far away, helps performance but hurts reliability in very large schematics + */ + public final Setting distanceTrim = new Setting<>(true); + /** * Cancel the current path if the goal has changed, and the path originally ended in the goal but doesn't anymore. *

@@ -886,6 +967,51 @@ public final class Settings { */ public final Setting colorGoalBox = new Setting<>(Color.GREEN); + /** + * The color of the goal box when it's inverted + */ + public final Setting colorInvertedGoalBox = new Setting<>(Color.RED); + + /** + * The color of all selections + */ + public final Setting colorSelection = new Setting<>(Color.CYAN); + + /** + * The color of the selection pos 1 + */ + public final Setting colorSelectionPos1 = new Setting<>(Color.BLACK); + + /** + * The color of the selection pos 2 + */ + public final Setting colorSelectionPos2 = new Setting<>(Color.ORANGE); + + /** + * The opacity of the selection. 0 is completely transparent, 1 is completely opaque + */ + public final Setting selectionOpacity = new Setting<>(.5f); + + /** + * Line width of the goal when rendered, in pixels + */ + public final Setting selectionLineWidth = new Setting<>(2F); + + /** + * Render selections + */ + public final Setting renderSelection = new Setting<>(true); + + /** + * Ignore depth when rendering selections + */ + public final Setting renderSelectionIgnoreDepth = new Setting<>(true); + + /** + * Render selection corners + */ + public final Setting renderSelectionCorners = new Setting<>(true); + /** * A map of lowercase setting field names to their respective setting @@ -900,6 +1026,7 @@ public final class Settings { public final Map, Type> settingTypes; public final class Setting { + public T value; public final T defaultValue; private String name; @@ -982,10 +1109,10 @@ public final class Settings { } @SuppressWarnings("unchecked") - public List> getAllValuesByType(Class klass) { + public List> getAllValuesByType(Class cla$$) { List> result = new ArrayList<>(); for (Setting setting : allSettings) { - if (setting.getValueClass().equals(klass)) { + if (setting.getValueClass().equals(cla$$)) { result.add((Setting) setting); } } diff --git a/src/api/java/baritone/api/accessor/IGuiScreen.java b/src/api/java/baritone/api/accessor/IGuiScreen.java new file mode 100644 index 000000000..ba01e2755 --- /dev/null +++ b/src/api/java/baritone/api/accessor/IGuiScreen.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +import java.net.URI; + +public interface IGuiScreen { + + void openLink(URI url); +} diff --git a/src/api/java/baritone/api/accessor/IItemStack.java b/src/api/java/baritone/api/accessor/IItemStack.java new file mode 100644 index 000000000..480c713fa --- /dev/null +++ b/src/api/java/baritone/api/accessor/IItemStack.java @@ -0,0 +1,23 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +public interface IItemStack { + + int getBaritoneHash(); +} diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java index 157774489..5444bb838 100644 --- a/src/api/java/baritone/api/behavior/IPathingBehavior.java +++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java @@ -64,9 +64,17 @@ public interface IPathingBehavior extends IBehavior { Goal getGoal(); /** - * @return Whether or not a path is currently being executed. + * @return Whether or not a path is currently being executed. This will be false if there's currently a pause. + * @see #hasPath() */ - default boolean isPathing() { + boolean isPathing(); + + /** + * @return If there is a current path. Note that the path is not necessarily being executed, for example when there + * is a pause in effect. + * @see #isPathing() + */ + default boolean hasPath() { return getCurrent() != null; } diff --git a/src/api/java/baritone/api/cache/IWaypoint.java b/src/api/java/baritone/api/cache/IWaypoint.java index 01df2a48b..ff2350993 100644 --- a/src/api/java/baritone/api/cache/IWaypoint.java +++ b/src/api/java/baritone/api/cache/IWaypoint.java @@ -17,12 +17,9 @@ package baritone.api.cache; -import net.minecraft.util.math.BlockPos; -import org.apache.commons.lang3.ArrayUtils; +import baritone.api.utils.BetterBlockPos; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * A marker for a position in the world. @@ -60,7 +57,7 @@ public interface IWaypoint { * * @return The block position of this waypoint */ - BlockPos getLocation(); + BetterBlockPos getLocation(); enum Tag { @@ -92,20 +89,48 @@ public interface IWaypoint { /** * The names for the tag, anything that the tag can be referred to as. */ - private final String[] names; + public final String[] names; Tag(String... names) { this.names = names; } /** - * Finds a tag from one of the names that could be used to identify said tag. - * - * @param name The name of the tag - * @return The tag, if one is found, otherwise, {@code null} + * @return A name that can be passed to {@link #getByName(String)} to retrieve this tag */ - public static Tag fromString(String name) { - return TAG_LIST.stream().filter(tag -> ArrayUtils.contains(tag.names, name.toLowerCase())).findFirst().orElse(null); + public String getName() { + return names[0]; + } + + /** + * Gets a tag by one of its names. + * + * @param name The name to search for. + * @return The tag, if found, or null. + */ + public static Tag getByName(String name) { + for (Tag action : Tag.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + + return null; + } + + /** + * @return All tag names. + */ + public static String[] getAllNames() { + Set names = new HashSet<>(); + + for (Tag tag : Tag.values()) { + names.addAll(Arrays.asList(tag.names)); + } + + return names.toArray(new String[0]); } } } diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java index caa44cbc6..325d4bd0e 100644 --- a/src/api/java/baritone/api/cache/IWorldScanner.java +++ b/src/api/java/baritone/api/cache/IWorldScanner.java @@ -17,6 +17,7 @@ package baritone.api.cache; +import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import net.minecraft.block.Block; import net.minecraft.util.math.BlockPos; @@ -33,28 +34,53 @@ public interface IWorldScanner { /** * Scans the world, up to the specified max chunk radius, for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon - * @param blocks The blocks to scan for + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @param maxSearchRadius The maximum chunk search radius * @return The matching block positions */ - List scanChunkRadius(IPlayerContext ctx, List blocks, int max, int yLevelThreshold, int maxSearchRadius); + List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius); + + default List scanChunkRadius(IPlayerContext ctx, List filter, int max, int yLevelThreshold, int maxSearchRadius) { + return scanChunkRadius(ctx, new BlockOptionalMetaLookup(filter.toArray(new Block[0])), max, yLevelThreshold, maxSearchRadius); + } /** * Scans a single chunk for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for + * @param pos The position of the target chunk + * @param max The maximum number of blocks to scan before cutoff + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. + * @return The matching block positions + */ + List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold); + + /** + * Scans a single chunk for the specified blocks. + * + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param blocks The blocks to scan for * @param pos The position of the target chunk * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @return The matching block positions */ - List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold); + default List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) { + return scanChunk(ctx, new BlockOptionalMetaLookup(blocks), pos, max, yLevelThreshold); + } + + /** + * Repacks 40 chunks around the player. + * + * @param ctx The player context for that player. + * @return The number of chunks queued for repacking. + */ + int repack(IPlayerContext ctx); } diff --git a/src/api/java/baritone/api/cache/Waypoint.java b/src/api/java/baritone/api/cache/Waypoint.java index 2b9a7232b..9c4fbfbfc 100644 --- a/src/api/java/baritone/api/cache/Waypoint.java +++ b/src/api/java/baritone/api/cache/Waypoint.java @@ -17,7 +17,7 @@ package baritone.api.cache; -import net.minecraft.util.math.BlockPos; +import baritone.api.utils.BetterBlockPos; import java.util.Date; @@ -31,9 +31,9 @@ public class Waypoint implements IWaypoint { private final String name; private final Tag tag; private final long creationTimestamp; - private final BlockPos location; + private final BetterBlockPos location; - public Waypoint(String name, Tag tag, BlockPos location) { + public Waypoint(String name, Tag tag, BetterBlockPos location) { this(name, tag, location, System.currentTimeMillis()); } @@ -46,7 +46,7 @@ public class Waypoint implements IWaypoint { * @param location The waypoint location * @param creationTimestamp When the waypoint was created */ - public Waypoint(String name, Tag tag, BlockPos location, long creationTimestamp) { + public Waypoint(String name, Tag tag, BetterBlockPos location, long creationTimestamp) { this.name = name; this.tag = tag; this.location = location; @@ -55,7 +55,7 @@ public class Waypoint implements IWaypoint { @Override public int hashCode() { - return name.hashCode() + tag.hashCode() + location.hashCode(); //lol + return name.hashCode() ^ tag.hashCode() ^ location.hashCode() ^ Long.hashCode(creationTimestamp); } @Override @@ -74,13 +74,18 @@ public class Waypoint implements IWaypoint { } @Override - public BlockPos getLocation() { + public BetterBlockPos getLocation() { return this.location; } @Override public String toString() { - return name + " " + location.toString() + " " + new Date(creationTimestamp).toString(); + return String.format( + "%s %s %s", + name, + BetterBlockPos.from(location).toString(), + new Date(creationTimestamp).toString() + ); } @Override diff --git a/src/api/java/baritone/api/event/events/TabCompleteEvent.java b/src/api/java/baritone/api/event/events/TabCompleteEvent.java new file mode 100644 index 000000000..fea1da292 --- /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 000000000..cbad9b999 --- /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 71045768a..e7d4dc292 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 93bdb7a61..44db8f62f 100644 --- a/src/api/java/baritone/api/event/listener/IGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java @@ -56,6 +56,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 89dd63048..c85e5cadd 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalBlock.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.util.math.BlockPos; @@ -67,7 +68,12 @@ public class GoalBlock implements Goal, IGoalRenderPos { @Override public String toString() { - return "GoalBlock{x=" + x + ",y=" + y + ",z=" + z + "}"; + return String.format( + "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 fb2a07aaf..8d6fdcb30 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.util.math.BlockPos; @@ -61,6 +62,11 @@ public class GoalGetToBlock implements Goal, IGoalRenderPos { @Override public String toString() { - return "GoalGetToBlock{x=" + x + ",y=" + y + ",z=" + z + "}"; + return String.format( + "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 000000000..197369241 --- /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 4f75aba72..73d64ed9f 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalNear.java +++ b/src/api/java/baritone/api/pathing/goals/GoalNear.java @@ -17,10 +17,12 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; 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; @@ -56,11 +58,12 @@ public class GoalNear implements Goal, IGoalRenderPos { @Override public String toString() { - return "GoalNear{" + - "x=" + x + - ", y=" + y + - ", z=" + z + - ", rangeSq=" + rangeSq + - "}"; + return String.format( + "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 3f4a02de1..a1a153780 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java +++ b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; import net.minecraft.util.math.BlockPos; import java.util.Arrays; @@ -82,7 +83,11 @@ public class GoalRunAway implements Goal { @Override public String toString() { if (maintainY != null) { - return "GoalRunAwayFromMaintainY y=" + maintainY + ", " + Arrays.asList(from); + return String.format( + "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 24ae385ac..b48a029ad 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java +++ b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -24,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; @@ -64,12 +66,13 @@ public class GoalStrictDirection implements Goal { @Override public String toString() { - return "GoalStrictDirection{" + - "x=" + x + - ", y=" + y + - ", z=" + z + - ", dx=" + dx + - ", dz=" + dz + - "}"; + 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) + ); } } diff --git a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java index f1026ab50..27be981e4 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java +++ b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.util.math.BlockPos; @@ -73,6 +74,11 @@ public class GoalTwoBlocks implements Goal, IGoalRenderPos { @Override public String toString() { - return "GoalTwoBlocks{x=" + x + ",y=" + y + ",z=" + z + "}"; + return String.format( + "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 7f8d16abe..63d39cd78 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalXZ.java +++ b/src/api/java/baritone/api/pathing/goals/GoalXZ.java @@ -18,6 +18,8 @@ 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; @@ -45,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; @@ -59,7 +66,11 @@ public class GoalXZ implements Goal { @Override public String toString() { - return "GoalXZ{x=" + x + ",z=" + z + "}"; + return String.format( + "GoalXZ{x=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(z) + ); } public static double calculate(double xDiff, double zDiff) { @@ -87,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 9add7176a..603ef9bd1 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalYLevel.java +++ b/src/api/java/baritone/api/pathing/goals/GoalYLevel.java @@ -18,6 +18,7 @@ package baritone.api.pathing.goals; import baritone.api.pathing.movement.ActionCosts; +import baritone.api.utils.SettingsUtil; /** * Useful for mining (getting to diamond / iron level) @@ -59,6 +60,9 @@ public class GoalYLevel implements Goal, ActionCosts { @Override public String toString() { - return "GoalYLevel{y=" + level + "}"; + return String.format( + "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 90aba4eaf..efd96d9fa 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 811a509f0..4950d31d5 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 c9234a24a..6ced16fba 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 7ebabc9cb..9cd76a09a 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 000000000..7a24e803f --- /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 000000000..21df29b4d --- /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 000000000..a26f356bf --- /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 000000000..aaaeb5dd1 --- /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 000000000..fa9b81269 --- /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 000000000..988cfd7f1 --- /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 000000000..4ead61b6c --- /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 000000000..e05de1e70 --- /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 000000000..59651feb0 --- /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 000000000..de5ee2887 --- /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 3b94a8339..a9b02445b 100644 --- a/src/api/java/baritone/api/utils/BetterBlockPos.java +++ b/src/api/java/baritone/api/utils/BetterBlockPos.java @@ -22,6 +22,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3i; +import javax.annotation.Nonnull; + /** * A better BlockPos that has fewer hash collisions (and slightly more performant offsets) *

@@ -32,6 +34,9 @@ import net.minecraft.util.math.Vec3i; * @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; @@ -51,6 +56,20 @@ public final class BetterBlockPos extends BlockPos { this(pos.getX(), pos.getY(), pos.getZ()); } + /** + * Like constructor but returns null if pos is null, good if you just need to possibly censor coordinates + * + * @param pos The BlockPos, possibly null, to convert + * @return A BetterBlockPos or null if pos was null + */ + public static BetterBlockPos from(BlockPos pos) { + if (pos == null) { + return null; + } + + return new BetterBlockPos(pos); + } + @Override public int hashCode() { return (int) longHash(x, y, z); @@ -182,4 +201,15 @@ public final class BetterBlockPos extends BlockPos { public BetterBlockPos west(int amt) { return amt == 0 ? this : new BetterBlockPos(x - amt, y, z); } + + @Override + @Nonnull + public String toString() { + return String.format( + "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 000000000..9a1891c8c --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockOptionalMeta.java @@ -0,0 +1,294 @@ +/* + * 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.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.state.IProperty; +import net.minecraft.state.properties.*; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.IRegistry; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +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 (!IRegistry.BLOCK.containsKey(id)) { + throw new IllegalArgumentException("Invalid block ID"); + } + + block = IRegistry.BLOCK.get(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(Half.BOTTOM); + put.accept(StairsShape.STRAIGHT); + put.accept(AttachFace.FLOOR); + put.accept(DoubleBlockHalf.UPPER); + put.accept(SlabType.BOTTOM); + put.accept(DoorHingeSide.LEFT); + put.accept(BedPart.HEAD); + put.accept(RailShape.NORTH_SOUTH); + _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(BlockDirtSnowy.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.UP, 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.DISTANCE, 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(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(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()) { + Class valueClass = property.getValueClass(); + if (normalizations.containsKey(property)) { + try { + newState = newState.with( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(property)) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(state.get(property))) { + try { + newState = newState.with( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(state.get(property))) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(valueClass)) { + try { + newState = newState.with( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(valueClass)) + ); + } catch (IllegalArgumentException ignored) {} + } + } + + return newState; + } + + public static int stateMeta(IBlockState state) { + throw new UnsupportedOperationException(); + } + + private static Set getStates(@Nonnull Block block, @Nullable Integer meta) { + throw new UnsupportedOperationException(); + /*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, ctx.world(), null, 0), + state.getBlock().(state) + )) + .map(stack -> ((IItemStack) (Object) stack).getBaritoneHash()) + .toArray(Integer[]::new) + );*/ + return ImmutableSet.of(); + } + + 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.getDamage(); + } + + return stackHashes.contains(hash); + } + + @Override + public String toString() { + return String.format("BlockOptionalMeta{block=%s,meta=%s}", block, getMeta()); + } + + 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 000000000..041c6162c --- /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 f7d0d9f6b..5d2974f3e 100644 --- a/src/api/java/baritone/api/utils/BlockUtils.java +++ b/src/api/java/baritone/api/utils/BlockUtils.java @@ -23,9 +23,9 @@ import net.minecraft.util.registry.IRegistry; 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) { @@ -40,7 +40,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 648702e53..000000000 --- a/src/api/java/baritone/api/utils/ExampleBaritoneControl.java +++ /dev/null @@ -1,747 +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.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.chunk.Chunk; -import net.minecraft.world.dimension.DimensionType; - -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.shallowCopy(); - 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); - 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.getInstance().gameSettings.renderDistanceChunks + 1) * 16; - Minecraft.getInstance().worldRenderer.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(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().getFormattedText().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().getString().trim().toLowerCase(); - if (!theirName.equals(ctx.player().getName().getString().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.getInstance().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]; - } - 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.getChunk(x, z, false, false); - if (chunk != null) { - 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.getDamage()); - } - } - } - - 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().dimension.getType().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().dimension.getType(); - } - -} diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java index f05f5ed08..7feb2ba50 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.getInstance(); + 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); + mc.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.shallowCopy(); - component.getStyle().setColor(TextFormatting.GRAY); - component.appendSibling(new TextComponentString(" " + message)); - Minecraft.getInstance().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 f0a5c3314..7918d1fbe 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/IPlayerController.java b/src/api/java/baritone/api/utils/IPlayerController.java index 14334d5e2..05199fcac 100644 --- a/src/api/java/baritone/api/utils/IPlayerController.java +++ b/src/api/java/baritone/api/utils/IPlayerController.java @@ -35,21 +35,27 @@ import net.minecraft.world.World; */ public interface IPlayerController { + void syncHeldItem(); + + boolean hasBrokenBlock(); + boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side); void resetBlockRemoving(); ItemStack windowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player); - void setGameType(GameType type); - GameType getGameType(); - default double getBlockReachDistance() { - return this.getGameType().isCreative() ? 5.0F : 4.5F; - } - EnumActionResult processRightClickBlock(EntityPlayerSP player, World world, BlockPos pos, EnumFacing direction, Vec3d vec, EnumHand hand); EnumActionResult processRightClick(EntityPlayerSP player, World world, EnumHand hand); + + boolean clickBlock(BlockPos loc, EnumFacing face); + + void setHittingBlock(boolean hittingBlock); + + default double getBlockReachDistance() { + return this.getGameType().isCreative() ? 5.0F : 4.5F; + } } diff --git a/src/api/java/baritone/api/utils/ISchematic.java b/src/api/java/baritone/api/utils/ISchematic.java index 821dc68c3..2adcbdd58 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 8b61012f9..7cda5aa35 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 b540342f1..ce281665f 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -17,14 +17,15 @@ package baritone.api.utils; +import baritone.api.BaritoneAPI; import baritone.api.Settings; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.item.Item; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.IRegistry; import net.minecraft.util.math.Vec3i; +import net.minecraft.util.registry.IRegistry; import java.awt.*; import java.io.BufferedReader; @@ -36,7 +37,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; @@ -44,9 +44,11 @@ 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; public class SettingsUtil { + private static final Path SETTINGS_PATH = Minecraft.getInstance().gameDir.toPath().resolve("baritone").resolve("settings.txt"); private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?.+)"); // key and value split by the first space @@ -122,15 +124,54 @@ 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 ""; + } + + return Integer.toString(coord); + } + public static String settingToString(Settings.Setting setting) throws IllegalStateException { if (setting.getName().equals("logger")) { return "logger"; } - Parser io = Parser.getParser(setting.getType()); - if (io == null) { - throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); - } - return setting.getName() + " " + io.toString(new ParserContext(setting), setting.value); + + return setting.getName() + " " + settingValueToString(setting); } public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { @@ -176,6 +217,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, @@ -202,7 +244,7 @@ public class SettingsUtil { public Object parse(ParserContext context, String raw) { 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()); } @@ -223,22 +265,22 @@ public class SettingsUtil { } }; - private final Class klass; + private final Class cla$$; private final Function parser; private final Function toString; Parser() { - this.klass = null; + this.cla$$ = null; this.parser = null; this.toString = null; } - Parser(Class klass, Function parser) { - this(klass, parser, Object::toString); + Parser(Class cla$$, Function parser) { + this(cla$$, parser, Object::toString); } - Parser(Class klass, Function parser, Function toString) { - this.klass = klass; + Parser(Class cla$$, Function parser, Function toString) { + this.cla$$ = cla$$; this.parser = parser::apply; this.toString = x -> toString.apply((T) x); } @@ -257,11 +299,11 @@ public class SettingsUtil { @Override public boolean accepts(Type type) { - return type instanceof Class && this.klass.isAssignableFrom((Class) type); + return type instanceof Class && this.cla$$.isAssignableFrom((Class) type); } 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 000000000..e83d6c50b --- /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 000000000..63b6a4458 --- /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 000000000..1b0730c60 --- /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 000000000..c33f61fbd --- /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 000000000..09e2aa2ea --- /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 000000000..a5b044c43 --- /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 000000000..750ff545d --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/BlockById.java @@ -0,0 +1,54 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.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 net.minecraft.util.registry.IRegistry; + +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 = IRegistry.BLOCK.get(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( + IRegistry.BLOCK.keySet() + .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 000000000..87e58e509 --- /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.EntityType; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.IRegistry; + +import java.util.stream.Stream; + +public enum EntityClassById implements IDatatypeFor { + INSTANCE; + + @Override + public EntityType get(IDatatypeContext ctx) throws CommandException { + ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); + EntityType entity; + if ((entity = IRegistry.ENTITY_TYPE.get(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(IRegistry.ENTITY_TYPE.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 000000000..f36826c6c --- /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 000000000..ed6dce31b --- /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 000000000..e60ff0c6a --- /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 000000000..8c155fb68 --- /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 000000000..1452bf4ab --- /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 000000000..4f0f42006 --- /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 000000000..2e4cc47af --- /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 000000000..a3de089e0 --- /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 000000000..4b91453d2 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/NearbyPlayer.java @@ -0,0 +1,56 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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 net.minecraft.util.text.ITextComponent; + +import java.util.List; +import java.util.stream.Stream; + +/** + * An {@link IDatatype} used to resolve nearby players, those within + * render distance of the target {@link IBaritone} instance. + */ +public enum NearbyPlayer implements IDatatypeFor { + INSTANCE; + + @Override + public EntityPlayer get(IDatatypeContext ctx) throws CommandException { + final String username = ctx.getConsumer().getString(); + return getPlayers(ctx).stream() + .filter(s -> s.getName().toString().equalsIgnoreCase(username)) + .findFirst().orElse(null); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(getPlayers(ctx).stream().map(EntityPlayer::getName).map(ITextComponent::getString)) + .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 000000000..8459db88d --- /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 000000000..84a29f63d --- /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 000000000..71a9ca58c --- /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 000000000..d47e05ce4 --- /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 000000000..cd1c0be10 --- /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 000000000..b8bd0cd62 --- /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 000000000..a8a9b232e --- /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 000000000..002d328e1 --- /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 000000000..9fdc3f735 --- /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 000000000..627067051 --- /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 000000000..4e00c1f7f --- /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 000000000..5e5b81cdd --- /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 000000000..f4f2d8181 --- /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 000000000..655652d6c --- /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 000000000..e8904cbc8 --- /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 000000000..24fc799fe --- /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 000000000..6d647a201 --- /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 000000000..379e97b7f --- /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 000000000..4ca5b193c --- /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 000000000..e92855d1d --- /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 000000000..da5ac4362 --- /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 000000000..db63b2d58 --- /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 000000000..63682433e --- /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 000000000..bac50dcfa --- /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 5bbbc0070..13e7e6860 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 000000000..9383c9aa5 --- /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 000000000..fb79c6f47 --- /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.get(((IBitArray) storage).getAtFast(index)); + } + + @Override + public IBlockState getAtPalette(int index) { + return palette.get(index); + } + + @Override + public int[] storageArray() { + return ((IBitArray) storage).toArray(); + } +} 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 000000000..9f91beaab --- /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 000000000..12cda8b3f --- /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; + + @Unique + private int baritoneHash; + + @Shadow + protected abstract int getDamage(); + + private void recalculateHash() { + baritoneHash = item == null ? -1 : item.hashCode() + getDamage(); + } + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onInit(CallbackInfo ci) { + recalculateHash(); + } + + @Inject( + method = "setDamage", + 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 6c065049a..453538502 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/MixinBlockPos.java b/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java similarity index 53% rename from src/launch/java/baritone/launch/mixins/MixinBlockPos.java rename to src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java index b0aa75ba4..88c4abec0 100644 --- a/src/launch/java/baritone/launch/mixins/MixinBlockPos.java +++ b/src/launch/java/baritone/launch/mixins/MixinPlayerControllerMP.java @@ -17,33 +17,25 @@ package baritone.launch.mixins; -import com.google.common.base.MoreObjects; +import baritone.utils.accessor.IPlayerControllerMP; +import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3i; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; -import javax.annotation.Nonnull; +@Mixin(PlayerControllerMP.class) +public abstract class MixinPlayerControllerMP implements IPlayerControllerMP { -/** - * @author Brady - * @since 8/25/2018 - */ -@Mixin(BlockPos.class) -public class MixinBlockPos extends Vec3i { - - public MixinBlockPos(int xIn, int yIn, int zIn) { - super(xIn, yIn, zIn); - } - - /** - * The purpose of this was to ensure a friendly name for when we print raw - * block positions to chat in the context of an obfuscated environment. - * - * @return a string representation of the object. - */ + @Accessor @Override - @Nonnull - public String toString() { - return MoreObjects.toStringHelper("BlockPos").add("x", this.getX()).add("y", this.getY()).add("z", this.getZ()).toString(); - } + public abstract void setIsHittingBlock(boolean isHittingBlock); + + @Accessor + @Override + public abstract BlockPos getCurrentBlock(); + + @Invoker + @Override + public abstract void callSyncCurrentPlayItem(); } diff --git a/src/launch/resources/mixins.baritone.json b/src/launch/resources/mixins.baritone.json index 056b17a42..ceaba84de 100644 --- a/src/launch/resources/mixins.baritone.json +++ b/src/launch/resources/mixins.baritone.json @@ -9,7 +9,8 @@ }, "client": [ "MixinAnvilChunkLoader", - "MixinBlockPos", + "MixinBitArray", + "MixinBlockStateContainer", "MixinChunkProviderClient", "MixinChunkProviderServer", "MixinChunkRenderContainer", @@ -17,9 +18,12 @@ "MixinEntityLivingBase", "MixinEntityPlayerSP", "MixinGameRenderer", + "MixinGuiScreen", + "MixinItemStack", "MixinMinecraft", "MixinNetHandlerPlayClient", "MixinNetworkManager", + "MixinPlayerControllerMP", "MixinRenderChunk", "MixinRenderChunkCache", "MixinRenderList", diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index 85ca71892..e92103a82 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 44715f4c5..416f1cae5 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 a9e0c2d65..3dada966b 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); } @@ -102,7 +103,7 @@ public final class InventoryBehavior extends Behavior { return -1; } - private int bestToolAgainst(Block against, Class klass) { + private int bestToolAgainst(Block against, Class cla$$) { NonNullList invy = ctx.player().inventory.mainInventory; int bestInd = -1; double bestSpeed = -1; @@ -111,7 +112,7 @@ public final class InventoryBehavior extends Behavior { if (stack.isEmpty()) { continue; } - if (klass.isInstance(stack.getItem())) { + if (cla$$.isInstance(stack.getItem())) { double speed = ToolSet.calculateSpeedVsBlock(stack, against.getDefaultState()); // takes into account enchants if (speed > bestSpeed) { bestSpeed = speed; @@ -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(new BlockItemUseContext(new ItemUseContext(ctx.player(), stack, ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ)))))) { return true; // gotem } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 2a8a4935b..801a5e43e 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -55,7 +55,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { if (Math.abs(rand) < 0.1) { rand *= 4; } - this.target = new Rotation(this.target.getYaw() + (float) (rand * Baritone.settings().randomLooking.value), this.target.getPitch()); + this.target = new Rotation(this.target.getYaw() + (float) (rand * Baritone.settings().randomLooking113.value), this.target.getPitch()); } this.force = force || !Baritone.settings().freeLook.value; } @@ -76,6 +76,8 @@ public final class LookBehavior extends Behavior implements ILookBehavior { float oldPitch = ctx.player().rotationPitch; float desiredPitch = this.target.getPitch(); ctx.player().rotationPitch = desiredPitch; + ctx.player().rotationYaw += (Math.random() - 0.5) * Baritone.settings().randomLooking.value; + ctx.player().rotationPitch += (Math.random() - 0.5) * Baritone.settings().randomLooking.value; if (desiredPitch == oldPitch && !Baritone.settings().freeLook.value) { nudgeToLevel(); } diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index d98e32376..66ab10f31 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -24,6 +24,7 @@ import baritone.api.event.events.PacketEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.type.EventState; +import baritone.api.utils.BetterBlockPos; import baritone.cache.ContainerMemory; import baritone.utils.BlockStateInterface; import net.minecraft.block.Block; @@ -99,8 +100,8 @@ public final class MemoryBehavior extends Behavior { TileEntityLockable lockable = (TileEntityLockable) tileEntity; int size = lockable.getSizeInventory(); - BlockPos position = tileEntity.getPos(); - BlockPos adj = neighboringConnectedBlock(position); + BetterBlockPos position = BetterBlockPos.from(tileEntity.getPos()); + BetterBlockPos adj = BetterBlockPos.from(neighboringConnectedBlock(position)); System.out.println(position + " " + adj); if (adj != null) { size *= 2; // double chest or double trapped chest @@ -159,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()))); } } @@ -239,7 +240,8 @@ public final class MemoryBehavior extends Behavior { this.slots = slots; this.type = type; this.pos = pos; - System.out.println("Future inventory created " + time + " " + slots + " " + type + " " + pos); + // betterblockpos has censoring + System.out.println("Future inventory created " + time + " " + slots + " " + type + " " + BetterBlockPos.from(pos)); } } @@ -253,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 7219175ed..86fd95055 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 237c460b4..e74053901 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 b8e7cc1e3..cc4fda72e 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -88,6 +88,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++) { @@ -101,6 +102,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 18b13b89b..222adfdf6 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 b3dc6664e..8e72fd3c4 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -17,16 +17,19 @@ 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.ChunkSection; +import net.minecraft.world.chunk.IChunkProvider; import java.util.*; import java.util.stream.IntStream; @@ -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) { ChunkSection[] chunkInternalStorageArray = chunk.getSections(); 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.getChunk(x, z, false, false); + + 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 5519d5023..f03378562 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 6de8290d0..2f7a0af5a 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/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index d46e783eb..a9a1440d0 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -61,6 +61,7 @@ public class CalculationContext { public final boolean allowParkourAscend; public final boolean assumeWalkOnWater; public final boolean allowDiagonalDescend; + public final boolean allowDiagonalAscend; public final boolean allowDownward; public final int maxFallHeightNoWater; public final int maxFallHeightBucket; @@ -94,6 +95,7 @@ public class CalculationContext { this.allowParkourAscend = Baritone.settings().allowParkourAscend.value; this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.value; this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.value; + this.allowDiagonalAscend = Baritone.settings().allowDiagonalAscend.value; this.allowDownward = Baritone.settings().allowDownward.value; this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.value; this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.value; diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 98cd069dd..ec9bf02e4 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -18,6 +18,7 @@ package baritone.pathing.movement; import baritone.Baritone; +import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.pathing.movement.ActionCosts; import baritone.api.pathing.movement.MovementStatus; @@ -185,7 +186,7 @@ public interface MovementHelper extends ActionCosts, Helper { return state.allowsMovement(null, null, PathType.LAND); } - 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 @@ -214,6 +215,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; @@ -415,7 +421,7 @@ public interface MovementHelper extends ActionCosts, Helper { * @param b the blockstate to mine */ static void switchToBestToolFor(IPlayerContext ctx, IBlockState b) { - switchToBestToolFor(ctx, b, new ToolSet(ctx.player())); + switchToBestToolFor(ctx, b, new ToolSet(ctx.player()), BaritoneAPI.getSettings().preferSilkTouch.value); } /** @@ -425,8 +431,8 @@ public interface MovementHelper extends ActionCosts, Helper { * @param b the blockstate to mine * @param ts previously calculated ToolSet */ - static void switchToBestToolFor(IPlayerContext ctx, IBlockState b, ToolSet ts) { - ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock()); + static void switchToBestToolFor(IPlayerContext ctx, IBlockState b, ToolSet ts, boolean preferSilkTouch) { + ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock(), preferSilkTouch); } static void moveTowards(IPlayerContext ctx, MovementState state, BlockPos pos) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index cbc8cdd86..66827a35f 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/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index 327d65bea..3373a7f4d 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -70,24 +70,37 @@ public class MovementDiagonal extends Movement { protected Set calculateValidPositions() { BetterBlockPos diagA = new BetterBlockPos(src.x, src.y, dest.z); BetterBlockPos diagB = new BetterBlockPos(dest.x, src.y, src.z); - if (dest.y != src.y) { // only if allowDiagonalDescend + if (dest.y < src.y) { return ImmutableSet.of(src, dest.up(), diagA, diagB, dest, diagA.down(), diagB.down()); } + if (dest.y > src.y) { + return ImmutableSet.of(src, src.up(), diagA, diagB, dest, diagA.up(), diagB.up()); + } return ImmutableSet.of(src, dest, diagA, diagB); } public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { - IBlockState destInto = context.get(destX, y, destZ); - if (!MovementHelper.canWalkThrough(context.bsi, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi, destX, y + 1, destZ)) { + if (!MovementHelper.canWalkThrough(context.bsi, destX, y + 1, destZ)) { return; } - IBlockState destWalkOn = context.get(destX, y - 1, destZ); + IBlockState destInto = context.get(destX, y, destZ); + boolean ascend = false; + IBlockState destWalkOn; boolean descend = false; - if (!MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) { - descend = true; - if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) { + if (!MovementHelper.canWalkThrough(context.bsi, destX, y, destZ, destInto)) { + ascend = true; + if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context.bsi, x, y + 2, z) || !MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi, destX, y + 2, destZ)) { return; } + destWalkOn = destInto; + } else { + destWalkOn = context.get(destX, y - 1, destZ); + if (!MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) { + descend = true; + if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) { + return; + } + } } double multiplier = WALK_ONE_BLOCK_COST; // For either possible soul sand, that affects half of our walking @@ -111,8 +124,43 @@ public class MovementDiagonal extends Movement { if (cuttingOver2.getBlock() == Blocks.MAGMA_BLOCK || MovementHelper.isLava(cuttingOver2)) { return; } + boolean water = false; + IBlockState startState = context.get(x, y, z); + Block startIn = startState.getBlock(); + if (MovementHelper.isWater(startState) || MovementHelper.isWater(destInto)) { + if (ascend) { + return; + } + // Ignore previous multiplier + // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water + // Not even touching the blocks below + multiplier = context.waterWalkSpeed; + water = true; + } IBlockState pb0 = context.get(x, y, destZ); IBlockState pb2 = context.get(destX, y, z); + if (ascend) { + boolean ATop = MovementHelper.canWalkThrough(context.bsi, x, y + 2, destZ); + boolean AMid = MovementHelper.canWalkThrough(context.bsi, x, y + 1, destZ); + boolean ALow = MovementHelper.canWalkThrough(context.bsi, x, y, destZ, pb0); + boolean BTop = MovementHelper.canWalkThrough(context.bsi, destX, y + 2, z); + boolean BMid = MovementHelper.canWalkThrough(context.bsi, destX, y + 1, z); + boolean BLow = MovementHelper.canWalkThrough(context.bsi, destX, y, z, pb2); + if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option + || MovementHelper.avoidWalkingInto(pb0) // bad + || MovementHelper.avoidWalkingInto(pb2) // bad + || (ATop && AMid && MovementHelper.canWalkOn(context.bsi, x, y, destZ, pb0)) // we could just ascend + || (BTop && BMid && MovementHelper.canWalkOn(context.bsi, destX, y, z, pb2)) // we could just ascend + || (!ATop && AMid && ALow) // head bonk A + || (!BTop && BMid && BLow)) { // head bonk B + return; + } + res.cost = multiplier * SQRT_2 + JUMP_ONE_BLOCK_COST; + res.x = destX; + res.z = destZ; + res.y = y + 1; + return; + } double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false); double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false); if (optionA != 0 && optionB != 0) { @@ -140,16 +188,6 @@ public class MovementDiagonal extends Movement { // and now that option B is fully calculated, see if we can edge around that way return; } - boolean water = false; - IBlockState startState = context.get(x, y, z); - Block startIn = startState.getBlock(); - if (MovementHelper.isWater(startState) || MovementHelper.isWater(destInto)) { - // Ignore previous multiplier - // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water - // Not even touching the blocks below - multiplier = context.waterWalkSpeed; - water = true; - } if (optionA != 0 || optionB != 0) { multiplier *= SQRT_2 - 0.001; // TODO tune if (startIn == Blocks.LADDER || startIn == Blocks.VINE) { @@ -188,6 +226,9 @@ public class MovementDiagonal extends Movement { } else if (!playerInValidPosition() && !(MovementHelper.isLiquid(ctx, src) && getValidPositions().contains(ctx.playerFeet().up()))) { return state.setStatus(MovementStatus.UNREACHABLE); } + if (dest.y > src.y && ctx.player().posY < src.y + 0.1 && ctx.player().collidedHorizontally) { + state.setInput(Input.JUMP, true); + } if (sprint()) { state.setInput(Input.SPRINT, true); } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index 089998e8b..92a35c56c 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -153,7 +153,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 60ff4ef9c..b031de9fa 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -114,7 +114,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) || MovementHelper.isWater(pb1); if (MovementHelper.isWater(destOn) && 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 686a15f09..1ba497d81 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 9e4b4899e..01622d9ac 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; @@ -71,6 +71,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); @@ -81,7 +82,19 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil this.name = name; this.schematic = schematic; this.realSchematic = null; - this.origin = origin; + int x = origin.getX(); + int y = origin.getY(); + int z = origin.getZ(); + if (Baritone.settings().schematicOrientationX.value) { + x += schematic.widthX(); + } + if (Baritone.settings().schematicOrientationY.value) { + y += schematic.heightY(); + } + if (Baritone.settings().schematicOrientationZ.value) { + z += schematic.lengthZ(); + } + this.origin = new Vec3i(x, y, z); this.paused = false; this.layer = 0; this.observedCompleted = new LongOpenHashSet(); @@ -95,6 +108,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; @@ -104,6 +122,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil e.printStackTrace(); return false; } + //noinspection ConstantConditions if (tag == null) { return false; } @@ -130,7 +149,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) { @@ -142,14 +166,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() instanceof BlockAir) { return null; } @@ -168,7 +192,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 } @@ -186,7 +210,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; @@ -200,7 +225,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++) { @@ -208,12 +233,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() instanceof BlockAir) { continue; } @@ -238,7 +263,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 (!toPlace.isValidPosition(ctx.world(), new BetterBlockPos(x, y, z))) { @@ -321,6 +346,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 { @@ -348,13 +374,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 @@ -392,7 +418,9 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil logDirect("Repeating build in vector " + repeat + ", new origin is " + origin); return onTick(calcFailed, isSafeToCancel); } - trim(); + if (Baritone.settings().distanceTrim.value) { + trim(); + } Optional> toBreak = toBreakNearPlayer(bcc); if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround) { @@ -414,7 +442,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); @@ -426,14 +454,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; } @@ -444,7 +471,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; } @@ -452,9 +479,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; @@ -489,13 +516,14 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil private void recalcNearby(BuilderCalculationContext bcc) { BetterBlockPos center = ctx.playerFeet(); - for (int dx = -5; dx <= 5; dx++) { - for (int dy = -5; dy <= 5; dy++) { - for (int dz = -5; dz <= 5; dz++) { + int radius = Baritone.settings().builderTickScanRadius.value; + for (int dx = -radius; dx <= radius; dx++) { + for (int dy = -radius; dy <= radius; dy++) { + for (int dz = -radius; dz <= radius; dz++) { 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); @@ -517,15 +545,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)); @@ -550,15 +579,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 BlockFlowingFluid) { @@ -576,8 +605,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)); } }); @@ -593,6 +622,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class JankyGoalComposite implements Goal { + private final Goal primary; private final Goal fallback; @@ -640,8 +670,10 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return new GoalPlace(pos); } boolean allowSameLevel = !(ctx.world().getBlockState(pos.up()).getBlock() instanceof BlockAir); + 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)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ()))) { + //noinspection ConstantConditions + if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current))) { return new GoalAdjacent(pos, pos.offset(facing), allowSameLevel); } } @@ -664,6 +696,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class GoalAdjacent extends GoalGetToBlock { + private boolean allowSameLevel; private BlockPos no; @@ -696,6 +729,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } public static class GoalPlace extends GoalBlock { + public GoalPlace(BlockPos placeAt) { super(placeAt.up()); } @@ -722,7 +756,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); @@ -752,7 +786,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; @@ -760,7 +795,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(); @@ -770,9 +805,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; } @@ -783,7 +818,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() instanceof BlockAir) { @@ -791,7 +826,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 @@ -817,7 +852,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() instanceof BlockAir) { // it should be air diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java index 3fa3d4133..220ae3b12 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/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index 001305c63..4894407cc 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -110,13 +110,19 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro SUGARCANE(Blocks.SUGAR_CANE, null) { @Override public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { - return world.getBlockState(pos.down()).getBlock() instanceof BlockReed; + if (Baritone.settings().replantCrops.value) { + return world.getBlockState(pos.down()).getBlock() instanceof BlockReed; + } + return true; } }, CACTUS(Blocks.CACTUS, null) { @Override public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { - return world.getBlockState(pos.down()).getBlock() instanceof BlockCactus; + if (Baritone.settings().replantCrops.value) { + return world.getBlockState(pos.down()).getBlock() instanceof BlockCactus; + } + return true; } }; public final Block block; @@ -164,10 +170,13 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro for (Harvest harvest : Harvest.values()) { scan.add(harvest.block); } - scan.add(Blocks.FARMLAND); - if (Baritone.settings().replantNetherWart.value) { - scan.add(Blocks.SOUL_SAND); + if (Baritone.settings().replantCrops.value) { + scan.add(Blocks.FARMLAND); + if (Baritone.settings().replantNetherWart.value) { + scan.add(Blocks.SOUL_SAND); + } } + if (Baritone.settings().mineGoalUpdateInterval.value != 0 && tickCount++ % Baritone.settings().mineGoalUpdateInterval.value == 0) { Baritone.getExecutor().execute(() -> locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, scan, 256, 10, 10)); } diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index f89843745..fe688e8f7 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 4c43cd6f2..ec99bd890 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 9dc037a47..4043e6270 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(), ctx.world(), null, 0).asItem(); - 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.getDisplayName(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(), world, null, 0).asItem(); - Item ore = block.asItem(); - 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 000000000..58e069de1 --- /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 000000000..c7f213001 --- /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 000000000..89104d030 --- /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/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index a71fe9d59..aaf68201b 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -19,9 +19,7 @@ package baritone.utils; import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; -import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; /** @@ -30,34 +28,43 @@ import net.minecraft.util.math.RayTraceResult; */ public final class BlockBreakHelper implements Helper { + private final IPlayerContext ctx; private boolean didBreakLastTick; - private final IPlayerContext playerContext; - - public BlockBreakHelper(IPlayerContext playerContext) { - this.playerContext = playerContext; - } - - private void tryBreakBlock(BlockPos pos, EnumFacing side) { - if (playerContext.playerController().onPlayerDamageBlock(pos, side)) { - playerContext.player().swingArm(EnumHand.MAIN_HAND); - } + BlockBreakHelper(IPlayerContext ctx) { + this.ctx = ctx; } public void stopBreakingBlock() { // The player controller will never be null, but the player can be - if (playerContext.player() != null) { - playerContext.playerController().resetBlockRemoving(); + if (ctx.player() != null && didBreakLastTick) { + if (!ctx.playerController().hasBrokenBlock()) { + // insane bypass to check breaking succeeded + ctx.playerController().setHittingBlock(true); + } + ctx.playerController().resetBlockRemoving(); + didBreakLastTick = false; } } - public void tick(boolean isLeftClick) { - RayTraceResult trace = playerContext.objectMouseOver(); + RayTraceResult trace = ctx.objectMouseOver(); boolean isBlockTrace = trace != null && trace.type == RayTraceResult.Type.BLOCK; if (isLeftClick && isBlockTrace) { - tryBreakBlock(trace.getBlockPos(), trace.sideHit); + if (!didBreakLastTick) { + ctx.playerController().syncHeldItem(); + ctx.playerController().clickBlock(trace.getBlockPos(), trace.sideHit); + ctx.player().swingArm(EnumHand.MAIN_HAND); + } + + // Attempt to break the block + if (ctx.playerController().onPlayerDamageBlock(trace.getBlockPos(), trace.sideHit)) { + ctx.player().swingArm(EnumHand.MAIN_HAND); + } + + ctx.playerController().setHittingBlock(false); + didBreakLastTick = true; } else if (didBreakLastTick) { stopBreakingBlock(); diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index 1ffb0e1fc..a0b34c336 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -25,10 +25,11 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.math.RayTraceResult; public class BlockPlaceHelper implements Helper { + private final IPlayerContext ctx; private int rightClickTimer; - public BlockPlaceHelper(IPlayerContext playerContext) { + BlockPlaceHelper(IPlayerContext playerContext) { this.ctx = playerContext; } diff --git a/src/main/java/baritone/utils/GuiClick.java b/src/main/java/baritone/utils/GuiClick.java index 0765b0b5c..60663d577 100644 --- a/src/main/java/baritone/utils/GuiClick.java +++ b/src/main/java/baritone/utils/GuiClick.java @@ -22,10 +22,15 @@ 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; import net.minecraft.util.math.*; +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.opengl.GL11; @@ -34,6 +39,7 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.Collections; +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; import static org.lwjgl.opengl.GL11.*; public class GuiClick extends GuiScreen { @@ -74,7 +80,16 @@ public class GuiClick extends GuiScreen { public boolean mouseReleased(double mouseX, double 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)); @@ -111,7 +126,7 @@ public class GuiClick extends GuiScreen { GlStateManager.disableDepthTest(); 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.enableDepthTest(); 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 000000000..ce444cb9f --- /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.color4f(colorComponents[0], colorComponents[1], colorComponents[2], alpha); + } + + static void startLines(Color color, float alpha, float lineWidth, boolean ignoreDepth) { + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + glColor(color, alpha); + GlStateManager.lineWidth(lineWidth); + GlStateManager.disableTexture2D(); + GlStateManager.depthMask(false); + + if (ignoreDepth) { + GlStateManager.disableDepthTest(); + } + } + + static void startLines(Color color, float lineWidth, boolean ignoreDepth) { + startLines(color, .4f, lineWidth, ignoreDepth); + } + + static void endLines(boolean ignoredDepth) { + if (ignoredDepth) { + GlStateManager.enableDepthTest(); + } + + 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 4bd7982b3..5f393059f 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 db66b8ba6..b2a87b02a 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; @@ -52,19 +49,18 @@ import static org.lwjgl.opengl.GL11.*; * @author Brady * @since 8/9/2018 */ -public final class PathRenderer implements Helper { +public final class PathRenderer implements IRenderer { private static final ResourceLocation TEXTURE_BEACON_BEAM = new ResourceLocation("textures/entity/beacon_beam.png"); - private static final Tessellator TESSELLATOR = Tessellator.getInstance(); - private static final BufferBuilder BUFFER = TESSELLATOR.getBuffer(); + 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().getDimension().getType().getId(); @@ -75,7 +71,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"); @@ -84,19 +80,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); @@ -104,60 +101,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.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderPathIgnoreDepth.value) { - GlStateManager.disableDepthTest(); - } - 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,106 +154,60 @@ public final class PathRenderer implements Helper { } alpha = 0.4F * (1.0F - (float) (i - fadeStart) / (float) (fadeEnd - fadeStart)); } - GlStateManager.color4f(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.enableDepthTest(); - } - //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.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - GlStateManager.color4f(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); - GlStateManager.lineWidth(Baritone.settings().pathRenderLineWidthPixels.value); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.value) { - GlStateManager.disableDepthTest(); - } - + 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); VoxelShape shape = state.getShape(player.world, pos); AxisAlignedBB toDraw = shape.isEmpty() ? VoxelShapes.fullCube().getBoundingBox() : shape.getBoundingBox(); toDraw = toDraw.offset(pos); - drawAABB(toDraw); + IRenderer.drawAABB(toDraw, .002D); }); - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.value) { - GlStateManager.enableDepthTest(); - } - - 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(); @@ -288,12 +230,13 @@ 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(TEXTURE_BEACON_BEAM); + TileEntityBeaconRenderer a; - if (Baritone.settings().renderGoalIgnoreDepth.value) { + Helper.mc.getTextureManager().bindTexture(TEXTURE_BEACON_BEAM); + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.disableDepthTest(); } @@ -313,7 +256,7 @@ public final class PathRenderer implements Helper { 0.25D ); - if (Baritone.settings().renderGoalIgnoreDepth.value) { + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.enableDepthTest(); } @@ -335,12 +278,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; @@ -349,46 +295,34 @@ public final class PathRenderer implements Helper { return; } - GlStateManager.enableBlend(); - GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - GlStateManager.color4f(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.6F); - GlStateManager.lineWidth(Baritone.settings().goalRenderLineWidthPixels.value); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderGoalIgnoreDepth.value) { - GlStateManager.disableDepthTest(); - } + 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(); - if (Baritone.settings().renderGoalIgnoreDepth.value) { - GlStateManager.enableDepthTest(); - } - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); + 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(); + + 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 1e8bfe6a4..c43166e98 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 14d921d0f..a83e53a1e 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 f41e0f629..22e2063ae 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 6fd032550..f61b038af 100644 --- a/src/main/java/baritone/utils/ToolSet.java +++ b/src/main/java/baritone/utils/ToolSet.java @@ -37,6 +37,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. @@ -83,30 +84,39 @@ public class ToolSet { return itemStack.getItem() instanceof ItemTool ? 1 : -1; } + public boolean hasSilkTouch(ItemStack stack) { + return EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0; + } + /** * Calculate which tool on the hotbar is best for mining * * @param b the blockstate to be mined * @return A byte containing the index in the tools array that worked best */ - public byte getBestSlot(Block b) { + public byte getBestSlot(Block b, boolean preferSilkTouch) { byte best = 0; - double value = Double.NEGATIVE_INFINITY; - int materialCost = Integer.MIN_VALUE; + double highestSpeed = Double.NEGATIVE_INFINITY; + int lowestCost = Integer.MIN_VALUE; + boolean bestSilkTouch = false; IBlockState blockState = b.getDefaultState(); for (byte i = 0; i < 9; i++) { ItemStack itemStack = player.inventory.getStackInSlot(i); - double v = calculateSpeedVsBlock(itemStack, blockState); - if (v > value) { - value = v; + double speed = calculateSpeedVsBlock(itemStack, blockState); + boolean silkTouch = hasSilkTouch(itemStack); + if (speed > highestSpeed) { + highestSpeed = speed; best = i; - materialCost = getMaterialCost(itemStack); - } else if (v == value) { - int c = getMaterialCost(itemStack); - if (c < materialCost) { - value = v; + lowestCost = getMaterialCost(itemStack); + bestSilkTouch = silkTouch; + } else if (speed == highestSpeed) { + int cost = getMaterialCost(itemStack); + if ((cost < lowestCost && (silkTouch || !bestSilkTouch)) || + (preferSilkTouch && !bestSilkTouch && silkTouch)) { + highestSpeed = speed; best = i; - materialCost = c; + lowestCost = cost; + bestSilkTouch = silkTouch; } } } @@ -120,7 +130,7 @@ public class ToolSet { * @return A double containing the destruction ticks with the best tool */ private double getBestDestructionTime(Block b) { - ItemStack stack = player.inventory.getStackInSlot(getBestSlot(b)); + ItemStack stack = player.inventory.getStackInSlot(getBestSlot(b, false)); return calculateSpeedVsBlock(stack, b.getDefaultState()) * avoidanceMultiplier(b); } 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 000000000..81501036b --- /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 000000000..7c47a9b80 --- /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 19f146854..e5fde40b4 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 new file mode 100644 index 000000000..354b9eefc --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IPlayerControllerMP.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.utils.accessor; + +import net.minecraft.util.math.BlockPos; + +public interface IPlayerControllerMP { + + void setIsHittingBlock(boolean isHittingBlock); + + BlockPos getCurrentBlock(); + + void callSyncCurrentPlayItem(); +} 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 000000000..e187c3e6c --- /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 000000000..86e667b7d --- /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).getA()).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.getA(); + String rest = msg.substring(pair.getA().length()); + ArgConsumer argc = new ArgConsumer(this.manager, pair.getB()); + 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.getA())) { + 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.getA()) != 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 000000000..ae52bfb25 --- /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 000000000..21551782b --- /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 000000000..9212bd36c --- /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(mc.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 000000000..a4ef4cc7b --- /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 000000000..d4101a629 --- /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/ClickCommand.java b/src/main/java/baritone/utils/command/defaults/ClickCommand.java new file mode 100644 index 000000000..e5180b2ff --- /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 000000000..bbefeb43a --- /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 000000000..cbd8ac77c --- /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 000000000..3f8e39087 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/DefaultCommands.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.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 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 000000000..dc156d0e0 --- /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 000000000..4d75d1b49 --- /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 000000000..1f803ec60 --- /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 000000000..53046b159 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FindCommand.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.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 net.minecraft.util.registry.IRegistry; + +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( + IRegistry.BLOCK.getKey(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 000000000..aa2d93ef1 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FollowCommand.java @@ -0,0 +1,156 @@ +/* + * 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.EntityLiving; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.IRegistry; + +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 EntityType) { + //noinspection unchecked + classes.add((EntityType) gotten); + } else { + entities.add((Entity) gotten); + } + } + baritone.getFollowProcess().follow( + classes.isEmpty() + ? entities::contains + : e -> classes.stream().anyMatch(c -> c.getEntityClass().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(IRegistry.ENTITY_TYPE::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 000000000..3de8a056b --- /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 000000000..c2e7952cd --- /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 000000000..3f7601117 --- /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 000000000..a9d0e1047 --- /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 000000000..f52e6226c --- /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 000000000..e77c3bd75 --- /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 000000000..4cd766ea1 --- /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 000000000..bc3c392ac --- /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 000000000..40d8e72b5 --- /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 000000000..c16000f73 --- /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 000000000..66926d23f --- /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.worldRenderer.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 000000000..bd918f097 --- /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 000000000..8343f7533 --- /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 000000000..91e31daa4 --- /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 000000000..d9899f429 --- /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 000000000..48c4580f5 --- /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 000000000..4c6f35226 --- /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 000000000..588287d64 --- /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 000000000..fe53f523e --- /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 000000000..01f5817e9 --- /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 000000000..1e8fe8382 --- /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 000000000..82f25a4a4 --- /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.getA(); + List args = pair.getB(); + 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.getA(); + ArgConsumer args = new ArgConsumer(this, expanded.getB()); + + 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 11b4a73f4..ca377fb07 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 56ccb0069..bbc23c7ff 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 b6e1ba8a2..17fc6ee2d 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 8bac2dc77..de10fdf3c 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/player/PrimaryPlayerController.java b/src/main/java/baritone/utils/player/PrimaryPlayerController.java index 120f996cc..b013f9161 100644 --- a/src/main/java/baritone/utils/player/PrimaryPlayerController.java +++ b/src/main/java/baritone/utils/player/PrimaryPlayerController.java @@ -19,6 +19,7 @@ package baritone.utils.player; import baritone.api.utils.Helper; import baritone.api.utils.IPlayerController; +import baritone.utils.accessor.IPlayerControllerMP; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.entity.player.EntityPlayer; @@ -42,6 +43,16 @@ public enum PrimaryPlayerController implements IPlayerController, Helper { INSTANCE; + @Override + public void syncHeldItem() { + ((IPlayerControllerMP) mc.playerController).callSyncCurrentPlayItem(); + } + + @Override + public boolean hasBrokenBlock() { + return ((IPlayerControllerMP) mc.playerController).getCurrentBlock().getY() == -1; + } + @Override public boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side) { return mc.playerController.onPlayerDamageBlock(pos, side); @@ -57,11 +68,6 @@ public enum PrimaryPlayerController implements IPlayerController, Helper { return mc.playerController.windowClick(windowId, slotId, mouseButton, type, player); } - @Override - public void setGameType(GameType type) { - mc.playerController.setGameType(type); - } - @Override public GameType getGameType() { return mc.playerController.getCurrentGameType(); @@ -69,7 +75,6 @@ public enum PrimaryPlayerController implements IPlayerController, Helper { @Override public EnumActionResult processRightClickBlock(EntityPlayerSP player, World world, BlockPos pos, EnumFacing direction, Vec3d vec, EnumHand hand) { - // primaryplayercontroller is always in a WorldClient so this is ok return mc.playerController.processRightClickBlock(player, (WorldClient) world, pos, direction, vec, hand); } @@ -77,4 +82,14 @@ public enum PrimaryPlayerController implements IPlayerController, Helper { public EnumActionResult processRightClick(EntityPlayerSP player, World world, EnumHand hand) { return mc.playerController.processRightClick(player, world, hand); } + + @Override + public boolean clickBlock(BlockPos loc, EnumFacing face) { + return mc.playerController.clickBlock(loc, face); + } + + @Override + public void setHittingBlock(boolean hittingBlock) { + ((IPlayerControllerMP) mc.playerController).setIsHittingBlock(hittingBlock); + } } 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 fd253105e..3216f59ff 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 134815239..1aec63dc7 100644 --- a/src/main/java/baritone/utils/schematic/MapArtSchematic.java +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -58,8 +58,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 9e4b67cd5..e2b8042b7 100644 --- a/src/main/java/baritone/utils/schematic/Schematic.java +++ b/src/main/java/baritone/utils/schematic/Schematic.java @@ -21,7 +21,10 @@ import baritone.api.utils.ISchematic; 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 fd0ace8ca..382790313 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 63b85670b..bacf1e9d2 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 280b38a6a..4c9da008c 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 7d786dbd4..415399df1 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 6430dbfcc..bbf114285 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 80c1b2a21..25e5c2807 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 191d01109..952f86a1b 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 cce61e4b3..9bfdafb0d 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 c30b45d11..d05a323bc 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()) {