diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..176a458f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 00000000..55e5ad06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,25 @@ +--- +name: Bug report +about: Please file a separate report for each issue +title: Please add a brief but descriptive title +labels: bug +assignees: '' +--- + +## Some information +Operating system: +Java version: +Minecraft version: +Baritone version: +Forge mods (if used): + +## Exception, error or logs +You can find your logs in `%appdata%/.minecraft/logs/` (Windows) or `/Library/Application\ Support/minecraft/logs` (Mac). + +## How to reproduce +Add your steps to reproduce the issue/bug experienced here. + +## Final checklist +- [ ] I have included the version of Minecraft I'm running, baritone's version and forge mods (if used). +- [ ] I have included logs, exceptions and / or steps to reproduce the issue. +- [ ] I have not used any OwO's or UwU's in this issue. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 00000000..2482fd4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,13 @@ +--- +name: Question +about: Please file a separate report for each question +title: Please add a brief but descriptive title +labels: question +assignees: '' +--- + +## What do you need help with? +With as much detail as possible, describe your question and what you may need help with. + +## Final checklist +- [ ] I have not used any OwO's or UwU's in this issue. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/suggestion.md b/.github/ISSUE_TEMPLATE/suggestion.md new file mode 100644 index 00000000..da61c638 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/suggestion.md @@ -0,0 +1,19 @@ +--- +name: Suggestion +about: Please file a separate report for each suggestion +title: Please add a brief but descriptive title +labels: enhancement +assignees: '' +--- + +## Describe your suggestion +With as much detail as possible, describe what your suggestion would do for Baritone. + +## Settings +If applicable, what settings/customizability should be offered to tweak the functionality of your suggestion. + +## Context +Describe how your suggestion would improve Baritone, or the reason behind it being added. + +## Final checklist +- [ ] I have not used any OwO's or UwU's in this issue. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..e9a3c7cc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ + diff --git a/.gitignore b/.gitignore index de408ec0..bd3b054a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,10 @@ classes/ # IntelliJ Files .idea/ *.iml +*.ipr +*.iws /logs/ # Copyright Files !/.idea/copyright/Baritone.xml -!/.idea/copyright/profiles_settings.xml \ No newline at end of file +!/.idea/copyright/profiles_settings.xml diff --git a/.travis.yml b/.travis.yml index a8344ba0..371639be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ install: - travis_retry docker build -t cabaletta/baritone . script: +- docker run --rm cabaletta/baritone ./gradlew javadoc - docker run --name baritone cabaletta/baritone /bin/sh -c "set -e; /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 128x128x24 -ac +extension GLX +render; DISPLAY=:99 BARITONE_AUTO_TEST=true ./gradlew runClient" - docker cp baritone:/code/dist dist - ls dist diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..474ecf7e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,83 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* No Anime (including uwu's or owo's) +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Giving and gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* Anime +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* ~~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 or consent +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +Project maintainers have the right and responsibility to immediately remove +without any sort of dispute any issues or pull requests that do not align +with their corresponding templates. Absolutely no leniancy shall be accepted +with these terms. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at complaints@leijurv.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Dockerfile b/Dockerfile index bec43d69..020bea0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,16 @@ -FROM debian:jessie +FROM debian:stretch -RUN echo 'deb http://deb.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/jessie-backports.list +RUN echo 'deb http://deb.debian.org/debian stretch-backports main' > /etc/apt/sources.list.d/stretch-backports.list ENV DEBIAN_FRONTEND noninteractive RUN apt update -y -RUN apt install --target-release jessie-backports \ +RUN apt install \ openjdk-8-jdk \ - ca-certificates-java \ --assume-yes -RUN apt install -qq --force-yes mesa-utils libgl1-mesa-glx libxcursor1 libxrandr2 libxxf86vm1 x11-xserver-utils xfonts-base xserver-common +RUN apt install -qq --assume-yes mesa-utils libgl1-mesa-glx libxcursor1 libxrandr2 libxxf86vm1 x11-xserver-utils xfonts-base xserver-common COPY . /code @@ -21,4 +20,4 @@ WORKDIR /code # source: https://github.com/tectonicus/tectonicus/issues/60#issuecomment-154239173 RUN dpkg -i scripts/xvfb_1.16.4-1_amd64.deb -RUN ./gradlew build \ No newline at end of file +RUN ./gradlew build diff --git a/FEATURES.md b/FEATURES.md index cd2a90fe..372a7741 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -4,13 +4,14 @@ - **Block breaking** Baritone considers breaking blocks as part of its path. It also takes into account your current tool set and hot bar. For example, if you have a Eff V diamond pick, it may choose to mine through a stone barrier, while if you only had a wood pick it might be faster to climb over it. - **Block placing** Baritone considers placing blocks as part of its path. This includes sneak-back-placing, pillaring, etc. It has a configurable penalty of placing a block (set to 1 second by default), to conserve its resources. The list of acceptable throwaway blocks is also configurable, and is cobble, dirt, or netherrack by default. Example - **Falling** Baritone will fall up to 3 blocks onto solid ground (configurable, if you have Feather Falling and/or don't mind taking a little damage). If you have a water bucket on your hotbar, it will fall up to 23 blocks and place the bucket beneath it. It will fall an unlimited distance into existing still water. -- **Vines and ladders** Baritone understands how to climb and descend vines and ladders. There is experimental support for more advanced maneuvers, like strafing to a different ladder / vine column in midair (off by default, setting named `allowVines`). +- **Vines and ladders** Baritone understands how to climb and descend vines and ladders. There is experimental support for more advanced maneuvers, like strafing to a different ladder / vine column in midair (off by default, setting named `allowVines`). Baritone can break its fall by grabbing ladders / vines midair, and understands when that is and isn't possible. - **Opening fence gates and doors** - **Slabs and stairs** - **Falling blocks** Baritone understands the costs of breaking blocks with falling blocks on top, and includes all of their break costs. Additionally, since it avoids breaking any blocks touching a liquid, it won't break the bottom of a gravel stack below a lava lake (anymore). - **Avoiding dangerous blocks** Obviously, it knows not to walk through fire or on magma, not to corner over lava (that deals some damage), not to break any blocks touching a liquid (it might drown), etc. - **Parkour** Sprint jumping over 1, 2, or 3 block gaps - **Parkour place** Sprint jumping over a 3 block gap and placing the block to land on while executing the jump. It's really cool. +- **Pigs** It can sort of control pigs. I wouldn't rely on it though. # Pathing method Baritone uses A*, with some modifications: @@ -21,9 +22,9 @@ Baritone uses A*, with some modifications: - **Backtrack cost favoring** While calculating the next segment, Baritone favors backtracking its current segment. The cost is decreased heavily, but is still positive (this won't cause it to backtrack if it doesn't need to). This allows it to splice and jump onto the next segment as early as possible, if the next segment begins with a backtrack of the current one. Example - **Backtrack detection and pausing** While path calculation happens on a separate thread, the main game thread has access to the latest node considered, and the best path so far (those are rendered light blue and dark blue respectively). When the current best path (rendered dark blue) passes through the player's current position on the current path segment, path execution is paused (if it's safe to do so), because there's no point continuing forward if we're about to turn around and go back that same way. Note that the current best path as reported by the path calculation thread takes into account the incremental cost backoff system, so it's accurate to what the path calculation thread will actually pick once it finishes. -# Configuring Baritone -All the settings and documentation are here. -To change a boolean setting, just say its name in chat (for example saying `allowBreak` toggles whether Baritone will consider breaking blocks). For a numeric setting, say its name then the new value (like `pathTimeoutMS 250`). It's case insensitive. +# Chat control + +- [Baritone chat control usage](USAGE.md) # Goals The pathing goal can be set to any of these options: @@ -46,7 +47,6 @@ Things it doesn't have yet See issues for more. Things it may not ever have, from most likely to least likely =( -- Pigs - Boats - Horses (2x3 path instead of 1x2) - Elytra diff --git a/IMPACT.md b/IMPACT.md deleted file mode 100644 index cc12252e..00000000 --- a/IMPACT.md +++ /dev/null @@ -1 +0,0 @@ -Impact 4.4 is out. See INSTALL.md \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 9df3d6bf..00000000 --- a/INSTALL.md +++ /dev/null @@ -1,96 +0,0 @@ -# Integration between Baritone and Impact -Impact 4.4 has Baritone included. - -These instructions apply to Impact 4.3 (and potentially other hacked clients). - -To run Baritone on Vanilla, just follow the instructions in the README (it's `./gradlew runClient`). - - -## An Introduction -There are some basic steps to getting Baritone setup with Impact. -- Acquiring a build of Baritone -- Placing Baritone in the libraries directory -- Modifying the Impact Profile JSON to run baritone -- How to use Baritone - -## Acquiring a build of Baritone -There are two methods of acquiring a build of Baritone - -### Official Release (Not always up to date) -https://github.com/cabaletta/baritone/releases - -For Impact 4.3, there is no Baritone integration yet, so you will want `baritone-standalone-X.Y.Z.jar`. - -Any official release will be GPG signed by leijurv (44A3EA646EADAC6A) and ZeroMemes (73A788379A197567). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by those two public keys of `checksums.txt`. - -The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone . && docker run --rm cabaletta/baritone cat /code/dist/checksums.txt` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too). - -### Building Baritone yourself -There are a few steps to this -- Clone this repository -- Setup the project as instructed in the README -- Run the ``build`` gradle task. You can either do this using IntelliJ's gradle UI or through a -command line - - Windows: ``gradlew build`` - - Mac/Linux: ``./gradlew build`` -- The build should be exported into ``/build/libs/baritone-X.Y.Z.jar`` - -## Placing Baritone in the libraries directory -``/libraries`` is a neat directory in your Minecraft Installation Directory -that contains all of the dependencies that are required from the game and some mods. This is where we will be -putting baritone. -- Locate the ``libraries`` folder, it should be in the Minecraft Installation Directory -- Create 3 new subdirectories starting from ``libraries`` - - ``cabaletta`` - - ``baritone`` - - ``X.Y.Z`` - - Copy the build of Baritone that was acquired earlier, and place it into the ``X.Y.Z`` folder - - The full path should look like ``/libraries/cabaletta/baritone/X.Y.Z/baritone-X.Y.Z.jar`` - -## Modifying the Impact Profile JSON to run baritone -The final step is "registering" the Baritone library with Impact, so that it loads on launch. -- Ensure your Minecraft launcher is closed -- Navigate back to the Minecraft Installation Directory -- Find the ``versions`` directory, and open in -- In here there should be a ``1.12.2-Impact_4.3`` folder. - - If you don't have any Impact folder or have a version older than 4.3, you can download Impact here. -- Open the folder and inside there should be a file called ``1.12.2-Impact_4.3.json`` -- Open the JSON file with a text editor that supports your system's line endings - - For example, Notepad on Windows likely will NOT work for this. You should instead use a Text Editor like - Notepad++ if you're on Windows. (For other systems, I'm not sure - what would work the best so you may have to do some research.) -- Find the ``libraries`` array in the JSON. It should look something like this. - ``` - "libraries": [ - { - "name": "net.minecraft:launchwrapper:1.12" - }, - { - "name": "com.github.ImpactDevelopment:Impact:4.3-1.12.2", - "url": "https://impactdevelopment.github.io/maven/" - }, - { - "name": "com.github.ImpactDeveloment:ClientAPI:3.0.2", - "url": "https://impactdevelopment.github.io/maven/" - }, - ... - ``` -- Create two new objects in the array, between the ``Impact`` and ``ClientAPI`` dependencies preferably. - ``` - { - "name": "cabaletta:baritone:X.Y.Z" - }, - { - "name": "com.github.ImpactDevelopment:SimpleTweaker:1.2", - "url": "https://impactdevelopment.github.io/maven/" - }, - ``` -- Now find the ``"minecraftArguments": "..."`` text near the top. -- At the very end of the quotes where it says ``--tweakClass clientapi.load.ClientTweaker"``, add on the following so it looks like: - - ``--tweakClass clientapi.load.ClientTweaker --tweakClass baritone.launch.BaritoneTweakerOptifine"`` -- If you didn't close your launcher for this step, restart it now. -- You can now launch Impact 4.3 as normal, and Baritone should start up - - ## How to use Baritone - Instructions on how to use Baritone are limited, and you may have to read a little bit of code (Really nothing much - just plain English), you can view that here. diff --git a/LICENSE-Part-2.jpg b/LICENSE-Part-2.jpg new file mode 100644 index 00000000..d087ee7e Binary files /dev/null and b/LICENSE-Part-2.jpg differ diff --git a/README.md b/README.md index 6bffdc24..19ec0e87 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,72 @@ # Baritone -[![Build Status](https://travis-ci.com/cabaletta/baritone.svg?branch=master)](https://travis-ci.com/cabaletta/baritone) -[![Release](https://img.shields.io/github/release/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/releases) -[![License](https://img.shields.io/badge/license-LGPL--3.0-green.svg)](LICENSE) +[![Build Status](https://travis-ci.com/cabaletta/baritone.svg?branch=master)](https://travis-ci.com/cabaletta/baritone/) +[![Release](https://img.shields.io/github/release/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/releases/) +[![License](https://img.shields.io/badge/license-LGPL--3.0%20with%20anime%20exception-green.svg)](LICENSE) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a73d037823b64a5faf597a18d71e3400)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade) -[![HitCount](http://hits.dwyl.com/cabaletta/baritone.svg)](http://hits.dwyl.com/cabaletta/baritone) +[![HitCount](http://hits.dwyl.com/cabaletta/baritone.svg)](http://hits.dwyl.com/cabaletta/baritone/) +[![GitHub All Releases](https://img.shields.io/github/downloads/cabaletta/baritone/total.svg)](https://github.com/cabaletta/baritone/releases/) +[![Minecraft](https://img.shields.io/badge/MC-1.12.2-brightgreen.svg)](https://github.com/cabaletta/baritone/tree/master/) +[![Minecraft](https://img.shields.io/badge/MC-1.13.2-brightgreen.svg)](https://github.com/cabaletta/baritone/tree/1.13.2/) +[![Minecraft](https://img.shields.io/badge/MC-1.14.4-brightgreen.svg)](https://github.com/cabaletta/baritone/tree/1.14.4/) +[![Code of Conduct](https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?style=flat)](https://github.com/cabaletta/baritone/blob/master/CODE_OF_CONDUCT.md) [![Known Vulnerabilities](https://snyk.io/test/github/cabaletta/baritone/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/cabaletta/baritone?targetFile=build.gradle) -[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/cabaletta/baritone/issues) -[![Minecraft](https://img.shields.io/badge/MC-1.12.2-green.svg)](https://minecraft.gamepedia.com/1.12.2) -[![Impact integration](https://img.shields.io/badge/Impact%20integration-v1.0.0--hotfix--4-brightgreen.svg)](https://impactdevelopment.github.io/) -[![KAMI integration](https://img.shields.io/badge/KAMI%20integration-v1.0.0-orange.svg)](https://github.com/zeroeightysix/KAMI/) -[![Asuna integration](https://img.shields.io/badge/Asuna%20integration-v1.0.0-orange.svg)](https://github.com/EmotionalLove/Asuna/) -[![Future integration](https://img.shields.io/badge/Future%20integration-%3F%3F%3F-red.svg)](https://futureclient.net/) +[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/cabaletta/baritone/issues/) +[![Issues](https://img.shields.io/github/issues/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/issues/) +[![GitHub issues-closed](https://img.shields.io/github/issues-closed/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/issues?q=is%3Aissue+is%3Aclosed) +[![Pull Requests](https://img.shields.io/github/issues-pr/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/pulls/) +![Code size](https://img.shields.io/github/languages/code-size/cabaletta/baritone.svg) +![GitHub repo size](https://img.shields.io/github/repo-size/cabaletta/baritone.svg) +![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%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%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/) +[![forthebadge](https://forthebadge.com/images/badges/mom-made-pizza-rolls.svg)](http://forthebadge.com/) 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://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). This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), -the original version of the bot for Minecraft 1.8, rebuilt for 1.12.2. Baritone focuses on reliability and particularly performance (it's over [29x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths). +the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 and 1.13.2. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths). + +Have committed at least once a day from Aug 1 2018 to Aug 1 2019. + +1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2 + +# Getting Started Here are some links to help to get started: - [Features](FEATURES.md) -- [Installation](INSTALL.md) +- [Installation & setup](SETUP.md) -# Setup +- [API Javadocs](https://baritone.leijurv.com/) -## Command Line -On Mac OSX and Linux, use `./gradlew` instead of `gradlew`. +- [Settings](https://baritone.leijurv.com/baritone/api/Settings.html#field.detail) -Running Baritone: +- [Usage (chat control)](USAGE.md) -``` -$ gradlew runClient -``` +# API -Building Baritone: -``` -$ gradlew build -``` +The API is heavily documented, you can find the Javadocs for the latest release [here](https://baritone.leijurv.com/). +Please note that usage of anything located outside of the ``baritone.api`` package is not supported by the API release +jar. -To replace out Impact 4.4's Baritone build with a customized one, switch to the `impact4.4-compat` branch, build Baritone as above then copy `dist/baritone-api-$VERSION$.jar` into `minecraft/libraries/cabaletta/baritone-api/1.0.0/baritone-api-1.0.0.jar`, replacing the jar that was previously there. You also need to edit `minecraft/versions/1.12.2-Impact_4.4/1.12.2-Impact_4.4.json`, find the line `"name": "cabaletta:baritone-api:1.0.0"`, remove the comma from the end, and entirely remove the line that's immediately after (starts with `"url"`). - -## IntelliJ's Gradle UI -- Open the project in IntelliJ as a Gradle project -- Run the Gradle tasks `setupDecompWorkspace` then `genIntellijRuns` -- Refresh the Gradle project (or, to be safe, just restart IntelliJ) -- Select the "Minecraft Client" launch config -- In `Edit Configurations...` you may need to select `baritone_launch` for `Use classpath of module:`. - -# Chat control -[Defined Here](src/main/java/baritone/utils/ExampleBaritoneControl.java) - -Quick start example: `thisway 1000` or `goal 70` to set the goal, `path` to actually start pathing. Also try `mine diamond_ore`. `cancel` to cancel. - -# API example +Below is an example of basic usage for changing some settings, and then pathing to a X/Z goal. ``` BaritoneAPI.getSettings().allowSprint.value = true; -BaritoneAPI.getSettings().pathTimeoutMS.value = 2000L; +BaritoneAPI.getSettings().primaryTimeoutMS.value = 2000L; BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalXZ(10000, 20000)); ``` @@ -68,11 +75,11 @@ BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAnd ## Can I use Baritone as a library in my custom utility client? -Sure! (As long as usage is in compliance with the LGPL 3 License) +That's what it's for, sure! (As long as usage is in compliance with the LGPL 3.0 License) ## How is it so fast? -Magic. (Hours of [Leijurv](https://github.com/leijurv) enduring excruciating pain) +Magic. (Hours of [leijurv](https://github.com/leijurv/) enduring excruciating pain) ## Why is it called Baritone? diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 00000000..1fcaf1d2 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,106 @@ +# Installation + +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). + +Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. + +## Prebuilt official releases +These releases are not always completely up to date with latest features, and are only released from `master`. (so if you want `backfill-2` branch for example, you'll have to build it yourself) + +Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases) + +v1.2.* is for 1.12.2, v1.3.* is for 1.13.2 + +Any official release will be GPG signed by leijurv (44A3EA646EADAC6A) and ZeroMemes (73A788379A197567). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by those two public keys of `checksums.txt`. + +The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too). + + +## Artifacts + +Building Baritone will result in 5 artifacts created in the ``dist`` directory. These are the same as the artifacts created in the [releases](https://github.com/cabaletta/baritone/releases). + +**The Forge release can simply be added as a Forge mod.** + +If another one of your Forge mods has a Baritone integration, you want `baritone-api-forge-VERSION.jar`. Otherwise, you want `baritone-standalone-forge-VERSION.jar` + +- **API**: Only the non-api packages are obfuscated. This should be used in environments where other mods would like to use Baritone's features. +- **Forge API**: Same as API, but packaged for Forge. This should be used where another mod has a Baritone integration. +- **Standalone**: Everything is obfuscated. This should be used in environments where there are no other mods present that would like to use Baritone's features. +- **Forge Standalone**: Same as Standalone, but packaged for Forge. This should be used when Baritone is your only Forge mod, or none of your other Forge mods integrate with Baritone. +- **Unoptimized**: Nothing is obfuscated. This shouldn't be used ever in production. + +## More Info +To replace out Impact 4.5's Baritone build with a customized one, build Baritone as above then copy & **rename** `dist/baritone-api-$VERSION$.jar` into `minecraft/libraries/cabaletta/baritone-api/1.2/baritone-api-1.2.jar`, replacing the jar that was previously there. You also need to edit `minecraft/versions/1.12.2-Impact_4.5/1.12.2-Impact_4.5.json`, find the line `"name": "cabaletta:baritone-api:1.2"`, remove the comma from the end, and **entirely remove the NEXT line** (starts with `"url"`). **Restart your launcher** then load as normal. + +You can verify whether or not it worked by running `.b version` in chat (only valid in Impact). It should print out the version that you downloaded. Note: The version that comes with 4.5 is `v1.2.3`. + +## Build it yourself +- Clone or download Baritone + + ![Image](https://i.imgur.com/kbqBtoN.png) + - If you choose to download, make sure you extract the ZIP archive. +- Follow one of the instruction sets below, based on your preference + +## Command Line +On Mac OSX and Linux, use `./gradlew` instead of `gradlew`. + +Setting up the Environment: + +``` +$ gradlew setupDecompWorkspace +$ gradlew --refresh-dependencies +``` + +Running Baritone: + +``` +$ gradlew runClient +``` + +For information on how to build baritone, see [Building Baritone](#building-baritone) + +## IntelliJ +- Open the project in IntelliJ as a Gradle project + + ![Image](https://i.imgur.com/jw7Q6vY.png) + +- Run the Gradle tasks `setupDecompWorkspace` then `genIntellijRuns` + + ![Image](https://i.imgur.com/QEfVvWP.png) + +- Refresh the Gradle project (or, to be safe, just restart IntelliJ) + + ![Image](https://i.imgur.com/3V7EdWr.png) + +- Select the "Minecraft Client" launch config + + ![Image](https://i.imgur.com/1qz2QGV.png) + +- Click on ``Edit Configurations...`` from the same dropdown and select the "Minecraft Client" config + + ![Image](https://i.imgur.com/s4ly0ZF.png) + +- In `Edit Configurations...` you need to select `baritone_launch` for `Use classpath of module:`. + + ![Image](https://i.imgur.com/hrLhG9u.png) + +# Building + +Make sure that you have properly [setup](#setup) the environment before trying to build it. + +## Command Line + +``` +$ gradlew build +``` + +## IntelliJ + +- Navigate to the gradle tasks on the right tab as follows + + ![Image](https://i.imgur.com/PE6r9iN.png) + +- Double click on **build** to run it diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 00000000..25fef11e --- /dev/null +++ b/USAGE.md @@ -0,0 +1,87 @@ +(assuming you already have Baritone [set up](SETUP.md)) + +# Prefix + +Baritone's chat control prefix is `#` by default. In Impact, you can also use `.b` as a prefix. (for example, `.b click` instead of `#click`) + +Baritone commands can also by default be typed in the chatbox. However if you make a typo, like typing "gola 10000 10000" instead of "goal" it goes into public chat, which is bad, so using `#` is suggested. + +To disable direct chat control (with no prefix), turn off the `chatControl` setting. To disable chat control with the `#` prefix, turn off the `prefixControl` setting. In Impact, `.b` cannot be disabled. Be careful that you don't leave yourself with all control methods disabled (if you do, reset your settings by deleting the file `minecraft/baritone/settings.txt` and relaunching). + +# Commands + +**All** of these commands may need a prefix before them, as above ^. + +`help` for (rudimentary) help. You can see what it says [here](https://github.com/cabaletta/baritone/blob/master/src/api/java/baritone/api/utils/ExampleBaritoneControl.java#L47). + +To toggle a boolean setting, just say its name in chat (for example saying `allowBreak` toggles whether Baritone will consider breaking blocks). For a numeric setting, say its name then the new value (like `primaryTimeoutMS 250`). It's case insensitive. To reset a setting to its default value, say `acceptableThrowawayItems reset`. To reset all settings, say `reset`. To see all settings that have been modified from their default values, say `modified`. + +Some common examples: +- `thisway 1000` then `path` to go in the direction you're facing for a thousand blocks +- `goal x y z` or `goal x z` or `goal y`, then `path` to set a goal to a certain coordinate then path to it +- `goto x y z` or `goto x z` or `goto y` to go to a certain coordinate (in a single step, starts going immediately) +- `goal` to set the goal to your player's feet +- `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 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. +- `build` to build a schematic. `build blah` will load `schematics/blah.schematic` and build it with the origin being your player feet. `build blah x y z` to set the origin. Any of those can be relative to your player (`~ 69 ~-420` would build at x=player x, y=69, z=player z-420). +- `schematica` to build the schematic that is currently open in schematica +- `tunnel` to dig just straight ahead and make a tunnel +- `farm` to automatically harvest, replant, or bone meal crops +- `axis` to go to an axis or diagonal axis at y=120 (`axisHeight` is a configurable setting, defaults to 120). +- `explore x z` to explore the world from the origin of x,z. Leave out x and z to default to player feet. This will continually path towards the closest chunk to the origin that it's never seen before. `explorefilter filter.json` with optional invert can be used to load in a list of chunks to load. +- `invert` to invert the current goal and path. This gets as far away from it as possible, instead of as close as possible. For example, do `goal` then `invert` to run as far as possible from where you're standing at the start. +- `version` to get the version of Baritone you're running +- `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. + +There are about a hundred settings, but here are some fun / interesting / important ones that you might want to look at changing in normal usage of Baritone. The documentation for each can be found at the above links. +- `allowBreak` +- `allowSprint` +- `allowPlace` +- `allowParkour` +- `allowParkourPlace` +- `blockPlacementPenalty` +- `renderCachedChunks` (and `cachedChunksOpacity`) <-- very fun but you need a beefy computer +- `avoidance` (avoidance of mobs / mob spawners) +- `legitMine` +- `followRadius` +- `backfill` (fill in tunnels behind you) +- `buildInLayers` +- `buildRepeatDistance` and `buildRepeatDirection` +- `worldExploringChunkOffset` +- `acceptableThrowawayItems` +- `blocksToAvoidBreaking` + + + + +# Troubleshooting / common issues + +## Why doesn't Baritone respond to any of my chat commands? +This could be one of many things. + +First, make sure it's actually installed. An easy way to check is seeing if it created the folder `baritone` in your Minecraft folder. + +Second, make sure that you're using the prefix properly, and that chat control is enabled in the way you expect. + +For example, Impact disables direct chat control. (i.e. anything typed in chat without a prefix will be ignored and sent publicly). **This is a saved setting**, so if you run Impact once, `chatControl` will be off from then on, **even in other clients**. +So you'll need to use the `#` prefix or edit `baritone/settings.txt` in your Minecraft folder to undo that (specifically, remove the line `chatControl false` then restart your client). + + +## Why can I do `.goto x z` in Impact but nowhere else? Why can I do `-path to x z` in KAMI but nowhere else? +These are custom commands that they added; those aren't from Baritone. +The equivalent you're looking for is `goal x z` then `path`. diff --git a/build.gradle b/build.gradle index 37f312b5..d0ba4720 100755 --- a/build.gradle +++ b/build.gradle @@ -1,124 +1,140 @@ -/* - * This file is part of Baritone. - * - * Baritone is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Baritone is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Baritone. If not, see . - */ - -group 'baritone' -version '1.0.0-hotfix-2' - -buildscript { - repositories { - maven { - name = 'forge' - url = 'http://files.minecraftforge.net/maven' - } - maven { - name = 'SpongePowered' - url = 'http://repo.spongepowered.org/maven' - } - jcenter() - } - - dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' - classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' - } -} - - -import baritone.gradle.task.CreateDistTask -import baritone.gradle.task.ProguardTask - -apply plugin: 'java' -apply plugin: 'net.minecraftforge.gradle.tweaker-client' -apply plugin: 'org.spongepowered.mixin' - -sourceCompatibility = targetCompatibility = '1.8' -compileJava { - sourceCompatibility = targetCompatibility = '1.8' -} - -sourceSets { - comms {} - main { - compileClasspath += comms.compileClasspath + comms.output - } - launch { - compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output - } -} - -minecraft { - version = '1.12.2' - mappings = 'stable_39' - tweakClass = 'baritone.launch.BaritoneTweaker' - runDir = 'run' - - // The sources jar should use SRG names not MCP to ensure compatibility with all mappings - makeObfSourceJar = true -} - -repositories { - mavenCentral() - - maven { - name = 'spongepowered-repo' - url = 'http://repo.spongepowered.org/maven/' - } - - maven { - name = 'impactdevelopment-repo' - url = 'https://impactdevelopment.github.io/maven/' - } -} - -dependencies { - runtime launchCompile('com.github.ImpactDevelopment:SimpleTweaker:1.2') - runtime launchCompile('org.spongepowered:mixin:0.7.11-SNAPSHOT') { - // Mixin includes a lot of dependencies that are too up-to-date - exclude module: 'launchwrapper' - exclude module: 'guava' - exclude module: 'gson' - exclude module: 'commons-io' - exclude module: 'log4j-core' - } - testImplementation 'junit:junit:4.12' -} - -mixin { - defaultObfuscationEnv notch - add sourceSets.launch, 'mixins.baritone.refmap.json' -} - -javadoc { - source = sourceSets.api.allJava - classpath = sourceSets.api.compileClasspath -} - -jar { - from sourceSets.comms.output, sourceSets.launch.output, sourceSets.api.output - preserveFileTimestamps = false - reproducibleFileOrder = true -} - -task proguard(type: ProguardTask) { - url 'https://downloads.sourceforge.net/project/proguard/proguard/6.0/proguard6.0.3.zip' - extract 'proguard6.0.3/lib/proguard.jar' -} - -task createDist(type: CreateDistTask, dependsOn: proguard) - -build.finalizedBy(createDist) - +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +group 'baritone' +version '1.2.9' + +buildscript { + repositories { + maven { + name = 'forge' + url = 'http://files.minecraftforge.net/maven' + } + maven { + name = 'SpongePowered' + url = 'http://repo.spongepowered.org/maven' + } + jcenter() + } + + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' + classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' + } +} + + +import baritone.gradle.task.CreateDistTask +import baritone.gradle.task.ProguardTask + +apply plugin: 'java' +apply plugin: 'net.minecraftforge.gradle.tweaker-client' +apply plugin: 'org.spongepowered.mixin' + +sourceCompatibility = targetCompatibility = '1.8' +compileJava { + sourceCompatibility = targetCompatibility = '1.8' + options.encoding = "UTF-8" // allow emoji in comments :^) +} + +sourceSets { + launch { + compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output + } + + schematica_api { + compileClasspath += main.compileClasspath + } + + main { + compileClasspath += schematica_api.output + } +} + +minecraft { + version = '1.12.2' + mappings = 'stable_39' + tweakClass = 'baritone.launch.BaritoneTweaker' + runDir = 'run' + + // The sources jar should use SRG names not MCP to ensure compatibility with all mappings + makeObfSourceJar = true +} + +repositories { + mavenCentral() + + maven { + name = 'spongepowered-repo' + url = 'http://repo.spongepowered.org/maven/' + } + + maven { + name = 'impactdevelopment-repo' + url = 'https://impactdevelopment.github.io/maven/' + } +} + +dependencies { + runtime launchCompile('com.github.ImpactDevelopment:SimpleTweaker:1.2') + runtime launchCompile('org.spongepowered:mixin:0.7.11-SNAPSHOT') { + // Mixin includes a lot of dependencies that are too up-to-date + exclude module: 'launchwrapper' + exclude module: 'guava' + exclude module: 'gson' + exclude module: 'commons-io' + exclude module: 'log4j-core' + } + testImplementation 'junit:junit:4.12' +} + +mixin { + defaultObfuscationEnv searge + add sourceSets.launch, 'mixins.baritone.refmap.json' +} + +javadoc { + options.addStringOption('Xwerror', '-quiet') // makes the build fail on travis when there is a javadoc error + options.linkSource true + options.encoding "UTF-8" // allow emoji in comments :^) + source = sourceSets.api.allJava + classpath += sourceSets.api.compileClasspath +} + +jar { + from sourceSets.launch.output, sourceSets.api.output + preserveFileTimestamps = false + reproducibleFileOrder = true + + manifest { + attributes( + 'MixinConfigs': 'mixins.baritone.json', + + 'Implementation-Title': 'Baritone', + 'Implementation-Version': version + ) + } +} + +task proguard(type: ProguardTask) { + url 'https://downloads.sourceforge.net/project/proguard/proguard/6.0/proguard6.0.3.zip' + extract 'proguard6.0.3/lib/proguard.jar' +} + +task createDist(type: CreateDistTask, dependsOn: proguard) + +build.finalizedBy(createDist) diff --git a/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java b/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java index 0450509f..7e26dac1 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java @@ -17,8 +17,6 @@ package baritone.gradle.task; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; import org.gradle.api.DefaultTask; import java.io.File; @@ -26,7 +24,6 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; /** * @author Brady @@ -34,8 +31,6 @@ import java.util.List; */ class BaritoneGradleTask extends DefaultTask { - protected static final JsonParser PARSER = new JsonParser(); - protected static final String PROGUARD_ZIP = "proguard.zip", PROGUARD_JAR = "proguard.jar", @@ -45,26 +40,28 @@ class BaritoneGradleTask extends DefaultTask { PROGUARD_STANDALONE_CONFIG = "standalone.pro", PROGUARD_EXPORT_PATH = "proguard_out.jar", - VERSION_MANIFEST = "version_manifest.json", - TEMP_LIBRARY_DIR = "tempLibraries/", - ARTIFACT_STANDARD = "%s-%s.jar", - ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar", - ARTIFACT_API = "%s-api-%s.jar", - ARTIFACT_STANDALONE = "%s-standalone-%s.jar"; + ARTIFACT_STANDARD = "%s-%s.jar", + ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar", + ARTIFACT_API = "%s-api-%s.jar", + ARTIFACT_STANDALONE = "%s-standalone-%s.jar", + ARTIFACT_FORGE_API = "%s-api-forge-%s.jar", + ARTIFACT_FORGE_STANDALONE = "%s-standalone-forge-%s.jar"; protected String artifactName, artifactVersion; - protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, proguardOut; + protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, artifactForgeApiPath, artifactForgeStandalonePath, proguardOut; protected void verifyArtifacts() throws IllegalStateException { this.artifactName = getProject().getName(); this.artifactVersion = getProject().getVersion().toString(); - this.artifactPath = this.getBuildFile(formatVersion(ARTIFACT_STANDARD)); - this.artifactUnoptimizedPath = this.getBuildFile(formatVersion(ARTIFACT_UNOPTIMIZED)); - this.artifactApiPath = this.getBuildFile(formatVersion(ARTIFACT_API)); - this.artifactStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_STANDALONE)); + this.artifactPath = this.getBuildFile(formatVersion(ARTIFACT_STANDARD)); + this.artifactUnoptimizedPath = this.getBuildFile(formatVersion(ARTIFACT_UNOPTIMIZED)); + this.artifactApiPath = this.getBuildFile(formatVersion(ARTIFACT_API)); + this.artifactStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_STANDALONE)); + this.artifactForgeApiPath = this.getBuildFile(formatVersion(ARTIFACT_FORGE_API)); + this.artifactForgeStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_FORGE_STANDALONE)); this.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH); @@ -95,8 +92,4 @@ class BaritoneGradleTask extends DefaultTask { protected Path getBuildFile(String file) { return getRelativeFile("build/libs/" + file); } - - protected JsonElement readJson(List lines) { - return PARSER.parse(String.join("\n", lines)); - } } diff --git a/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java b/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java index 49e2e142..3ca935d1 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java @@ -42,9 +42,11 @@ public class CreateDistTask extends BaritoneGradleTask { super.verifyArtifacts(); // Define the distribution file paths - Path api = getRelativeFile("dist/" + formatVersion(ARTIFACT_API)); - Path standalone = getRelativeFile("dist/" + formatVersion(ARTIFACT_STANDALONE)); - Path unoptimized = getRelativeFile("dist/" + formatVersion(ARTIFACT_UNOPTIMIZED)); + Path api = getRelativeFile("dist/" + formatVersion(ARTIFACT_API)); + Path standalone = getRelativeFile("dist/" + formatVersion(ARTIFACT_STANDALONE)); + Path unoptimized = getRelativeFile("dist/" + formatVersion(ARTIFACT_UNOPTIMIZED)); + Path forgeApi = getRelativeFile("dist/" + formatVersion(ARTIFACT_FORGE_API)); + Path forgeStandalone = getRelativeFile("dist/" + formatVersion(ARTIFACT_FORGE_STANDALONE)); // NIO will not automatically create directories Path dir = getRelativeFile("dist/"); @@ -53,12 +55,14 @@ public class CreateDistTask extends BaritoneGradleTask { } // Copy build jars to dist/ - Files.copy(this.artifactApiPath, api, REPLACE_EXISTING); - Files.copy(this.artifactStandalonePath, standalone, REPLACE_EXISTING); - Files.copy(this.artifactUnoptimizedPath, unoptimized, REPLACE_EXISTING); + Files.copy(this.artifactApiPath, api, REPLACE_EXISTING); + Files.copy(this.artifactStandalonePath, standalone, REPLACE_EXISTING); + Files.copy(this.artifactUnoptimizedPath, unoptimized, REPLACE_EXISTING); + Files.copy(this.artifactForgeApiPath, forgeApi, REPLACE_EXISTING); + Files.copy(this.artifactForgeStandalonePath, forgeStandalone, REPLACE_EXISTING); // Calculate all checksums and format them like "shasum" - List shasum = Stream.of(api, standalone, unoptimized) + List shasum = Stream.of(api, forgeApi, standalone, forgeStandalone, unoptimized) .map(path -> sha1(path) + " " + path.getFileName().toString()) .collect(Collectors.toList()); diff --git a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java index 0276971c..44e1cb9f 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java @@ -60,6 +60,8 @@ public class ProguardTask extends BaritoneGradleTask { private List requiredLibraries; + private File mixin; + @TaskAction protected void exec() throws Exception { super.verifyArtifacts(); @@ -80,7 +82,7 @@ public class ProguardTask extends BaritoneGradleTask { Files.delete(this.artifactUnoptimizedPath); } - Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString()); + Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString(), Optional.empty()); } private void downloadProguard() throws Exception { @@ -175,10 +177,16 @@ public class ProguardTask extends BaritoneGradleTask { // Find the library jar file, and copy it to tempLibraries for (File file : pair.getLeft().files(pair.getRight())) { if (file.getName().startsWith(lib)) { + if (lib.contains("mixin")) { + mixin = file; + } Files.copy(file.toPath(), getTemporaryFile("tempLibraries/" + lib + ".jar"), REPLACE_EXISTING); } } } + if (mixin == null) { + throw new IllegalStateException("Unable to find mixin jar"); + } } // a bunch of epic stuff to get the path to the cached jar @@ -266,12 +274,14 @@ public class ProguardTask extends BaritoneGradleTask { private void proguardApi() throws Exception { runProguard(getTemporaryFile(PROGUARD_API_CONFIG)); - Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString()); + Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString(), Optional.empty()); + Determinizer.determinize(this.proguardOut.toString(), this.artifactForgeApiPath.toString(), Optional.of(mixin)); } private void proguardStandalone() throws Exception { runProguard(getTemporaryFile(PROGUARD_STANDALONE_CONFIG)); - Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString()); + Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString(), Optional.empty()); + Determinizer.determinize(this.proguardOut.toString(), this.artifactForgeStandalonePath.toString(), Optional.of(mixin)); } private void cleanup() { @@ -300,8 +310,8 @@ public class ProguardTask extends BaritoneGradleTask { .start(); // We can't do output inherit process I/O with gradle for some reason and have it work, so we have to do this - this.printOutputLog(p.getInputStream()); - this.printOutputLog(p.getErrorStream()); + this.printOutputLog(p.getInputStream(), System.out); + this.printOutputLog(p.getErrorStream(), System.err); // Halt the current thread until the process is complete, if the exit code isn't 0, throw an exception int exitCode = p.waitFor(); @@ -310,12 +320,12 @@ public class ProguardTask extends BaritoneGradleTask { } } - private void printOutputLog(InputStream stream) { + private void printOutputLog(InputStream stream, PrintStream outerr) { new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { String line; while ((line = reader.readLine()) != null) { - System.out.println(line); + outerr.println(line); } } catch (Exception e) { e.printStackTrace(); diff --git a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java index fc268cd3..d9f475a5 100644 --- a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java +++ b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java @@ -22,10 +22,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.*; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; @@ -39,7 +36,7 @@ import java.util.stream.Collectors; */ public class Determinizer { - public static void determinize(String inputPath, String outputPath) throws IOException { + public static void determinize(String inputPath, String outputPath, Optional toInclude) throws IOException { System.out.println("Running Determinizer"); System.out.println(" Input path: " + inputPath); System.out.println(" Output path: " + outputPath); @@ -66,10 +63,30 @@ public class Determinizer { if (entry.getName().endsWith(".refmap.json")) { JsonObject object = new JsonParser().parse(new InputStreamReader(jarFile.getInputStream(entry))).getAsJsonObject(); jos.write(writeSorted(object).getBytes()); + } else if (entry.getName().equals("META-INF/MANIFEST.MF") && toInclude.isPresent()) { // only replace for forge jar + ByteArrayOutputStream cancer = new ByteArrayOutputStream(); + copy(jarFile.getInputStream(entry), cancer); + String manifest = new String(cancer.toByteArray()); + if (!manifest.contains("baritone.launch.BaritoneTweaker")) { + throw new IllegalStateException("unable to replace"); + } + manifest = manifest.replace("baritone.launch.BaritoneTweaker", "org.spongepowered.asm.launch.MixinTweaker"); + jos.write(manifest.getBytes()); } else { copy(jarFile.getInputStream(entry), jos); } } + if (toInclude.isPresent()) { + try (JarFile mixin = new JarFile(toInclude.get())) { + for (JarEntry entry : mixin.stream().sorted(Comparator.comparing(JarEntry::getName)).collect(Collectors.toList())) { + if (entry.getName().startsWith("META-INF") && !entry.getName().startsWith("META-INF/services")) { + continue; + } + jos.putNextEntry(entry); + copy(mixin.getInputStream(entry), jos); + } + } + } jos.finish(); } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 59006a9e..599a02b7 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jul 31 21:56:56 PDT 2018 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip +#Tue Jul 31 21:56:56 PDT 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip diff --git a/gradlew.bat b/gradlew.bat index e95643d6..f9553162 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/scripts/proguard.pro b/scripts/proguard.pro index 07e1ec41..4ac0f723 100644 --- a/scripts/proguard.pro +++ b/scripts/proguard.pro @@ -12,14 +12,16 @@ -flattenpackagehierarchy -repackageclasses 'baritone' +# lwjgl is weird +-dontwarn org.lwjgl.** + -keep class baritone.api.** { *; } # this is the keep api # service provider needs these class names -keep class baritone.BaritoneProvider -keep class baritone.api.IBaritoneProvider -# hack --keep class baritone.utils.ExampleBaritoneControl { *; } # have to include this string to remove this keep in the standalone build: # this is the keep api +-keep class baritone.api.utils.MyChunkPos { *; } # even in standalone we need to keep this for gson reflect # setting names are reflected from field names, so keep field names -keepclassmembers class baritone.api.Settings { @@ -29,6 +31,11 @@ # need to keep mixin names -keep class baritone.launch.** { *; } +#try to keep usage of schematica in separate classes +-keep class baritone.utils.schematic.schematica.** +#proguard doesnt like it when it cant find our fake schematica classes +-dontwarn baritone.utils.schematic.schematica.** + # copy all necessary libraries into tempLibraries to build # The correct jar will be copied from the forgegradle cache based on the mapping type being compiled with diff --git a/settings.gradle b/settings.gradle index baa9dfc9..567f8411 100755 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1,19 @@ -/* - * This file is part of Baritone. - * - * Baritone is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Baritone is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Baritone. If not, see . - */ - -rootProject.name = 'baritone' - +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +rootProject.name = 'baritone' + diff --git a/src/api/java/baritone/api/BaritoneAPI.java b/src/api/java/baritone/api/BaritoneAPI.java index c1227830..53937bd8 100644 --- a/src/api/java/baritone/api/BaritoneAPI.java +++ b/src/api/java/baritone/api/BaritoneAPI.java @@ -23,9 +23,7 @@ import java.util.Iterator; import java.util.ServiceLoader; /** - * API exposure for various things implemented in Baritone. - *

- * W.I.P + * Exposes the {@link IBaritoneProvider} instance and the {@link Settings} instance for API usage. * * @author Brady * @since 9/23/2018 @@ -36,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 6bdb7d10..8c5de47a 100644 --- a/src/api/java/baritone/api/IBaritone.java +++ b/src/api/java/baritone/api/IBaritone.java @@ -22,12 +22,11 @@ import baritone.api.behavior.IPathingBehavior; import baritone.api.cache.IWorldProvider; import baritone.api.event.listener.IEventBus; import baritone.api.pathing.calc.IPathingControlManager; -import baritone.api.process.ICustomGoalProcess; -import baritone.api.process.IFollowProcess; -import baritone.api.process.IGetToBlockProcess; -import baritone.api.process.IMineProcess; +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 @@ -36,10 +35,10 @@ import baritone.api.utils.IPlayerContext; public interface IBaritone { /** - * @return The {@link IFollowProcess} instance - * @see IFollowProcess + * @return The {@link IPathingBehavior} instance + * @see IPathingBehavior */ - IFollowProcess getFollowProcess(); + IPathingBehavior getPathingBehavior(); /** * @return The {@link ILookBehavior} instance @@ -47,6 +46,12 @@ public interface IBaritone { */ ILookBehavior getLookBehavior(); + /** + * @return The {@link IFollowProcess} instance + * @see IFollowProcess + */ + IFollowProcess getFollowProcess(); + /** * @return The {@link IMineProcess} instance * @see IMineProcess @@ -54,10 +59,34 @@ public interface IBaritone { IMineProcess getMineProcess(); /** - * @return The {@link IPathingBehavior} instance - * @see IPathingBehavior + * @return The {@link IBuilderProcess} instance + * @see IBuilderProcess */ - IPathingBehavior getPathingBehavior(); + IBuilderProcess getBuilderProcess(); + + /** + * @return The {@link IExploreProcess} instance + * @see IExploreProcess + */ + IExploreProcess getExploreProcess(); + + /** + * @return The {@link IFarmProcess} instance + * @see IFarmProcess + */ + IFarmProcess getFarmProcess(); + + /** + * @return The {@link ICustomGoalProcess} instance + * @see ICustomGoalProcess + */ + ICustomGoalProcess getCustomGoalProcess(); + + /** + * @return The {@link IGetToBlockProcess} instance + * @see IGetToBlockProcess + */ + IGetToBlockProcess getGetToBlockProcess(); /** * @return The {@link IWorldProvider} instance @@ -65,15 +94,47 @@ public interface IBaritone { */ IWorldProvider getWorldProvider(); + /** + * Returns the {@link IPathingControlManager} for this {@link IBaritone} instance, which is responsible + * for managing the {@link IBaritoneProcess}es which control the {@link IPathingBehavior} state. + * + * @return The {@link IPathingControlManager} instance + * @see IPathingControlManager + */ IPathingControlManager getPathingControlManager(); + /** + * @return The {@link IInputOverrideHandler} instance + * @see IInputOverrideHandler + */ IInputOverrideHandler getInputOverrideHandler(); - ICustomGoalProcess getCustomGoalProcess(); - - IGetToBlockProcess getGetToBlockProcess(); - + /** + * @return The {@link IPlayerContext} instance + * @see IPlayerContext + */ IPlayerContext getPlayerContext(); + /** + * @return The {@link IEventBus} instance + * @see IEventBus + */ IEventBus getGameEventHandler(); + + /** + * @return The {@link ISelectionManager} instance + * @see ISelectionManager + */ + ISelectionManager getSelectionManager(); + + /** + * @return The {@link ICommandManager} instance + * @see ICommandManager + */ + ICommandManager getCommandManager(); + + /** + * Open click + */ + void openClick(); } diff --git a/src/api/java/baritone/api/IBaritoneProvider.java b/src/api/java/baritone/api/IBaritoneProvider.java index d66f7831..ac4d623e 100644 --- a/src/api/java/baritone/api/IBaritoneProvider.java +++ b/src/api/java/baritone/api/IBaritoneProvider.java @@ -22,9 +22,12 @@ import baritone.api.cache.IWorldScanner; import net.minecraft.client.entity.EntityPlayerSP; import java.util.List; +import java.util.Objects; /** - * @author Leijurv + * Provides the present {@link IBaritone} instances + * + * @author leijurv */ public interface IBaritoneProvider { @@ -48,18 +51,19 @@ public interface IBaritoneProvider { /** * Provides the {@link IBaritone} instance for a given {@link EntityPlayerSP}. This will likely be - * replaced with {@code #getBaritoneForUser(IBaritoneUser)} when {@code bot-system} is merged. + * replaced with or be overloaded in addition to {@code #getBaritoneForUser(IBaritoneUser)} when + * {@code bot-system} is merged into {@code master}. * * @param player The player * @return The {@link IBaritone} instance. */ default IBaritone getBaritoneForPlayer(EntityPlayerSP player) { for (IBaritone baritone : getAllBaritones()) { - if (player.equals(baritone.getPlayerContext().player())) { + if (Objects.equals(player, baritone.getPlayerContext().player())) { return baritone; } } - throw new IllegalStateException("No baritone for player " + player); + return null; } /** diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 89716a38..8d54cbde 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -17,83 +17,96 @@ package baritone.api; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.TypeUtils; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; import net.minecraft.item.Item; +import net.minecraft.util.math.Vec3i; import net.minecraft.util.text.ITextComponent; import java.awt.*; import java.lang.reflect.Field; -import java.util.*; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.List; +import java.util.*; import java.util.function.Consumer; /** - * Baritone's settings + * Baritone's settings. Settings apply to all Baritone instances. * * @author leijurv */ -public class Settings { +public final class Settings { /** * Allow Baritone to break blocks */ - public Setting allowBreak = new Setting<>(true); + public final Setting allowBreak = new Setting<>(true); /** * Allow Baritone to sprint */ - public Setting allowSprint = new Setting<>(true); + public final Setting allowSprint = new Setting<>(true); /** * Allow Baritone to place blocks */ - public Setting allowPlace = new Setting<>(true); + public final Setting allowPlace = new Setting<>(true); /** * Allow Baritone to move items in your inventory to your hotbar */ - public Setting allowInventory = new Setting<>(false); + public final Setting allowInventory = new Setting<>(false); /** * It doesn't actually take twenty ticks to place a block, this cost is so high - * because we want to generally conserve blocks which might be limited + * because we want to generally conserve blocks which might be limited. + *

+ * Decrease to make Baritone more often consider paths that would require placing blocks */ - public Setting blockPlacementPenalty = new Setting<>(20D); + public final Setting blockPlacementPenalty = new Setting<>(20D); /** * This is just a tiebreaker to make it less likely to break blocks if it can avoid it. * For example, fire has a break cost of 0, this makes it nonzero, so all else being equal * it will take an otherwise equivalent route that doesn't require it to put out fire. */ - public Setting blockBreakAdditionalPenalty = new Setting<>(2D); + public final Setting blockBreakAdditionalPenalty = new Setting<>(2D); /** - * Additional penalty for hitting the space bar (ascend, pillar, or parkour) beacuse it uses hunger + * Additional penalty for hitting the space bar (ascend, pillar, or parkour) because it uses hunger */ - public Setting jumpPenalty = new Setting<>(2D); + public final Setting jumpPenalty = new Setting<>(2D); /** * Walking on water uses up hunger really quick, so penalize it */ - public Setting walkOnWaterOnePenalty = new Setting<>(5D); + public final Setting walkOnWaterOnePenalty = new Setting<>(3D); /** * Allow Baritone to fall arbitrary distances and place a water bucket beneath it. * Reliability: questionable. */ - public Setting allowWaterBucketFall = new Setting<>(true); + public final Setting allowWaterBucketFall = new Setting<>(true); /** * Allow Baritone to assume it can walk on still water just like any other block. * This functionality is assumed to be provided by a separate library that might have imported Baritone. */ - public Setting assumeWalkOnWater = new Setting<>(false); + public final Setting assumeWalkOnWater = new Setting<>(false); + + /** + * If you have Fire Resistance and Jesus then I guess you could turn this on lol + */ + public final Setting assumeWalkOnLava = new Setting<>(false); /** * Assume step functionality; don't jump on an Ascend. */ - public Setting assumeStep = new Setting<>(false); + public final Setting assumeStep = new Setting<>(false); /** * Assume safe walk functionality; don't sneak on a backplace traverse. @@ -102,14 +115,21 @@ public class Settings { * it won't sneak right click, it'll just right click, which means it'll open the chest instead of placing * against it. That's why this defaults to off. */ - public Setting assumeSafeWalk = new Setting<>(false); + public final Setting assumeSafeWalk = new Setting<>(false); /** * If true, parkour is allowed to make jumps when standing on blocks at the maximum height, so player feet is y=256 *

- * Defaults to false because this fails on NCP + * Defaults to false because this fails on constantiam. Please let me know if this is ever disabled. Please. */ - public Setting allowJumpAt256 = new Setting<>(false); + public final Setting allowJumpAt256 = new Setting<>(false); + + /** + * This should be monetized it's so good + *

+ * Defaults to true, but only actually takes effect if allowParkour is also true + */ + public final Setting allowParkourAscend = new Setting<>(true); /** * Allow descending diagonally @@ -118,45 +138,120 @@ public class Settings { *

* For a generic "take some risks" mode I'd turn on this one, parkour, and parkour place. */ - public Setting allowDiagonalDescend = new Setting<>(false); + 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 + *

+ * Turn this off to force it to make more staircases and less shafts + */ + public final Setting allowDownward = new Setting<>(true); /** * Blocks that Baritone is allowed to place (as throwaway, for sneak bridging, pillaring, etc.) */ - public Setting> acceptableThrowawayItems = new Setting<>(new ArrayList<>(Arrays.asList( + public final Setting> acceptableThrowawayItems = new Setting<>(new ArrayList<>(Arrays.asList( Item.getItemFromBlock(Blocks.DIRT), Item.getItemFromBlock(Blocks.COBBLESTONE), - Item.getItemFromBlock(Blocks.NETHERRACK) + Item.getItemFromBlock(Blocks.NETHERRACK), + Item.getItemFromBlock(Blocks.STONE) ))); + /** + * Blocks that Baritone will attempt to avoid (Used in avoidance) + */ + public final Setting> blocksToAvoid = new Setting<>(new ArrayList<>( + // Leave Empty by Default + )); + + /** + * Blocks that Baritone is not allowed to break + */ + public final Setting> blocksToAvoidBreaking = new Setting<>(new ArrayList<>(Arrays.asList( // TODO can this be a HashSet or ImmutableSet? + Blocks.CRAFTING_TABLE, + Blocks.FURNACE, + Blocks.LIT_FURNACE, + Blocks.CHEST, + Blocks.TRAPPED_CHEST, + Blocks.STANDING_SIGN, + Blocks.WALL_SIGN + ))); + + /** + * If this setting is true, Baritone will never break a block that is adjacent to an unsupported falling block. + *

+ * I.E. it will never trigger cascading sand / gravel falls + */ + public final Setting avoidUpdatingFallingBlocks = new Setting<>(true); + /** * Enables some more advanced vine features. They're honestly just gimmicks and won't ever be needed in real * pathing scenarios. And they can cause Baritone to get trapped indefinitely in a strange scenario. + *

+ * Almost never turn this on lol */ - public Setting allowVines = new Setting<>(false); + public final Setting allowVines = new Setting<>(false); /** * Slab behavior is complicated, disable this for higher path reliability. Leave enabled if you have bottom slabs * everywhere in your base. */ - public Setting allowWalkOnBottomSlab = new Setting<>(true); + public final Setting allowWalkOnBottomSlab = new Setting<>(true); /** * You know what it is *

* But it's very unreliable and falls off when cornering like all the time so. + *

+ * It also overshoots the landing pretty much always (making contact with the next block over), so be careful */ - public Setting allowParkour = new Setting<>(false); + public final Setting allowParkour = new Setting<>(false); /** - * Like parkour, but even more unreliable! + * Actually pretty reliable. + *

+ * Doesn't make it any more dangerous compared to just normal allowParkour th */ - public Setting allowParkourPlace = new Setting<>(false); + public final Setting allowParkourPlace = new Setting<>(false); /** * For example, if you have Mining Fatigue or Haste, adjust the costs of breaking blocks accordingly. */ - public Setting considerPotionEffects = new Setting<>(true); + public final Setting considerPotionEffects = new Setting<>(true); + + /** + * Sprint and jump a block early on ascends wherever possible + */ + public final Setting sprintAscends = new Setting<>(true); + + /** + * If we overshoot a traverse and end up one block beyond the destination, mark it as successful anyway. + *

+ * This helps with speed at >=20m/s + */ + public final Setting overshootTraverse = new Setting<>(true); + + /** + * When breaking blocks for a movement, wait until all falling blocks have settled before continuing + */ + public final Setting pauseMiningForFallingBlocks = new Setting<>(true); + + /** + * How many ticks between right clicks are allowed. Default in game is 4 + */ + public final Setting rightClickSpeed = new Setting<>(4); + + /** + * 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. @@ -173,131 +268,139 @@ public class Settings { *

* Finding the optimal path is worth it, so it's the default. */ - public Setting costHeuristic = new Setting<>(3.563); + public final Setting costHeuristic = new Setting<>(3.563); // a bunch of obscure internal A* settings that you probably don't want to change /** * The maximum number of times it will fetch outside loaded or cached chunks before assuming that * pathing has reached the end of the known area, and should therefore stop. */ - public Setting pathingMaxChunkBorderFetch = new Setting<>(50); + public final Setting pathingMaxChunkBorderFetch = new Setting<>(50); /** * Set to 1.0 to effectively disable this feature * * @see Issue #18 */ - public Setting backtrackCostFavoringCoefficient = new Setting<>(0.5); + public final Setting backtrackCostFavoringCoefficient = new Setting<>(0.5); /** * Toggle the following 4 settings *

- * They have a noticable performance impact, so they default off + * They have a noticeable performance impact, so they default off + *

+ * Specifically, building up the avoidance map on the main thread before pathing starts actually takes a noticeable + * amount of time, especially when there are a lot of mobs around, and your game jitters for like 200ms while doing so */ - public Setting avoidance = new Setting<>(false); + public final Setting avoidance = new Setting<>(false); + /** * Set to 1.0 to effectively disable this feature *

* Set below 1.0 to go out of your way to walk near mob spawners */ - public Setting mobSpawnerAvoidanceCoefficient = new Setting<>(2.0); + public final Setting mobSpawnerAvoidanceCoefficient = new Setting<>(2.0); - public Setting mobSpawnerAvoidanceRadius = new Setting<>(16); + public final Setting mobSpawnerAvoidanceRadius = new Setting<>(16); /** * Set to 1.0 to effectively disable this feature *

* Set below 1.0 to go out of your way to walk near mobs */ - public Setting mobAvoidanceCoefficient = new Setting<>(1.5); + public final Setting mobAvoidanceCoefficient = new Setting<>(1.5); - public Setting mobAvoidanceRadius = new Setting<>(8); + public final Setting mobAvoidanceRadius = new Setting<>(8); /** * When running a goto towards a container block (chest, ender chest, furnace, etc), * right click and open it once you arrive. */ - public Setting rightClickContainerOnArrival = new Setting<>(true); + public final Setting rightClickContainerOnArrival = new Setting<>(true); /** * When running a goto towards a nether portal block, walk all the way into the portal * instead of stopping one block before. */ - public Setting enterPortal = new Setting<>(true); + public final Setting enterPortal = new Setting<>(true); /** * Don't repropagate cost improvements below 0.01 ticks. They're all just floating point inaccuracies, * and there's no point. */ - public Setting minimumImprovementRepropagation = new Setting<>(true); + public final Setting minimumImprovementRepropagation = new Setting<>(true); /** * After calculating a path (potentially through cached chunks), artificially cut it off to just the part that is * entirely within currently loaded chunks. Improves path safety because cached chunks are heavily simplified. + *

+ * This is much safer to leave off now, and makes pathing more efficient. More explanation in the issue. * - * @see Issue #144 + * @see Issue #114 */ - public Setting cutoffAtLoadBoundary = new Setting<>(false); + public final Setting cutoffAtLoadBoundary = new Setting<>(false); /** * If a movement's cost increases by more than this amount between calculation and execution (due to changes * in the environment / world), cancel and recalculate */ - public Setting maxCostIncrease = new Setting<>(10D); + public final Setting maxCostIncrease = new Setting<>(10D); /** * Stop 5 movements before anything that made the path COST_INF. * For example, if lava has spread across the path, don't walk right up to it then recalculate, it might * still be spreading lol */ - public Setting costVerificationLookahead = new Setting<>(5); + public final Setting costVerificationLookahead = new Setting<>(5); /** * Static cutoff factor. 0.9 means cut off the last 10% of all paths, regardless of chunk load state */ - public Setting pathCutoffFactor = new Setting<>(0.9); + public final Setting pathCutoffFactor = new Setting<>(0.9); /** * Only apply static cutoff for paths of at least this length (in terms of number of movements) */ - public Setting pathCutoffMinimumLength = new Setting<>(30); + public final Setting pathCutoffMinimumLength = new Setting<>(30); /** * Start planning the next path once the remaining movements tick estimates sum up to less than this value */ - public Setting planningTickLookAhead = new Setting<>(150); + public final Setting planningTickLookahead = new Setting<>(150); /** * Default size of the Long2ObjectOpenHashMap used in pathing */ - public Setting pathingMapDefaultSize = new Setting<>(1024); + public final Setting pathingMapDefaultSize = new Setting<>(1024); /** * Load factor coefficient for the Long2ObjectOpenHashMap used in pathing *

* Decrease for faster map operations, but higher memory usage */ - public Setting pathingMapLoadFactor = new Setting<>(0.75f); + public final Setting pathingMapLoadFactor = new Setting<>(0.75f); /** * How far are you allowed to fall onto solid ground (without a water bucket)? * 3 won't deal any damage. But if you just want to get down the mountain quickly and you have * Feather Falling IV, you might set it a bit higher, like 4 or 5. */ - public Setting maxFallHeightNoWater = new Setting<>(3); + public final Setting maxFallHeightNoWater = new Setting<>(3); /** * How far are you allowed to fall onto solid ground (with a water bucket)? * It's not that reliable, so I've set it below what would kill an unarmored player (23) */ - public Setting maxFallHeightBucket = new Setting<>(20); + public final Setting maxFallHeightBucket = new Setting<>(20); /** * Is it okay to sprint through a descend followed by a diagonal? * The player overshoots the landing, but not enough to fall off. And the diagonal ensures that there isn't * lava or anything that's !canWalkInto in that space, so it's technically safe, just a little sketchy. + *

+ * Note: this is *not* related to the allowDiagonalDescend setting, that is a completely different thing. */ - public Setting allowOvershootDiagonalDescend = new Setting<>(true); + public final Setting allowOvershootDiagonalDescend = new Setting<>(true); /** * If your goal is a GoalBlock in an unloaded chunk, assume it's far enough away that the Y coord @@ -306,131 +409,157 @@ public class Settings { * of considering the Y coord. The reasoning is that if your X and Z are 10,000 blocks away, * your Y coordinate's accuracy doesn't matter at all until you get much much closer. */ - public Setting simplifyUnloadedYCoord = new Setting<>(true); + public final Setting simplifyUnloadedYCoord = new Setting<>(true); /** * If a movement takes this many ticks more than its initial cost estimate, cancel it */ - public Setting movementTimeoutTicks = new Setting<>(100); + public final Setting movementTimeoutTicks = new Setting<>(100); /** * Pathing ends after this amount of time, but only if a path has been found *

* If no valid path (length above the minimum) has been found, pathing continues up until the failure timeout */ - public Setting primaryTimeoutMS = new Setting<>(500L); + public final Setting primaryTimeoutMS = new Setting<>(500L); /** * Pathing can never take longer than this, even if that means failing to find any path at all */ - public Setting failureTimeoutMS = new Setting<>(2000L); + public final Setting failureTimeoutMS = new Setting<>(2000L); /** * Planning ahead while executing a segment ends after this amount of time, but only if a path has been found *

* If no valid path (length above the minimum) has been found, pathing continues up until the failure timeout */ - public Setting planAheadPrimaryTimeoutMS = new Setting<>(4000L); + public final Setting planAheadPrimaryTimeoutMS = new Setting<>(4000L); /** * Planning ahead while executing a segment can never take longer than this, even if that means failing to find any path at all */ - public Setting planAheadFailureTimeoutMS = new Setting<>(5000L); + public final Setting planAheadFailureTimeoutMS = new Setting<>(5000L); /** * For debugging, consider nodes much much slower */ - public Setting slowPath = new Setting<>(false); + public final Setting slowPath = new Setting<>(false); /** * Milliseconds between each node */ - public Setting slowPathTimeDelayMS = new Setting<>(100L); + public final Setting slowPathTimeDelayMS = new Setting<>(100L); /** * The alternative timeout number when slowPath is on */ - public Setting slowPathTimeoutMS = new Setting<>(40000L); + public final Setting slowPathTimeoutMS = new Setting<>(40000L); /** * The big one. Download all chunks in simplified 2-bit format and save them for better very-long-distance pathing. */ - public Setting chunkCaching = new Setting<>(true); + public final Setting chunkCaching = new Setting<>(true); /** * On save, delete from RAM any cached regions that are more than 1024 blocks away from the player *

- * Temporarily disabled, see issue #248 + * Temporarily disabled + *

+ * Temporarily reenabled + * + * @see Issue #248 */ - public Setting pruneRegionsFromRAM = new Setting<>(false); + public final Setting pruneRegionsFromRAM = new Setting<>(true); + + /** + * Remember the contents of containers (chests, echests, furnaces) + *

+ * Really buggy since the packet stuff is multithreaded badly thanks to brady + */ + public final Setting containerMemory = new Setting<>(false); + + /** + * Fill in blocks behind you + */ + public final Setting backfill = new Setting<>(false); /** * Print all the debug messages to chat */ - public Setting chatDebug = new Setting<>(true); + public final Setting chatDebug = new Setting<>(false); /** * Allow chat based control of Baritone. Most likely should be disabled when Baritone is imported for use in * something else */ - public Setting chatControl = new Setting<>(true); + 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 Setting removePrefix = new Setting<>(false); + public final Setting chatControlAnyway = new Setting<>(false); /** * Render the path */ - public Setting renderPath = new Setting<>(true); + 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 */ - public Setting renderGoal = new Setting<>(true); + public final Setting renderGoal = new Setting<>(true); + + /** + * Render selection boxes + */ + public final Setting renderSelectionBoxes = new Setting<>(true); /** * Ignore depth when rendering the goal */ - public Setting renderGoalIgnoreDepth = new Setting<>(true); + public final Setting renderGoalIgnoreDepth = new Setting<>(true); /** * Renders X/Z type Goals with the vanilla beacon beam effect. Combining this with * {@link #renderGoalIgnoreDepth} will cause strange render clipping. */ - public Setting renderGoalXZBeacon = new Setting<>(false); + public final Setting renderGoalXZBeacon = new Setting<>(false); /** * Ignore depth when rendering the selection boxes (to break, to place, to walk into) */ - public Setting renderSelectionBoxesIgnoreDepth = new Setting<>(true); + public final Setting renderSelectionBoxesIgnoreDepth = new Setting<>(true); /** * Ignore depth when rendering the path */ - public Setting renderPathIgnoreDepth = new Setting<>(true); + public final Setting renderPathIgnoreDepth = new Setting<>(true); /** * Line width of the path when rendered, in pixels */ - public Setting pathRenderLineWidthPixels = new Setting<>(5F); + public final Setting pathRenderLineWidthPixels = new Setting<>(5F); /** * Line width of the goal when rendered, in pixels */ - public Setting goalRenderLineWidthPixels = new Setting<>(3F); + public final Setting goalRenderLineWidthPixels = new Setting<>(3F); /** * Start fading out the path at 20 movements ahead, and stop rendering it entirely 30 movements ahead. * Improves FPS. */ - public Setting fadePath = new Setting<>(false); + public final Setting fadePath = new Setting<>(false); /** * Move without having to force the client-sided rotations */ - public Setting freeLook = new Setting<>(true); + public final Setting freeLook = new Setting<>(true); /** * Will cause some minor behavioral differences to ensure that Baritone works on anticheats. @@ -438,43 +567,241 @@ public class Settings { * At the moment this will silently set the player's rotations when using freeLook so you're not sprinting in * directions other than forward, which is picken up by more "advanced" anticheats like AAC, but not NCP. */ - public Setting antiCheatCompatibility = new Setting<>(true); + public final Setting antiCheatCompatibility = new Setting<>(true); /** * Exclusively use cached chunks for pathing + *

+ * Never turn this on */ - public Setting pathThroughCachedOnly = new Setting<>(false); + public final Setting pathThroughCachedOnly = new Setting<>(false); /** - * Whether or not to use the "#" command prefix + * Continue sprinting while in water */ - public Setting prefix = new Setting<>(false); + public final Setting sprintInWater = new Setting<>(true); + + /** + * When GetToBlockProcess or MineProcess fails to calculate a path, instead of just giving up, mark the closest instance + * of that block as "unreachable" and go towards the next closest. GetToBlock expands this seaarch to the whole "vein"; MineProcess does not. + * This is because MineProcess finds individual impossible blocks (like one block in a vein that has gravel on top then lava, so it can't break) + * Whereas GetToBlock should blacklist the whole "vein" if it can't get to any of them. + */ + public final Setting blacklistClosestOnFailure = new Setting<>(true); + + /** + * 😎 Render cached chunks as semitransparent. Doesn't work with OptiFine 😭 Rarely randomly crashes, see this issue. + *

+ * Can be very useful on servers with low render distance. After enabling, you may need to reload the world in order for it to have an effect + * (e.g. disconnect and reconnect, enter then exit the nether, die and respawn, etc). This may literally kill your FPS and CPU because + * every chunk gets recompiled twice as much as normal, since the cached version comes into range, then the normal one comes from the server for real. + *

+ * Note that flowing water is cached as AVOID, which is rendered as lava. As you get closer, you may therefore see lava falls being replaced with water falls. + *

+ * SOLID is rendered as stone in the overworld, netherrack in the nether, and end stone in the end + */ + public final Setting renderCachedChunks = new Setting<>(false); + + /** + * 0.0f = not visible, fully transparent (instead of setting this to 0, turn off renderCachedChunks) + * 1.0f = fully opaque + */ + public final Setting cachedChunksOpacity = new Setting<>(0.5f); + + /** + * 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 */ - public Setting walkWhileBreaking = new Setting<>(true); + public final Setting walkWhileBreaking = new Setting<>(true); /** - * If we are more than 500 movements into the current path, discard the oldest segments, as they are no longer useful + * When a new segment is calculated that doesn't overlap with the current one, but simply begins where the current segment ends, + * splice it on and make a longer combined path. If this setting is off, any planned segment will not be spliced and will instead + * be the "next path" in PathingBehavior, and will only start after this one ends. Turning this off hurts planning ahead, + * because the next segment will exist even if it's very short. + * + * @see #planningTickLookahead */ - public Setting maxPathHistoryLength = new Setting<>(300); + public final Setting splicePath = new Setting<>(true); + + /** + * If we are more than 300 movements into the current path, discard the oldest segments, as they are no longer useful + */ + public final Setting maxPathHistoryLength = new Setting<>(300); /** * If the current path is too long, cut off this many movements from the beginning. */ - public Setting pathHistoryCutoffAmount = new Setting<>(50); + public final Setting pathHistoryCutoffAmount = new Setting<>(50); /** * Rescan for the goal once every 5 ticks. * Set to 0 to disable. */ - public Setting mineGoalUpdateInterval = new Setting<>(5); + public final Setting mineGoalUpdateInterval = new Setting<>(5); + + /** + * After finding this many instances of the target block in the cache, it will stop expanding outward the chunk search. + */ + public final Setting maxCachedWorldScanCount = new Setting<>(10); + + /** + * When GetToBlock doesn't know any locations for the desired block, explore randomly instead of giving up. + */ + public final Setting exploreForBlocks = new Setting<>(true); + + /** + * While exploring the world, offset the closest unloaded chunk by this much in both axes. + *

+ * This can result in more efficient loading, if you set this to the render distance. + */ + public final Setting worldExploringChunkOffset = new Setting<>(0); + + /** + * Take the 10 closest chunks, even if they aren't strictly tied for distance metric from origin. + */ + public final Setting exploreChunkSetMinimumSize = new Setting<>(10); + + /** + * Attempt to maintain Y coordinate while exploring + *

+ * -1 to disable + */ + public final Setting exploreMaintainY = new Setting<>(64); + + /** + * 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); + + /** + * When the cache scan gives less blocks than the maximum threshold (but still above zero), scan the main world too. + *

+ * Only if you have a beefy CPU and automatically mine blocks that are in cache + */ + public final Setting extendCacheOnThreshold = new Setting<>(false); + + /** + * Don't consider the next layer in builder until the current one is done + */ + public final Setting buildInLayers = new Setting<>(false); + + /** + * false = build from bottom to top + *

+ * true = build from top to bottom + */ + public final Setting layerOrder = new Setting<>(false); + + /** + * How far to move before repeating the build. 0 to disable repeating on a certain axis, 0,0,0 to disable entirely + */ + public final Setting buildRepeat = new Setting<>(new Vec3i(0, 0, 0)); + + /** + * Allow standing above a block while mining it, in BuilderProcess + *

+ * Experimental + */ + public final Setting breakFromAbove = new Setting<>(false); + + /** + * As well as breaking from above, set a goal to up and to the side of all blocks to break. + *

+ * Never turn this on without also turning on breakFromAbove. + */ + public final Setting goalBreakFromAbove = new Setting<>(false); + + /** + * Build in map art mode, which makes baritone only care about the top block in each column + */ + public final Setting mapArtMode = new Setting<>(false); + + /** + * Override builder's behavior to not attempt to correct blocks that are currently water + */ + public final Setting okIfWater = new Setting<>(false); + + /** + * The set of incorrect blocks can never grow beyond this size + */ + public final Setting incorrectSize = new Setting<>(100); + + /** + * Multiply the cost of breaking a block that's correct in the builder's schematic by this coefficient + */ + 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 Setting mineScanDroppedItems = new Setting<>(true); + 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. @@ -489,54 +816,78 @@ public class Settings { *

* Also on cosmic prisons this should be set to true since you don't actually mine the ore it just gets replaced with stone. */ - public Setting cancelOnGoalInvalidation = new Setting<>(true); + public final Setting cancelOnGoalInvalidation = new Setting<>(true); /** * The "axis" command (aka GoalAxis) will go to a axis, or diagonal axis, at this Y level. */ - public Setting axisHeight = new Setting<>(120); + public final Setting axisHeight = new Setting<>(120); /** - * Allow MineBehavior to use X-Ray to see where the ores are. Turn this option off to force it to mine "legit" - * where it will only mine an ore once it can actually see it, so it won't do or know anything that a normal player - * couldn't. If you don't want it to look like you're X-Raying, turn this off + * Disconnect from the server upon arriving at your goal */ - public Setting legitMine = new Setting<>(false); + public final Setting disconnectOnArrival = new Setting<>(false); + + /** + * Disallow MineBehavior from using X-Ray to see where the ores are. Turn this option on to force it to mine "legit" + * where it will only mine an ore once it can actually see it, so it won't do or know anything that a normal player + * couldn't. If you don't want it to look like you're X-Raying, turn this on + */ + public final Setting legitMine = new Setting<>(false); /** * What Y level to go to for legit strip mining */ - public Setting legitMineYLevel = new Setting<>(11); + public final Setting legitMineYLevel = new Setting<>(11); + + /** + * Magically see ores that are separated diagonally from existing ores. Basically like mining around the ores that it finds + * in case there's one there touching it diagonally, except it checks it un-legit-ly without having the mine blocks to see it. + * You can decide whether this looks plausible or not. + *

+ * This is disabled because it results in some weird behavior. For example, it can """see""" the top block of a vein of iron_ore + * through a lava lake. This isn't an issue normally since it won't consider anything touching lava, so it just ignores it. + * However, this setting expands that and allows it to see the entire vein so it'll mine under the lava lake to get the iron that + * it can reach without mining blocks adjacent to lava. This really defeats the purpose of legitMine since a player could never + * do that lol, so thats one reason why its disabled + */ + public final Setting legitMineIncludeDiagonals = new Setting<>(false); /** * When mining block of a certain type, try to mine two at once instead of one. * If the block above is also a goal block, set GoalBlock instead of GoalTwoBlocks * If the block below is also a goal block, set GoalBlock to the position one down instead of GoalTwoBlocks */ - public Setting forceInternalMining = new Setting<>(true); + public final Setting forceInternalMining = new Setting<>(true); /** * Modification to the previous setting, only has effect if forceInternalMining is true * If true, only apply the previous setting if the block adjacent to the goal isn't air. */ - public Setting internalMiningAirException = new Setting<>(true); + public final Setting internalMiningAirException = new Setting<>(true); /** * The actual GoalNear is set this distance away from the entity you're following *

* For example, set followOffsetDistance to 5 and followRadius to 0 to always stay precisely 5 blocks north of your follow target. */ - public Setting followOffsetDistance = new Setting<>(0D); + public final Setting followOffsetDistance = new Setting<>(0D); /** * The actual GoalNear is set in this direction from the entity you're following. This value is in degrees. */ - public Setting followOffsetDirection = new Setting<>(0F); + public final Setting followOffsetDirection = new Setting<>(0F); /** * The radius (for the GoalNear) of how close to your target position you actually have to be */ - public Setting followRadius = new Setting<>(3); + public final Setting followRadius = new Setting<>(3); + + /** + * Turn this on if your exploration filter is enormous, you don't want it to check if it's done, + * and you are just fine with it just hanging on completion + */ + public final Setting disableCompletionCheck = new Setting<>(false); /** * Cached chunks (regardless of if they're in RAM or saved to disk) expire and are deleted after this number of seconds @@ -558,54 +909,105 @@ public class Settings { * has to build up that cache from scratch. But after it's gone through an area just once, the next time will have zero * backtracking, since the entire area is now known and cached. */ - public Setting cachedChunksExpirySeconds = new Setting<>(-1L); + public final Setting cachedChunksExpirySeconds = new Setting<>(-1L); /** * The function that is called when Baritone will log to chat. This function can be added to * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ - public Setting> logger = new Setting<>(Minecraft.getMinecraft().ingameGUI.getChatGUI()::printChatMessage); + public final Setting> logger = new Setting<>(Minecraft.getMinecraft().ingameGUI.getChatGUI()::printChatMessage); + + /** + * The size of the box that is rendered when the current goal is a GoalYLevel + */ + public final Setting yLevelBoxSize = new Setting<>(15D); /** * The color of the current path */ - public Setting colorCurrentPath = new Setting<>(Color.RED); + public final Setting colorCurrentPath = new Setting<>(Color.RED); /** * The color of the next path */ - public Setting colorNextPath = new Setting<>(Color.MAGENTA); + public final Setting colorNextPath = new Setting<>(Color.MAGENTA); /** * The color of the blocks to break */ - public Setting colorBlocksToBreak = new Setting<>(Color.RED); + public final Setting colorBlocksToBreak = new Setting<>(Color.RED); /** * The color of the blocks to place */ - public Setting colorBlocksToPlace = new Setting<>(Color.GREEN); + public final Setting colorBlocksToPlace = new Setting<>(Color.GREEN); /** * The color of the blocks to walk into */ - public Setting colorBlocksToWalkInto = new Setting<>(Color.MAGENTA); + public final Setting colorBlocksToWalkInto = new Setting<>(Color.MAGENTA); /** * The color of the best path so far */ - public Setting colorBestPathSoFar = new Setting<>(Color.BLUE); + public final Setting colorBestPathSoFar = new Setting<>(Color.BLUE); /** * The color of the path to the most recent considered node */ - public Setting colorMostRecentConsidered = new Setting<>(Color.CYAN); + public final Setting colorMostRecentConsidered = new Setting<>(Color.CYAN); /** * The color of the goal box */ - public Setting colorGoalBox = new Setting<>(Color.GREEN); + 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 @@ -617,17 +1019,13 @@ public class Settings { */ public final List> allSettings; - public void reset() { - for (Setting setting : allSettings) { - setting.value = setting.defaultValue; - } - } + public final Map, Type> settingTypes; + + public final class Setting { - public class Setting { public T value; public final T defaultValue; private String name; - private final Class klass; @SuppressWarnings("unchecked") private Setting(T value) { @@ -636,12 +1034,16 @@ public class Settings { } this.value = value; this.defaultValue = value; - this.klass = (Class) value.getClass(); } - @SuppressWarnings("unchecked") - public final K get() { - return (K) value; + /** + * Deprecated! Please use .value directly instead + * + * @return the current setting value + */ + @Deprecated + public final T get() { + return value; } public final String getName() { @@ -649,11 +1051,24 @@ public class Settings { } public Class getValueClass() { - return klass; + // noinspection unchecked + return (Class) TypeUtils.resolveBaseClass(getType()); } + @Override public String toString() { - return name + ": " + value; + return SettingsUtil.settingToString(this); + } + + /** + * Reset this setting to its default value + */ + public void reset() { + value = defaultValue; + } + + public final Type getType() { + return settingTypes.get(this); } } @@ -661,8 +1076,11 @@ public class Settings { Settings() { Field[] temp = getClass().getFields(); - HashMap> tmpByName = new HashMap<>(); + + Map> tmpByName = new HashMap<>(); List> tmpAll = new ArrayList<>(); + Map, Type> tmpSettingTypes = new HashMap<>(); + try { for (Field field : temp) { if (field.getType().equals(Setting.class)) { @@ -675,6 +1093,7 @@ public class Settings { } tmpByName.put(name, setting); tmpAll.add(setting); + tmpSettingTypes.put(setting, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]); } } } catch (IllegalAccessException e) { @@ -682,13 +1101,14 @@ public class Settings { } byLowerName = Collections.unmodifiableMap(tmpByName); allSettings = Collections.unmodifiableList(tmpAll); + settingTypes = Collections.unmodifiableMap(tmpSettingTypes); } @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 00000000..ba01e275 --- /dev/null +++ b/src/api/java/baritone/api/accessor/IGuiScreen.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +import java.net.URI; + +public interface IGuiScreen { + + void openLink(URI url); +} diff --git a/src/api/java/baritone/api/accessor/IItemStack.java b/src/api/java/baritone/api/accessor/IItemStack.java new file mode 100644 index 00000000..480c713f --- /dev/null +++ b/src/api/java/baritone/api/accessor/IItemStack.java @@ -0,0 +1,23 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.accessor; + +public interface IItemStack { + + int getBaritoneHash(); +} diff --git a/src/api/java/baritone/api/behavior/IBehavior.java b/src/api/java/baritone/api/behavior/IBehavior.java index 248148e7..811563b9 100644 --- a/src/api/java/baritone/api/behavior/IBehavior.java +++ b/src/api/java/baritone/api/behavior/IBehavior.java @@ -18,9 +18,13 @@ package baritone.api.behavior; import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.event.listener.IGameEventListener; /** + * A behavior is simply a type that is able to listen to events. + * * @author Brady + * @see IGameEventListener * @since 9/23/2018 */ public interface IBehavior extends AbstractGameEventListener {} diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java index 0f44f2ee..5444bb83 100644 --- a/src/api/java/baritone/api/behavior/IPathingBehavior.java +++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java @@ -37,7 +37,26 @@ public interface IPathingBehavior extends IBehavior { * * @return The estimated remaining ticks in the current segment. */ - Optional ticksRemainingInSegment(); + default Optional ticksRemainingInSegment() { + return ticksRemainingInSegment(true); + } + + /** + * Returns the estimated remaining ticks in the current pathing + * segment. Given that the return type is an optional, {@link Optional#empty()} + * will be returned in the case that there is no current segment being pathed. + * + * @param includeCurrentMovement whether or not to include the entirety of the cost of the currently executing movement in the total + * @return The estimated remaining ticks in the current segment. + */ + default Optional ticksRemainingInSegment(boolean includeCurrentMovement) { + IPathExecutor current = getCurrent(); + if (current == null) { + return Optional.empty(); + } + int start = includeCurrentMovement ? current.getPosition() : current.getPosition() + 1; + return Optional.of(current.getPath().ticksRemainingFrom(start)); + } /** * @return The current pathing goal @@ -45,10 +64,20 @@ 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() */ 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; + } + /** * Cancels the pathing behavior or the current path calculation, and all processes that could be controlling path. *

@@ -59,6 +88,13 @@ public interface IPathingBehavior extends IBehavior { */ boolean cancelEverything(); + /** + * PLEASE never call this + *

+ * If cancelEverything was like "kill" this is "sudo kill -9". Or shutting off your computer. + */ + void forceCancel(); + /** * Returns the current path, from the current path executor, if there is one. * diff --git a/src/api/java/baritone/api/cache/ICachedWorld.java b/src/api/java/baritone/api/cache/ICachedWorld.java index a435ebe0..837ae076 100644 --- a/src/api/java/baritone/api/cache/ICachedWorld.java +++ b/src/api/java/baritone/api/cache/ICachedWorld.java @@ -20,7 +20,7 @@ package baritone.api.cache; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.Chunk; -import java.util.LinkedList; +import java.util.ArrayList; /** * @author Brady @@ -68,7 +68,7 @@ public interface ICachedWorld { * @param maxRegionDistanceSq The maximum region distance, squared * @return The locations found that match the special block */ - LinkedList getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq); + ArrayList getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq); /** * Reloads all of the cached regions in this world from disk. Anything that is not saved @@ -81,4 +81,6 @@ public interface ICachedWorld { * in a new thread by default. */ void save(); + + } diff --git a/src/api/java/baritone/api/cache/IWaypoint.java b/src/api/java/baritone/api/cache/IWaypoint.java index 01df2a48..ff235099 100644 --- a/src/api/java/baritone/api/cache/IWaypoint.java +++ b/src/api/java/baritone/api/cache/IWaypoint.java @@ -17,12 +17,9 @@ package baritone.api.cache; -import net.minecraft.util.math.BlockPos; -import org.apache.commons.lang3.ArrayUtils; +import baritone.api.utils.BetterBlockPos; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * A marker for a position in the world. @@ -60,7 +57,7 @@ public interface IWaypoint { * * @return The block position of this waypoint */ - BlockPos getLocation(); + BetterBlockPos getLocation(); enum Tag { @@ -92,20 +89,48 @@ public interface IWaypoint { /** * The names for the tag, anything that the tag can be referred to as. */ - private final String[] names; + public final String[] names; Tag(String... names) { this.names = names; } /** - * Finds a tag from one of the names that could be used to identify said tag. - * - * @param name The name of the tag - * @return The tag, if one is found, otherwise, {@code null} + * @return A name that can be passed to {@link #getByName(String)} to retrieve this tag */ - public static Tag fromString(String name) { - return TAG_LIST.stream().filter(tag -> ArrayUtils.contains(tag.names, name.toLowerCase())).findFirst().orElse(null); + public String getName() { + return names[0]; + } + + /** + * Gets a tag by one of its names. + * + * @param name The name to search for. + * @return The tag, if found, or null. + */ + public static Tag getByName(String name) { + for (Tag action : Tag.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + + return null; + } + + /** + * @return All tag names. + */ + public static String[] getAllNames() { + Set names = new HashSet<>(); + + for (Tag tag : Tag.values()) { + names.addAll(Arrays.asList(tag.names)); + } + + return names.toArray(new String[0]); } } } diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java index caa44cbc..325d4bd0 100644 --- a/src/api/java/baritone/api/cache/IWorldScanner.java +++ b/src/api/java/baritone/api/cache/IWorldScanner.java @@ -17,6 +17,7 @@ package baritone.api.cache; +import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; import net.minecraft.block.Block; import net.minecraft.util.math.BlockPos; @@ -33,28 +34,53 @@ public interface IWorldScanner { /** * Scans the world, up to the specified max chunk radius, for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon - * @param blocks The blocks to scan for + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @param maxSearchRadius The maximum chunk search radius * @return The matching block positions */ - List scanChunkRadius(IPlayerContext ctx, List blocks, int max, int yLevelThreshold, int maxSearchRadius); + List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius); + + default List scanChunkRadius(IPlayerContext ctx, List filter, int max, int yLevelThreshold, int maxSearchRadius) { + return scanChunkRadius(ctx, new BlockOptionalMetaLookup(filter.toArray(new Block[0])), max, yLevelThreshold, maxSearchRadius); + } /** * Scans a single chunk for the specified blocks. * - * @param ctx The {@link IPlayerContext} containing player and world info that the - * scan is based upon + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon + * @param filter The blocks to scan for + * @param pos The position of the target chunk + * @param max The maximum number of blocks to scan before cutoff + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. + * @return The matching block positions + */ + List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold); + + /** + * Scans a single chunk for the specified blocks. + * + * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon * @param blocks The blocks to scan for * @param pos The position of the target chunk * @param max The maximum number of blocks to scan before cutoff - * @param yLevelThreshold If a block is found within this Y level, the current result will be - * returned, if the value is negative, then this condition doesn't apply. + * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value + * is negative, then this condition doesn't apply. * @return The matching block positions */ - List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold); + default List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) { + return scanChunk(ctx, new BlockOptionalMetaLookup(blocks), pos, max, yLevelThreshold); + } + + /** + * Repacks 40 chunks around the player. + * + * @param ctx The player context for that player. + * @return The number of chunks queued for repacking. + */ + int repack(IPlayerContext ctx); } diff --git a/src/main/java/baritone/cache/Waypoint.java b/src/api/java/baritone/api/cache/Waypoint.java similarity index 78% rename from src/main/java/baritone/cache/Waypoint.java rename to src/api/java/baritone/api/cache/Waypoint.java index 19e574f9..9c4fbfbf 100644 --- a/src/main/java/baritone/cache/Waypoint.java +++ b/src/api/java/baritone/api/cache/Waypoint.java @@ -15,10 +15,9 @@ * along with Baritone. If not, see . */ -package baritone.cache; +package baritone.api.cache; -import baritone.api.cache.IWaypoint; -import net.minecraft.util.math.BlockPos; +import baritone.api.utils.BetterBlockPos; import java.util.Date; @@ -32,9 +31,9 @@ public class Waypoint implements IWaypoint { private final String name; private final Tag tag; private final long creationTimestamp; - private final BlockPos location; + private final BetterBlockPos location; - public Waypoint(String name, Tag tag, BlockPos location) { + public Waypoint(String name, Tag tag, BetterBlockPos location) { this(name, tag, location, System.currentTimeMillis()); } @@ -47,7 +46,7 @@ public class Waypoint implements IWaypoint { * @param location The waypoint location * @param creationTimestamp When the waypoint was created */ - Waypoint(String name, Tag tag, BlockPos location, long creationTimestamp) { + public Waypoint(String name, Tag tag, BetterBlockPos location, long creationTimestamp) { this.name = name; this.tag = tag; this.location = location; @@ -56,7 +55,7 @@ public class Waypoint implements IWaypoint { @Override public int hashCode() { - return name.hashCode() + tag.hashCode() + location.hashCode(); //lol + return name.hashCode() ^ tag.hashCode() ^ location.hashCode() ^ Long.hashCode(creationTimestamp); } @Override @@ -75,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/ChatEvent.java b/src/api/java/baritone/api/event/events/ChatEvent.java index 5ab9f5bb..d91e87b0 100644 --- a/src/api/java/baritone/api/event/events/ChatEvent.java +++ b/src/api/java/baritone/api/event/events/ChatEvent.java @@ -17,22 +17,20 @@ package baritone.api.event.events; -import baritone.api.event.events.type.ManagedPlayerEvent; -import net.minecraft.client.entity.EntityPlayerSP; +import baritone.api.event.events.type.Cancellable; /** * @author Brady * @since 8/1/2018 */ -public final class ChatEvent extends ManagedPlayerEvent.Cancellable { +public final class ChatEvent extends Cancellable { /** * The message being sent */ private final String message; - public ChatEvent(EntityPlayerSP player, String message) { - super(player); + public ChatEvent(String message) { this.message = message; } diff --git a/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java b/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java index 99370552..6ac08bdd 100644 --- a/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java +++ b/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java @@ -18,22 +18,19 @@ package baritone.api.event.events; import baritone.api.event.events.type.EventState; -import baritone.api.event.events.type.ManagedPlayerEvent; -import net.minecraft.client.entity.EntityPlayerSP; /** * @author Brady * @since 8/21/2018 */ -public final class PlayerUpdateEvent extends ManagedPlayerEvent { +public final class PlayerUpdateEvent { /** * The state of the event */ private final EventState state; - public PlayerUpdateEvent(EntityPlayerSP player, EventState state) { - super(player); + public PlayerUpdateEvent(EventState state) { this.state = state; } diff --git a/src/api/java/baritone/api/event/events/RotationMoveEvent.java b/src/api/java/baritone/api/event/events/RotationMoveEvent.java index 790318e0..109061c7 100644 --- a/src/api/java/baritone/api/event/events/RotationMoveEvent.java +++ b/src/api/java/baritone/api/event/events/RotationMoveEvent.java @@ -17,8 +17,6 @@ package baritone.api.event.events; -import baritone.api.event.events.type.ManagedPlayerEvent; -import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; @@ -26,7 +24,7 @@ import net.minecraft.entity.EntityLivingBase; * @author Brady * @since 8/21/2018 */ -public final class RotationMoveEvent extends ManagedPlayerEvent { +public final class RotationMoveEvent { /** * The type of event @@ -38,8 +36,7 @@ public final class RotationMoveEvent extends ManagedPlayerEvent { */ private float yaw; - public RotationMoveEvent(EntityPlayerSP player, Type type, float yaw) { - super(player); + public RotationMoveEvent(Type type, float yaw) { this.type = type; this.yaw = yaw; } diff --git a/src/api/java/baritone/api/event/events/SprintStateEvent.java b/src/api/java/baritone/api/event/events/SprintStateEvent.java index e7b8d193..60672801 100644 --- a/src/api/java/baritone/api/event/events/SprintStateEvent.java +++ b/src/api/java/baritone/api/event/events/SprintStateEvent.java @@ -17,21 +17,14 @@ package baritone.api.event.events; -import baritone.api.event.events.type.ManagedPlayerEvent; -import net.minecraft.client.entity.EntityPlayerSP; - /** * @author Brady * @since 1/18/2019 */ -public class SprintStateEvent extends ManagedPlayerEvent { +public final class SprintStateEvent { private Boolean state; - public SprintStateEvent(EntityPlayerSP player) { - super(player); - } - public final void setState(boolean state) { this.state = state; } diff --git a/src/api/java/baritone/api/event/events/TabCompleteEvent.java b/src/api/java/baritone/api/event/events/TabCompleteEvent.java new file mode 100644 index 00000000..fea1da29 --- /dev/null +++ b/src/api/java/baritone/api/event/events/TabCompleteEvent.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.event.events; + +import baritone.api.event.events.type.Cancellable; +import baritone.api.event.events.type.Overrideable; + +/** + * @author LoganDark + */ +public abstract class TabCompleteEvent extends Cancellable { + + public final Overrideable prefix; + public final Overrideable completions; + + TabCompleteEvent(String prefix, String[] completions) { + this.prefix = new Overrideable<>(prefix); + this.completions = new Overrideable<>(completions); + } + + public boolean wasModified() { + return prefix.wasModified() || completions.wasModified(); + } + + public static final class Pre extends TabCompleteEvent { + + public Pre(String prefix) { + super(prefix, null); + } + } + + public static final class Post extends TabCompleteEvent { + + public Post(String prefix, String[] completions) { + super(prefix, completions); + } + } +} diff --git a/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.java b/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.java deleted file mode 100644 index a3487a67..00000000 --- a/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.java +++ /dev/null @@ -1,61 +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.event.events.type; - -import net.minecraft.client.entity.EntityPlayerSP; - -/** - * An event that has a reference to a locally managed player. - * - * @author Brady - * @since 10/11/2018 - */ -public class ManagedPlayerEvent { - - protected final EntityPlayerSP player; - - public ManagedPlayerEvent(EntityPlayerSP player) { - this.player = player; - } - - public final EntityPlayerSP getPlayer() { - return this.player; - } - - public static class Cancellable extends ManagedPlayerEvent implements ICancellable { - - /** - * Whether or not this event has been cancelled - */ - private boolean cancelled; - - public Cancellable(EntityPlayerSP player) { - super(player); - } - - @Override - public final void cancel() { - this.cancelled = true; - } - - @Override - public final boolean isCancelled() { - return this.cancelled; - } - } -} diff --git a/src/api/java/baritone/api/event/events/type/Overrideable.java b/src/api/java/baritone/api/event/events/type/Overrideable.java new file mode 100644 index 00000000..cbad9b99 --- /dev/null +++ b/src/api/java/baritone/api/event/events/type/Overrideable.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.event.events.type; + +/** + * @author LoganDark + */ +public class Overrideable { + + private T value; + private boolean modified; + + public Overrideable(T current) { + value = current; + } + + public T get() { + return value; + } + + public void set(T newValue) { + value = newValue; + modified = true; + } + + public boolean wasModified() { + return modified; + } + + @Override + public String toString() { + return String.format( + "Overrideable{modified=%b,value=%s}", + modified, + value.toString() + ); + } +} diff --git a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java index 71045768..e7d4dc29 100644 --- a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java @@ -39,6 +39,12 @@ public interface AbstractGameEventListener extends IGameEventListener { @Override default void onSendChatMessage(ChatEvent event) {} + @Override + default void onPreTabComplete(TabCompleteEvent.Pre event) {} + + @Override + default void onPostTabComplete(TabCompleteEvent.Post event) {} + @Override default void onChunkEvent(ChunkEvent event) {} diff --git a/src/api/java/baritone/api/event/listener/IGameEventListener.java b/src/api/java/baritone/api/event/listener/IGameEventListener.java index dc471e5f..b7522125 100644 --- a/src/api/java/baritone/api/event/listener/IGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java @@ -57,6 +57,21 @@ public interface IGameEventListener { */ void onSendChatMessage(ChatEvent event); + /** + * Runs whenever the client player tries to tab complete in chat. + * + * @param event The event + */ + void onPreTabComplete(TabCompleteEvent.Pre event); + + /** + * Runs whenever the client player tries to tab complete in chat once completions have been recieved from the + * server. This will only be called if the {@link TabCompleteEvent#cancel()} method was not called. + * + * @param event The event + */ + void onPostTabComplete(TabCompleteEvent.Post event); + /** * Runs before and after whenever a chunk is either loaded, unloaded, or populated. * diff --git a/src/api/java/baritone/api/pathing/goals/GoalAxis.java b/src/api/java/baritone/api/pathing/goals/GoalAxis.java index d8811cf9..7c9b2670 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalAxis.java +++ b/src/api/java/baritone/api/pathing/goals/GoalAxis.java @@ -25,7 +25,7 @@ public class GoalAxis implements Goal { @Override public boolean isInGoal(int x, int y, int z) { - return y == BaritoneAPI.getSettings().axisHeight.get() && (x == 0 || z == 0 || Math.abs(x) == Math.abs(z)); + return y == BaritoneAPI.getSettings().axisHeight.value && (x == 0 || z == 0 || Math.abs(x) == Math.abs(z)); } @Override @@ -39,7 +39,7 @@ public class GoalAxis implements Goal { double flatAxisDistance = Math.min(x, Math.min(z, diff * SQRT_2_OVER_2)); - return flatAxisDistance * BaritoneAPI.getSettings().costHeuristic.get() + GoalYLevel.calculate(BaritoneAPI.getSettings().axisHeight.get(), y); + return flatAxisDistance * BaritoneAPI.getSettings().costHeuristic.value + GoalYLevel.calculate(BaritoneAPI.getSettings().axisHeight.value, y); } @Override diff --git a/src/api/java/baritone/api/pathing/goals/GoalBlock.java b/src/api/java/baritone/api/pathing/goals/GoalBlock.java index e0a60b59..c85e5cad 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; @@ -30,17 +31,17 @@ public class GoalBlock implements Goal, IGoalRenderPos { /** * The X block position of this goal */ - private final int x; + public final int x; /** * The Y block position of this goal */ - private final int y; + public final int y; /** * The Z block position of this goal */ - private final int z; + public final int z; public GoalBlock(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); @@ -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 c4856cdd..8d6fdcb3 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; @@ -28,9 +29,9 @@ import net.minecraft.util.math.BlockPos; */ public class GoalGetToBlock implements Goal, IGoalRenderPos { - private final int x; - private final int y; - private final int z; + public final int x; + public final int y; + public final int z; public GoalGetToBlock(BlockPos pos) { this.x = pos.getX(); @@ -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 00000000..19736924 --- /dev/null +++ b/src/api/java/baritone/api/pathing/goals/GoalInverted.java @@ -0,0 +1,52 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.pathing.goals; + +/** + * Invert any goal. + *

+ * In the old chat control system, #invert just tried to pick a {@link GoalRunAway} that effectively inverted the + * current goal. This goal just reverses the heuristic to act as a TRUE invert. Inverting a Y level? Baritone tries to + * get away from that Y level. Inverting a GoalBlock? Baritone will try to make distance whether it's in the X, Y or Z + * directions. And of course, you can always invert a GoalXZ. + * + * @author LoganDark + */ +public class GoalInverted implements Goal { + + public final Goal origin; + + public GoalInverted(Goal origin) { + this.origin = origin; + } + + @Override + public boolean isInGoal(int x, int y, int z) { + return false; + } + + @Override + public double heuristic(int x, int y, int z) { + return -origin.heuristic(x, y, z); + } + + @Override + public String toString() { + return String.format("GoalInverted{%s}", origin.toString()); + } +} diff --git a/src/api/java/baritone/api/pathing/goals/GoalNear.java b/src/api/java/baritone/api/pathing/goals/GoalNear.java index 6befda6b..73d64ed9 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 3f4a02de..a1a15378 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 new file mode 100644 index 00000000..b48a029a --- /dev/null +++ b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java @@ -0,0 +1,78 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.pathing.goals; + +import baritone.api.utils.SettingsUtil; +import net.minecraft.util.EnumFacing; +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; + public final int dx; + public final int dz; + + public GoalStrictDirection(BlockPos origin, EnumFacing direction) { + x = origin.getX(); + y = origin.getY(); + z = origin.getZ(); + dx = direction.getXOffset(); + dz = direction.getZOffset(); + if (dx == 0 && dz == 0) { + throw new IllegalArgumentException(direction + ""); + } + } + + @Override + public boolean isInGoal(int x, int y, int z) { + return false; + } + + @Override + public double heuristic(int x, int y, int z) { + int distanceFromStartInDesiredDirection = (x - this.x) * dx + (z - this.z) * dz; + + int distanceFromStartInIncorrectDirection = Math.abs((x - this.x) * dz) + Math.abs((z - this.z) * dx); + + int verticalDistanceFromStart = Math.abs(y - this.y); + + // we want heuristic to decrease as desiredDirection increases + double heuristic = -distanceFromStartInDesiredDirection * 100; + + heuristic += distanceFromStartInIncorrectDirection * 1000; + heuristic += verticalDistanceFromStart * 1000; + return heuristic; + } + + @Override + public String toString() { + return String.format( + "GoalStrictDirection{x=%s, y=%s, z=%s, dx=%s, dz=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z), + SettingsUtil.maybeCensor(dx), + SettingsUtil.maybeCensor(dz) + ); + } +} diff --git a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java index b1dc07a1..27be981e 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; @@ -31,17 +32,17 @@ public class GoalTwoBlocks implements Goal, IGoalRenderPos { /** * The X block position of this goal */ - private final int x; + protected final int x; /** * The Y block position of this goal */ - private final int y; + protected final int y; /** * The Z block position of this goal */ - private final int z; + protected final int z; public GoalTwoBlocks(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); @@ -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 636c649f..63d39cd7 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalXZ.java +++ b/src/api/java/baritone/api/pathing/goals/GoalXZ.java @@ -18,6 +18,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) { @@ -80,14 +91,14 @@ public class GoalXZ implements Goal { diagonal = z; } diagonal *= SQRT_2; - return (diagonal + straight) * BaritoneAPI.getSettings().costHeuristic.get(); // big TODO tune + return (diagonal + straight) * BaritoneAPI.getSettings().costHeuristic.value; // big TODO tune } public static GoalXZ fromDirection(Vec3d origin, float yaw, double distance) { 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 ce54eebb..603ef9bd 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) @@ -29,7 +30,7 @@ public class GoalYLevel implements Goal, ActionCosts { /** * The target Y level */ - private final int level; + public final int level; public GoalYLevel(int level) { this.level = 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/pathing/movement/IMovement.java b/src/api/java/baritone/api/pathing/movement/IMovement.java index c9b9ea4b..dae8668d 100644 --- a/src/api/java/baritone/api/pathing/movement/IMovement.java +++ b/src/api/java/baritone/api/pathing/movement/IMovement.java @@ -45,10 +45,6 @@ public interface IMovement { */ boolean safeToCancel(); - double recalculateCost(); - - double calculateCostWithoutCaching(); - boolean calculatedWhileLoaded(); BetterBlockPos getSrc(); diff --git a/src/api/java/baritone/api/pathing/path/IPathExecutor.java b/src/api/java/baritone/api/pathing/path/IPathExecutor.java index f72060dc..a5c3108e 100644 --- a/src/api/java/baritone/api/pathing/path/IPathExecutor.java +++ b/src/api/java/baritone/api/pathing/path/IPathExecutor.java @@ -26,4 +26,6 @@ import baritone.api.pathing.calc.IPath; public interface IPathExecutor { IPath getPath(); + + int getPosition(); } diff --git a/src/api/java/baritone/api/process/IBaritoneProcess.java b/src/api/java/baritone/api/process/IBaritoneProcess.java index c2eb2f49..726260d8 100644 --- a/src/api/java/baritone/api/process/IBaritoneProcess.java +++ b/src/api/java/baritone/api/process/IBaritoneProcess.java @@ -25,8 +25,8 @@ import baritone.api.event.events.PathEvent; *

* Differences between a baritone process and a behavior: *

    - *
  • Only one baritone process can be active at a time
  • - *
  • PathingBehavior can only be controlled by a process
  • + *
  • Only one baritone process can be active at a time
  • + *
  • PathingBehavior can only be controlled by a process
  • *
*

* That's it actually @@ -35,6 +35,16 @@ import baritone.api.event.events.PathEvent; */ public interface IBaritoneProcess { + /** + * Default priority. Most normal processes should have this value. + *

+ * Some examples of processes that should have different values might include some kind of automated mob avoidance + * that would be temporary and would forcefully take control. Same for something that pauses pathing for auto eat, etc. + *

+ * The value is -1 beacuse that's what Impact 4.5's beta auto walk returns and I want to tie with it. + */ + double DEFAULT_PRIORITY = -1; + /** * Would this process like to be in control? * @@ -82,12 +92,23 @@ public interface IBaritoneProcess { * * @return A double representing the priority */ - double priority(); + default double priority() { + return DEFAULT_PRIORITY; + } /** * Returns a user-friendly name for this process. Suitable for a HUD. * * @return A display name that's suitable for a HUD */ - String displayName(); + default String displayName() { + if (!isActive()) { + // i love it when impcat's scuffed HUD calls displayName for inactive processes for 1 tick too long + // causing NPEs when the displayname relies on fields that become null when inactive + return "INACTIVE"; + } + return displayName0(); + } + + String displayName0(); } diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java new file mode 100644 index 00000000..c48c2a04 --- /dev/null +++ b/src/api/java/baritone/api/process/IBuilderProcess.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.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 + * @since 1/15/2019 + */ +public interface IBuilderProcess extends IBaritoneProcess { + + /** + * Requests a build for the specified schematic, labeled as specified, with the specified origin. + * + * @param name A user-friendly name for the schematic + * @param schematic The object representation of the schematic + * @param origin The origin position of the schematic being built + */ + void build(String name, ISchematic schematic, Vec3i origin); + + /** + * Requests a build for the specified schematic, labeled as specified, with the specified origin. + * + * @param name A user-friendly name for the schematic + * @param schematic The file path of the schematic + * @param origin The origin position of the schematic being built + * @return Whether or not the schematic was able to load from file + */ + boolean build(String name, File schematic, Vec3i origin); + + default boolean build(String schematicFile, BlockPos origin) { + File file = new File(new File(Minecraft.getMinecraft().gameDir, "schematics"), schematicFile); + return build(schematicFile, file, origin); + } + + void buildOpenSchematic(); + + 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 new file mode 100644 index 00000000..4950d31d --- /dev/null +++ b/src/api/java/baritone/api/process/IExploreProcess.java @@ -0,0 +1,27 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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 new file mode 100644 index 00000000..6ced16fb --- /dev/null +++ b/src/api/java/baritone/api/process/IFarmProcess.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.process; + +public interface IFarmProcess extends IBaritoneProcess { + + void farm(); +} diff --git a/src/api/java/baritone/api/process/IGetToBlockProcess.java b/src/api/java/baritone/api/process/IGetToBlockProcess.java index ff3dc9b5..a5a35577 100644 --- a/src/api/java/baritone/api/process/IGetToBlockProcess.java +++ b/src/api/java/baritone/api/process/IGetToBlockProcess.java @@ -25,4 +25,6 @@ import net.minecraft.block.Block; public interface IGetToBlockProcess extends IBaritoneProcess { void getToBlock(Block block); + + boolean blacklistClosest(); } diff --git a/src/api/java/baritone/api/process/IMineProcess.java b/src/api/java/baritone/api/process/IMineProcess.java index 7ebabc9c..9cd76a09 100644 --- a/src/api/java/baritone/api/process/IMineProcess.java +++ b/src/api/java/baritone/api/process/IMineProcess.java @@ -17,8 +17,12 @@ package baritone.api.process; +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.BlockOptionalMetaLookup; import net.minecraft.block.Block; +import java.util.stream.Stream; + /** * @author Brady * @since 9/23/2018 @@ -28,9 +32,9 @@ public interface IMineProcess extends IBaritoneProcess { /** * Begin to search for and mine the specified blocks until * the number of specified items to get from the blocks that - * are mined. This is based on the first target block to mine. + * are mined. * - * @param quantity The number of items to get from blocks mined + * @param quantity The total number of items to get * @param blocks The blocks to mine */ void mineByName(int quantity, String... blocks); @@ -41,9 +45,18 @@ public interface IMineProcess extends IBaritoneProcess { * are mined. This is based on the first target block to mine. * * @param quantity The number of items to get from blocks mined - * @param blocks The blocks to mine + * @param filter The blocks to mine */ - void mine(int quantity, Block... blocks); + void mine(int quantity, BlockOptionalMetaLookup filter); + + /** + * Begin to search for and mine the specified blocks. + * + * @param filter The blocks to mine + */ + default void mine(BlockOptionalMetaLookup filter) { + mine(0, filter); + } /** * Begin to search for and mine the specified blocks. @@ -54,6 +67,38 @@ public interface IMineProcess extends IBaritoneProcess { mineByName(0, blocks); } + /** + * Begin to search for and mine the specified blocks. + * + * @param boms The blocks to mine + */ + default void mine(int quantity, BlockOptionalMeta... boms) { + mine(quantity, new BlockOptionalMetaLookup(boms)); + } + + /** + * Begin to search for and mine the specified blocks. + * + * @param boms The blocks to mine + */ + default void mine(BlockOptionalMeta... boms) { + mine(0, boms); + } + + /** + * Begin to search for and mine the specified blocks. + * + * @param quantity The total number of items to get + * @param blocks The blocks to mine + */ + default void mine(int quantity, Block... blocks) { + mine(quantity, new BlockOptionalMetaLookup( + Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new) + )); + } + /** * Begin to search for and mine the specified blocks. * diff --git a/src/api/java/baritone/api/process/PathingCommand.java b/src/api/java/baritone/api/process/PathingCommand.java index 2caef158..61445f8a 100644 --- a/src/api/java/baritone/api/process/PathingCommand.java +++ b/src/api/java/baritone/api/process/PathingCommand.java @@ -53,4 +53,9 @@ public class PathingCommand { this.goal = goal; this.commandType = commandType; } + + @Override + public String toString() { + return commandType + " " + goal; + } } diff --git a/src/api/java/baritone/api/process/PathingCommandType.java b/src/api/java/baritone/api/process/PathingCommandType.java index da64748a..af25591a 100644 --- a/src/api/java/baritone/api/process/PathingCommandType.java +++ b/src/api/java/baritone/api/process/PathingCommandType.java @@ -51,5 +51,10 @@ public enum PathingCommandType { *

* Cancel the current path if the goals are not equal */ - FORCE_REVALIDATE_GOAL_AND_PATH + FORCE_REVALIDATE_GOAL_AND_PATH, + + /** + * Go and ask the next process what to do + */ + DEFER } diff --git a/src/api/java/baritone/api/schematic/AbstractSchematic.java b/src/api/java/baritone/api/schematic/AbstractSchematic.java new file mode 100644 index 00000000..7a24e803 --- /dev/null +++ b/src/api/java/baritone/api/schematic/AbstractSchematic.java @@ -0,0 +1,48 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; + +public abstract class AbstractSchematic implements ISchematic { + + protected int x; + protected int y; + protected int z; + + public AbstractSchematic(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public int widthX() { + return x; + } + + @Override + public int heightY() { + return y; + } + + @Override + public int lengthZ() { + return z; + } +} diff --git a/src/api/java/baritone/api/schematic/CompositeSchematic.java b/src/api/java/baritone/api/schematic/CompositeSchematic.java new file mode 100644 index 00000000..21df29b4 --- /dev/null +++ b/src/api/java/baritone/api/schematic/CompositeSchematic.java @@ -0,0 +1,75 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +import java.util.ArrayList; +import java.util.List; + +public class CompositeSchematic extends AbstractSchematic { + + private final List schematics; + private CompositeSchematicEntry[] schematicArr; + + private void recalcArr() { + schematicArr = schematics.toArray(new CompositeSchematicEntry[0]); + for (CompositeSchematicEntry entry : schematicArr) { + this.x = Math.max(x, entry.x + entry.schematic.widthX()); + this.y = Math.max(y, entry.y + entry.schematic.heightY()); + this.z = Math.max(z, entry.z + entry.schematic.lengthZ()); + } + } + + public CompositeSchematic(int x, int y, int z) { + super(x, y, z); + schematics = new ArrayList<>(); + recalcArr(); + } + + public void put(ISchematic extra, int x, int y, int z) { + schematics.add(new CompositeSchematicEntry(extra, x, y, z)); + recalcArr(); + } + + private CompositeSchematicEntry getSchematic(int x, int y, int z, IBlockState currentState) { + for (CompositeSchematicEntry entry : schematicArr) { + if (x >= entry.x && y >= entry.y && z >= entry.z && + entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState)) { + return entry; + } + } + return null; + } + + @Override + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + CompositeSchematicEntry entry = getSchematic(x, y, z, currentState); + return entry != null && entry.schematic.inSchematic(x - entry.x, y - entry.y, z - entry.z, currentState); + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + CompositeSchematicEntry entry = getSchematic(x, y, z, current); + if (entry == null) { + throw new IllegalStateException("couldn't find schematic for this position"); + } + return entry.schematic.desiredState(x - entry.x, y - entry.y, z - entry.z, current, approxPlaceable); + } +} diff --git a/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java new file mode 100644 index 00000000..a26f356b --- /dev/null +++ b/src/api/java/baritone/api/schematic/CompositeSchematicEntry.java @@ -0,0 +1,35 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; + +public class CompositeSchematicEntry { + + public final ISchematic schematic; + public final int x; + public final int y; + public final int z; + + public CompositeSchematicEntry(ISchematic schematic, int x, int y, int z) { + this.schematic = schematic; + this.x = x; + this.y = y; + this.z = z; + } +} diff --git a/src/api/java/baritone/api/schematic/FillSchematic.java b/src/api/java/baritone/api/schematic/FillSchematic.java new file mode 100644 index 00000000..aaaeb5dd --- /dev/null +++ b/src/api/java/baritone/api/schematic/FillSchematic.java @@ -0,0 +1,53 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.BlockOptionalMeta; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; + +import java.util.List; + +public class FillSchematic extends AbstractSchematic { + + private final BlockOptionalMeta bom; + + public FillSchematic(int x, int y, int z, BlockOptionalMeta bom) { + super(x, y, z); + this.bom = bom; + } + + public BlockOptionalMeta getBom() { + return bom; + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + if (bom.matches(current)) { + return current; + } else if (current.getBlock() != Blocks.AIR) { + return Blocks.AIR.getDefaultState(); + } + for (IBlockState placeable : approxPlaceable) { + if (bom.matches(placeable)) { + return placeable; + } + } + return bom.getAnyBlockState(); + } +} diff --git a/src/api/java/baritone/api/schematic/MaskSchematic.java b/src/api/java/baritone/api/schematic/MaskSchematic.java new file mode 100644 index 00000000..fa9b8126 --- /dev/null +++ b/src/api/java/baritone/api/schematic/MaskSchematic.java @@ -0,0 +1,45 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +import java.util.List; + +public abstract class MaskSchematic extends AbstractSchematic { + + private final ISchematic schematic; + + public MaskSchematic(ISchematic schematic) { + super(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); + this.schematic = schematic; + } + + protected abstract boolean partOfMask(int x, int y, int z, IBlockState currentState); + + @Override + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + return schematic.inSchematic(x, y, z, currentState) && partOfMask(x, y, z, currentState); + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return schematic.desiredState(x, y, z, current, approxPlaceable); + } +} diff --git a/src/api/java/baritone/api/schematic/ReplaceSchematic.java b/src/api/java/baritone/api/schematic/ReplaceSchematic.java new file mode 100644 index 00000000..988cfd7f --- /dev/null +++ b/src/api/java/baritone/api/schematic/ReplaceSchematic.java @@ -0,0 +1,42 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.BlockOptionalMetaLookup; +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class ReplaceSchematic extends MaskSchematic { + + private final BlockOptionalMetaLookup filter; + private final Boolean[][][] cache; + + public ReplaceSchematic(ISchematic schematic, BlockOptionalMetaLookup filter) { + super(schematic); + this.filter = filter; + this.cache = new Boolean[widthX()][heightY()][lengthZ()]; + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + if (cache[x][y][z] == null) { + cache[x][y][z] = filter.has(currentState); + } + return cache[x][y][z]; + } +} diff --git a/src/api/java/baritone/api/schematic/ShellSchematic.java b/src/api/java/baritone/api/schematic/ShellSchematic.java new file mode 100644 index 00000000..4ead61b6 --- /dev/null +++ b/src/api/java/baritone/api/schematic/ShellSchematic.java @@ -0,0 +1,33 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class ShellSchematic extends MaskSchematic { + + public ShellSchematic(ISchematic schematic) { + super(schematic); + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + return x == 0 || y == 0 || z == 0 || x == widthX() - 1 || y == heightY() - 1 || z == lengthZ() - 1; + } +} diff --git a/src/api/java/baritone/api/schematic/WallsSchematic.java b/src/api/java/baritone/api/schematic/WallsSchematic.java new file mode 100644 index 00000000..e05de1e7 --- /dev/null +++ b/src/api/java/baritone/api/schematic/WallsSchematic.java @@ -0,0 +1,33 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +public class WallsSchematic extends MaskSchematic { + + public WallsSchematic(ISchematic schematic) { + super(schematic); + } + + @Override + protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { + return x == 0 || z == 0 || x == widthX() - 1 || z == lengthZ() - 1; + } +} diff --git a/src/api/java/baritone/api/selection/ISelection.java b/src/api/java/baritone/api/selection/ISelection.java new file mode 100644 index 00000000..59651feb --- /dev/null +++ b/src/api/java/baritone/api/selection/ISelection.java @@ -0,0 +1,91 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.selection; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3i; + +/** + * A selection is an immutable object representing the current selection. The selection is commonly used for certain + * types of build commands, however it can be used for anything. + */ +public interface ISelection { + + /** + * @return The first corner of this selection. This is meant to preserve the user's original first corner. + */ + BetterBlockPos pos1(); + + /** + * @return The second corner of this selection. This is meant to preserve the user's original second corner. + */ + BetterBlockPos pos2(); + + /** + * @return The {@link BetterBlockPos} with the lowest x, y, and z position in the selection. + */ + BetterBlockPos min(); + + /** + * @return The opposite corner from the {@link #min()}. + */ + BetterBlockPos max(); + + /** + * @return The size of this ISelection. + */ + Vec3i size(); + + /** + * @return An {@link AxisAlignedBB} encompassing all blocks in this selection. + */ + AxisAlignedBB aabb(); + + /** + * Returns a new {@link ISelection} expanded in the specified direction by the specified number of blocks. + * + * @param direction The direction to expand the selection. + * @param blocks How many blocks to expand it. + * @return A new selection, expanded as specified. + */ + ISelection expand(EnumFacing direction, int blocks); + + /** + * Returns a new {@link ISelection} contracted in the specified direction by the specified number of blocks. + *

+ * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it + * is DOWN, the top of the selection will be shifted down. + * + * @param direction The direction to contract the selection. + * @param blocks How many blocks to contract it. + * @return A new selection, contracted as specified. + */ + ISelection contract(EnumFacing direction, int blocks); + + /** + * Returns a new {@link ISelection} shifted in the specified direction by the specified number of blocks. This moves + * the whole selection. + * + * @param direction The direction to shift the selection. + * @param blocks How many blocks to shift it. + * @return A new selection, shifted as specified. + */ + ISelection shift(EnumFacing direction, int blocks); +} diff --git a/src/api/java/baritone/api/selection/ISelectionManager.java b/src/api/java/baritone/api/selection/ISelectionManager.java new file mode 100644 index 00000000..de5ee288 --- /dev/null +++ b/src/api/java/baritone/api/selection/ISelectionManager.java @@ -0,0 +1,116 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.selection; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; + +/** + * The selection manager handles setting Baritone's selections. You can set the selection here, as well as retrieving + * the current selection. + */ +public interface ISelectionManager { + + /** + * Adds a new selection. The added selection is returned. + * + * @param selection The new selection to add. + */ + ISelection addSelection(ISelection selection); + + /** + * Adds a new {@link ISelection} constructed from the given block positions. The new selection is returned. + * + * @param pos1 One corner of the selection + * @param pos2 The new corner of the selection + */ + ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2); + + /** + * Removes the selection from the current selections. + * + * @param selection The selection to remove. + * @return The removed selection. + */ + ISelection removeSelection(ISelection selection); + + /** + * Removes all selections. + * + * @return The selections that were removed, sorted from oldest to newest.. + */ + ISelection[] removeAllSelections(); + + /** + * @return The current selections, sorted from oldest to newest. + */ + ISelection[] getSelections(); + + /** + * For anything expecting only one selection, this method is provided. However, to enforce multi-selection support, + * this method will only return a selection if there is ONLY one. + * + * @return The only selection, or null if there isn't only one. + */ + ISelection getOnlySelection(); + + /** + * This method will always return the last selection. ONLY use this if you want to, for example, modify the most + * recent selection based on user input. ALWAYS use {@link #getOnlySelection()} or, ideally, + * {@link #getSelections()} for retrieving the content of selections. + * + * @return The last selection, or null if it doesn't exist. + */ + ISelection getLastSelection(); + + /** + * Replaces the specified {@link ISelection} with one expanded in the specified direction by the specified number of + * blocks. Returns the new selection. + * + * @param selection The selection to expand. + * @param direction The direction to expand the selection. + * @param blocks How many blocks to expand it. + * @return The new selection, expanded as specified. + */ + ISelection expand(ISelection selection, EnumFacing direction, int blocks); + + /** + * Replaces the specified {@link ISelection} with one contracted in the specified direction by the specified number + * of blocks. + *

+ * Note that, for example, if the direction specified is UP, the bottom of the selection will be shifted up. If it + * is DOWN, the top of the selection will be shifted down. + * + * @param selection The selection to contract. + * @param direction The direction to contract the selection. + * @param blocks How many blocks to contract it. + * @return The new selection, contracted as specified. + */ + ISelection contract(ISelection selection, EnumFacing direction, int blocks); + + /** + * Replaces the specified {@link ISelection} with one shifted in the specified direction by the specified number of + * blocks. This moves the whole selection. + * + * @param selection The selection to shift. + * @param direction The direction to shift the selection. + * @param blocks How many blocks to shift it. + * @return The new selection, shifted as specified. + */ + ISelection shift(ISelection selection, EnumFacing direction, int blocks); +} diff --git a/src/api/java/baritone/api/utils/BetterBlockPos.java b/src/api/java/baritone/api/utils/BetterBlockPos.java index a1a3cb32..a9b02445 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); @@ -61,6 +80,8 @@ public final class BetterBlockPos extends BlockPos { } public static long longHash(int x, int y, int z) { + // TODO use the same thing as BlockPos.fromLong(); + // invertibility would be incredibly useful /* * This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name) * @@ -180,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 00000000..c65b6d3d --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockOptionalMeta.java @@ -0,0 +1,299 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import baritone.api.accessor.IItemStack; +import com.google.common.collect.ImmutableSet; +import net.minecraft.block.*; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Consumer; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public final class BlockOptionalMeta { + + private final Block block; + private final int meta; + private final boolean noMeta; + private final Set blockstates; + private final ImmutableSet stateHashes; + private final ImmutableSet stackHashes; + private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$"); + private static final Map normalizations; + + public BlockOptionalMeta(@Nonnull Block block, @Nullable Integer meta) { + this.block = block; + this.noMeta = meta == null; + this.meta = noMeta ? 0 : meta; + this.blockstates = getStates(block, meta); + this.stateHashes = getStateHashes(blockstates); + this.stackHashes = getStackHashes(blockstates); + } + + public BlockOptionalMeta(@Nonnull Block block) { + this(block, null); + } + + public BlockOptionalMeta(@Nonnull String selector) { + Matcher matcher = pattern.matcher(selector); + + if (!matcher.find()) { + throw new IllegalArgumentException("invalid block selector"); + } + + MatchResult matchResult = matcher.toMatchResult(); + noMeta = matchResult.group(2) == null; + + ResourceLocation id = new ResourceLocation(matchResult.group(1)); + + if (!Block.REGISTRY.containsKey(id)) { + throw new IllegalArgumentException("Invalid block ID"); + } + + block = Block.REGISTRY.getObject(id); + meta = noMeta ? 0 : Integer.parseInt(matchResult.group(2)); + blockstates = getStates(block, getMeta()); + stateHashes = getStateHashes(blockstates); + stackHashes = getStackHashes(blockstates); + } + + static { + Map _normalizations = new HashMap<>(); + Consumer put = instance -> _normalizations.put(instance.getClass(), instance); + put.accept(EnumFacing.NORTH); + put.accept(EnumFacing.Axis.Y); + put.accept(BlockLog.EnumAxis.Y); + put.accept(BlockStairs.EnumHalf.BOTTOM); + put.accept(BlockStairs.EnumShape.STRAIGHT); + put.accept(BlockLever.EnumOrientation.DOWN_X); + put.accept(BlockDoublePlant.EnumBlockHalf.LOWER); + put.accept(BlockSlab.EnumBlockHalf.BOTTOM); + put.accept(BlockDoor.EnumDoorHalf.LOWER); + put.accept(BlockDoor.EnumHingePosition.LEFT); + put.accept(BlockBed.EnumPartType.HEAD); + put.accept(BlockRailBase.EnumRailDirection.NORTH_SOUTH); + put.accept(BlockTrapDoor.DoorHalf.BOTTOM); + _normalizations.put(BlockBanner.ROTATION, 0); + _normalizations.put(BlockBed.OCCUPIED, false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[0], false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[1], false); + _normalizations.put(BlockBrewingStand.HAS_BOTTLE[2], false); + _normalizations.put(BlockButton.POWERED, false); + // _normalizations.put(BlockCactus.AGE, 0); + // _normalizations.put(BlockCauldron.LEVEL, 0); + // _normalizations.put(BlockChorusFlower.AGE, 0); + _normalizations.put(BlockChorusPlant.NORTH, false); + _normalizations.put(BlockChorusPlant.EAST, false); + _normalizations.put(BlockChorusPlant.SOUTH, false); + _normalizations.put(BlockChorusPlant.WEST, false); + _normalizations.put(BlockChorusPlant.UP, false); + _normalizations.put(BlockChorusPlant.DOWN, false); + // _normalizations.put(BlockCocoa.AGE, 0); + // _normalizations.put(BlockCrops.AGE, 0); + _normalizations.put(BlockDirt.SNOWY, false); + _normalizations.put(BlockDoor.OPEN, false); + _normalizations.put(BlockDoor.POWERED, false); + // _normalizations.put(BlockFarmland.MOISTURE, 0); + _normalizations.put(BlockFence.NORTH, false); + _normalizations.put(BlockFence.EAST, false); + _normalizations.put(BlockFence.WEST, false); + _normalizations.put(BlockFence.SOUTH, false); + // _normalizations.put(BlockFenceGate.POWERED, false); + // _normalizations.put(BlockFenceGate.IN_WALL, false); + _normalizations.put(BlockFire.AGE, 0); + _normalizations.put(BlockFire.NORTH, false); + _normalizations.put(BlockFire.EAST, false); + _normalizations.put(BlockFire.SOUTH, false); + _normalizations.put(BlockFire.WEST, false); + _normalizations.put(BlockFire.UPPER, false); + // _normalizations.put(BlockFrostedIce.AGE, 0); + _normalizations.put(BlockGrass.SNOWY, false); + // _normalizations.put(BlockHopper.ENABLED, true); + // _normalizations.put(BlockLever.POWERED, false); + // _normalizations.put(BlockLiquid.LEVEL, 0); + // _normalizations.put(BlockMycelium.SNOWY, false); + // _normalizations.put(BlockNetherWart.AGE, false); + _normalizations.put(BlockLeaves.CHECK_DECAY, false); + // _normalizations.put(BlockLeaves.DECAYABLE, false); + // _normalizations.put(BlockObserver.POWERED, false); + _normalizations.put(BlockPane.NORTH, false); + _normalizations.put(BlockPane.EAST, false); + _normalizations.put(BlockPane.WEST, false); + _normalizations.put(BlockPane.SOUTH, false); + // _normalizations.put(BlockPistonBase.EXTENDED, false); + // _normalizations.put(BlockPressurePlate.POWERED, false); + // _normalizations.put(BlockPressurePlateWeighted.POWER, false); + _normalizations.put(BlockQuartz.EnumType.LINES_X, BlockQuartz.EnumType.LINES_Y); + _normalizations.put(BlockQuartz.EnumType.LINES_Z, BlockQuartz.EnumType.LINES_Y); + // _normalizations.put(BlockRailDetector.POWERED, false); + // _normalizations.put(BlockRailPowered.POWERED, false); + _normalizations.put(BlockRedstoneWire.NORTH, false); + _normalizations.put(BlockRedstoneWire.EAST, false); + _normalizations.put(BlockRedstoneWire.SOUTH, false); + _normalizations.put(BlockRedstoneWire.WEST, false); + // _normalizations.put(BlockReed.AGE, false); + _normalizations.put(BlockSapling.STAGE, 0); + _normalizations.put(BlockSkull.NODROP, false); + _normalizations.put(BlockStandingSign.ROTATION, 0); + _normalizations.put(BlockStem.AGE, 0); + _normalizations.put(BlockTripWire.NORTH, false); + _normalizations.put(BlockTripWire.EAST, false); + _normalizations.put(BlockTripWire.WEST, false); + _normalizations.put(BlockTripWire.SOUTH, false); + _normalizations.put(BlockVine.NORTH, false); + _normalizations.put(BlockVine.EAST, false); + _normalizations.put(BlockVine.SOUTH, false); + _normalizations.put(BlockVine.WEST, false); + _normalizations.put(BlockVine.UP, false); + _normalizations.put(BlockWall.UP, false); + _normalizations.put(BlockWall.NORTH, false); + _normalizations.put(BlockWall.EAST, false); + _normalizations.put(BlockWall.WEST, false); + _normalizations.put(BlockWall.SOUTH, false); + normalizations = Collections.unmodifiableMap(_normalizations); + } + + private static , P extends IProperty> P castToIProperty(Object value) { + //noinspection unchecked + return (P) value; + } + + private static , P extends IProperty> C castToIPropertyValue(P iproperty, Object value) { + //noinspection unchecked + return (C) value; + } + + public static IBlockState normalize(IBlockState state) { + IBlockState newState = state; + + for (IProperty property : state.getProperties().keySet()) { + Class valueClass = property.getValueClass(); + if (normalizations.containsKey(property)) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(property)) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(state.getValue(property))) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(state.getValue(property))) + ); + } catch (IllegalArgumentException ignored) {} + } else if (normalizations.containsKey(valueClass)) { + try { + newState = newState.withProperty( + castToIProperty(property), + castToIPropertyValue(property, normalizations.get(valueClass)) + ); + } catch (IllegalArgumentException ignored) {} + } + } + + return newState; + } + + public static int stateMeta(IBlockState state) { + return state.getBlock().getMetaFromState(normalize(state)); + } + + private static Set getStates(@Nonnull Block block, @Nullable Integer meta) { + return block.getBlockState().getValidStates().stream() + .filter(blockstate -> meta == null || stateMeta(blockstate) == meta) + .collect(Collectors.toSet()); + } + + private static ImmutableSet getStateHashes(Set blockstates) { + return ImmutableSet.copyOf( + blockstates.stream() + .map(IBlockState::hashCode) + .toArray(Integer[]::new) + ); + } + + private static ImmutableSet getStackHashes(Set blockstates) { + //noinspection ConstantConditions + return ImmutableSet.copyOf( + blockstates.stream() + .map(state -> new ItemStack( + state.getBlock().getItemDropped(state, new Random(), 0), + state.getBlock().damageDropped(state) + )) + .map(stack -> ((IItemStack) (Object) stack).getBaritoneHash()) + .toArray(Integer[]::new) + ); + } + + public Block getBlock() { + return block; + } + + public Integer getMeta() { + return noMeta ? null : meta; + } + + public boolean matches(@Nonnull Block block) { + return block == this.block; + } + + public boolean matches(@Nonnull IBlockState blockstate) { + Block block = blockstate.getBlock(); + return block == this.block && stateHashes.contains(blockstate.hashCode()); + } + + public boolean matches(ItemStack stack) { + //noinspection ConstantConditions + int hash = ((IItemStack) (Object) stack).getBaritoneHash(); + + if (noMeta) { + hash -= stack.getItemDamage(); + } + + return stackHashes.contains(hash); + } + + @Override + public String toString() { + return String.format("BlockOptionalMeta{block=%s,meta=%s}", block, getMeta()); + } + + public static IBlockState blockStateFromStack(ItemStack stack) { + //noinspection deprecation + return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata()); + } + + public IBlockState getAnyBlockState() { + if (blockstates.size() > 0) { + return blockstates.iterator().next(); + } + + return null; + } +} diff --git a/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java new file mode 100644 index 00000000..041c6162 --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java @@ -0,0 +1,95 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class BlockOptionalMetaLookup { + + private final BlockOptionalMeta[] boms; + + public BlockOptionalMetaLookup(BlockOptionalMeta... boms) { + this.boms = boms; + } + + public BlockOptionalMetaLookup(Block... blocks) { + this.boms = Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public BlockOptionalMetaLookup(List blocks) { + this.boms = blocks.stream() + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public BlockOptionalMetaLookup(String... blocks) { + this.boms = Stream.of(blocks) + .map(BlockOptionalMeta::new) + .toArray(BlockOptionalMeta[]::new); + } + + public boolean has(Block block) { + for (BlockOptionalMeta bom : boms) { + if (bom.getBlock() == block) { + return true; + } + } + + return false; + } + + public boolean has(IBlockState state) { + for (BlockOptionalMeta bom : boms) { + if (bom.matches(state)) { + return true; + } + } + + return false; + } + + public boolean has(ItemStack stack) { + for (BlockOptionalMeta bom : boms) { + if (bom.matches(stack)) { + return true; + } + } + + return false; + } + + public List blocks() { + return Arrays.asList(boms); + } + + @Override + public String toString() { + return String.format( + "BlockOptionalMetaLookup{%s}", + Arrays.toString(boms) + ); + } +} diff --git a/src/api/java/baritone/api/utils/BlockUtils.java b/src/api/java/baritone/api/utils/BlockUtils.java new file mode 100644 index 00000000..43a97f83 --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockUtils.java @@ -0,0 +1,67 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.util.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; + +public class BlockUtils { + + private static transient Map resourceCache = new HashMap<>(); + + public static String blockToString(Block block) { + ResourceLocation loc = Block.REGISTRY.getNameForObject(block); + String name = loc.getPath(); // normally, only write the part after the minecraft: + if (!loc.getNamespace().equals("minecraft")) { + // Baritone is running on top of forge with mods installed, perhaps? + name = loc.toString(); // include the namespace with the colon + } + return name; + } + + public static Block stringToBlockRequired(String name) { + Block block = stringToBlockNullable(name); + + if (block == null) { + throw new IllegalArgumentException(String.format("Invalid block name %s", name)); + } + + return block; + } + + public static Block stringToBlockNullable(String name) { + // do NOT just replace this with a computeWithAbsent, it isn't thread safe + Block block = resourceCache.get(name); // map is never mutated in place so this is safe + if (block != null) { + return block; + } + if (resourceCache.containsKey(name)) { + return null; // cached as null + } + block = Block.getBlockFromName(name.contains(":") ? name : "minecraft:" + name); + Map copy = new HashMap<>(resourceCache); // read only copy is safe, wont throw concurrentmodification + copy.put(name, block); + resourceCache = copy; + return block; + } + + private BlockUtils() {} +} diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java new file mode 100755 index 00000000..91d7890f --- /dev/null +++ b/src/api/java/baritone/api/utils/Helper.java @@ -0,0 +1,111 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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 net.minecraft.client.Minecraft; +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 + */ +public interface Helper { + + /** + * Instance of {@link Helper}. Used for static-context reference. + */ + Helper HELPER = new Helper() {}; + + /** + * Instance of the game + */ + Minecraft mc = Minecraft.getMinecraft(); + + static ITextComponent getPrefix() { + // Inner text component + ITextComponent baritone = new TextComponentString(BaritoneAPI.getSettings().shortBaritonePrefix.value ? "B" : "Baritone"); + baritone.getStyle().setColor(TextFormatting.LIGHT_PURPLE); + + // Outer brackets + ITextComponent prefix = new TextComponentString(""); + prefix.getStyle().setColor(TextFormatting.DARK_PURPLE); + prefix.appendText("["); + prefix.appendSibling(baritone); + prefix.appendText("]"); + + return prefix; + } + + /** + * Send a message to chat only if chatDebug is on + * + * @param message The message to display in chat + */ + default void logDebug(String message) { + if (!BaritoneAPI.getSettings().chatDebug.value) { + //System.out.println("Suppressed debug message:"); + //System.out.println(message); + return; + } + logDirect(message); + } + + /** + * Send components to chat with the [Baritone] prefix + * + * @param components The components to send + */ + default void logDirect(ITextComponent... components) { + ITextComponent component = new TextComponentString(""); + component.appendSibling(getPrefix()); + component.appendSibling(new TextComponentString(" ")); + Arrays.asList(components).forEach(component::appendSibling); + Minecraft.getMinecraft().addScheduledTask(() -> BaritoneAPI.getSettings().logger.value.accept(component)); + } + + /** + * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a + * direct response to a chat command) + * + * @param message The message to display in chat + * @param color The color to print that message in + */ + default void logDirect(String message, TextFormatting color) { + Stream.of(message.split("\n")).forEach(line -> { + ITextComponent component = new TextComponentString(line.replace("\t", " ")); + component.getStyle().setColor(color); + logDirect(component); + }); + } + + /** + * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a + * direct response to a chat command) + * + * @param message The message to display in chat + */ + default void logDirect(String message) { + logDirect(message, TextFormatting.GRAY); + } +} diff --git a/src/api/java/baritone/api/utils/IInputOverrideHandler.java b/src/api/java/baritone/api/utils/IInputOverrideHandler.java index 03f6e4dd..3cfb7438 100644 --- a/src/api/java/baritone/api/utils/IInputOverrideHandler.java +++ b/src/api/java/baritone/api/utils/IInputOverrideHandler.java @@ -19,7 +19,6 @@ package baritone.api.utils; import baritone.api.behavior.IBehavior; import baritone.api.utils.input.Input; -import net.minecraft.client.settings.KeyBinding; /** * @author Brady @@ -27,8 +26,6 @@ import net.minecraft.client.settings.KeyBinding; */ public interface IInputOverrideHandler extends IBehavior { - Boolean isInputForcedDown(KeyBinding key); - boolean isInputForcedDown(Input input); void setInputForceState(Input input, boolean forced); diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java index 3fdca1fa..d99290ec 100644 --- a/src/api/java/baritone/api/utils/IPlayerContext.java +++ b/src/api/java/baritone/api/utils/IPlayerContext.java @@ -49,9 +49,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; } @@ -73,20 +85,26 @@ public interface IPlayerContext { * @return The position of the highlighted block */ default Optional getSelectedBlock() { - if (objectMouseOver() != null && objectMouseOver().typeOfHit == RayTraceResult.Type.BLOCK) { - return Optional.of(objectMouseOver().getBlockPos()); + RayTraceResult result = objectMouseOver(); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { + return Optional.of(result.getBlockPos()); } return Optional.empty(); } + default boolean isLookingAt(BlockPos pos) { + return getSelectedBlock().equals(Optional.of(pos)); + } + /** * Returns the entity that the crosshair is currently placed over. Updated once per tick. * * @return The entity */ default Optional getSelectedEntity() { - if (objectMouseOver() != null && objectMouseOver().typeOfHit == RayTraceResult.Type.ENTITY) { - return Optional.of(objectMouseOver().entityHit); + RayTraceResult result = objectMouseOver(); + if (result != null && result.typeOfHit == RayTraceResult.Type.ENTITY) { + return Optional.of(result.entityHit); } return Optional.empty(); } diff --git a/src/api/java/baritone/api/utils/IPlayerController.java b/src/api/java/baritone/api/utils/IPlayerController.java index 2b3c7fc5..f395c542 100644 --- a/src/api/java/baritone/api/utils/IPlayerController.java +++ b/src/api/java/baritone/api/utils/IPlayerController.java @@ -17,13 +17,17 @@ package baritone.api.utils; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ClickType; import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameType; +import net.minecraft.world.World; /** * @author Brady @@ -31,6 +35,10 @@ import net.minecraft.world.GameType; */ public interface IPlayerController { + void syncHeldItem(); + + boolean hasBrokenBlock(); + boolean onPlayerDamageBlock(BlockPos pos, EnumFacing side); void resetBlockRemoving(); @@ -41,7 +49,13 @@ public interface IPlayerController { GameType getGameType(); - RayTraceResult objectMouseOver(); + 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 new file mode 100644 index 00000000..2adcbdd5 --- /dev/null +++ b/src/api/java/baritone/api/utils/ISchematic.java @@ -0,0 +1,90 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.state.IBlockState; +import net.minecraft.util.EnumFacing; + +import java.util.List; + +/** + * Basic representation of a schematic. Provides the dimensions and the desired state for a given position relative to + * the origin. + * + * @author leijurv + */ +public interface ISchematic { + + /** + * Does the block at this coordinate matter to the schematic? + *

+ * Normally just a check for if the coordinate is in the cube. + *

+ * 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 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, IBlockState currentState) { + return x >= 0 && x < widthX() && y >= 0 && y < heightY() && z >= 0 && z < lengthZ(); + } + + default int size(EnumFacing.Axis axis) { + switch (axis) { + case X: + return widthX(); + case Y: + return heightY(); + case Z: + return lengthZ(); + default: + throw new UnsupportedOperationException(axis + ""); + } + } + + /** + * 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 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 current, List approxPlaceable); + + /** + * @return The width (X axis length) of this schematic + */ + int widthX(); + + /** + * @return The height (Y axis length) of this schematic + */ + int heightY(); + + /** + * @return The length (Z axis length) of this schematic + */ + int lengthZ(); +} diff --git a/src/api/java/baritone/api/utils/MyChunkPos.java b/src/api/java/baritone/api/utils/MyChunkPos.java new file mode 100644 index 00000000..7cda5aa3 --- /dev/null +++ b/src/api/java/baritone/api/utils/MyChunkPos.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; + +import com.google.gson.annotations.SerializedName; + +/** + * Need a non obfed chunkpos that we can load using GSON + */ +public class MyChunkPos { + + @SerializedName("x") + public int x; + + @SerializedName("z") + public int z; + + @Override + public String toString() { + return x + ", " + z; + } +} diff --git a/src/api/java/baritone/api/utils/Rotation.java b/src/api/java/baritone/api/utils/Rotation.java index 7f93547b..54f63ebf 100644 --- a/src/api/java/baritone/api/utils/Rotation.java +++ b/src/api/java/baritone/api/utils/Rotation.java @@ -117,8 +117,12 @@ public class Rotation { * @return are they really close */ public boolean isReallyCloseTo(Rotation other) { - float yawDiff = Math.abs(this.yaw - other.yaw); // you cant fool me - return (yawDiff < 0.01 || yawDiff > 359.9) && Math.abs(this.pitch - other.pitch) < 0.01; + return yawIsReallyClose(other) && Math.abs(this.pitch - other.pitch) < 0.01; + } + + public boolean yawIsReallyClose(Rotation other) { + float yawDiff = Math.abs(normalizeYaw(yaw) - normalizeYaw(other.yaw)); // you cant fool me + return (yawDiff < 0.01 || yawDiff > 359.99); } /** @@ -147,4 +151,9 @@ public class Rotation { } return newYaw; } + + @Override + public String toString() { + return "Yaw: " + yaw + ", Pitch: " + pitch; + } } diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 9352b7fe..e19f531d 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -78,6 +78,9 @@ public final class RotationUtils { * @return The wrapped angles */ public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { + if (current.yawIsReallyClose(target)) { + return new Rotation(current.getYaw(), target.getPitch()); + } return target.subtract(current).normalize().add(current); } @@ -102,7 +105,7 @@ public final class RotationUtils { * @param dest The destination position * @return The rotation from the origin to the destination */ - public static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest) { + private static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest) { double[] delta = {orig.x - dest.x, orig.y - dest.y, orig.z - dest.z}; double yaw = MathHelper.atan2(delta[0], -delta[2]); double dist = Math.sqrt(delta[0] * delta[0] + delta[2] * delta[2]); @@ -151,7 +154,7 @@ public final class RotationUtils { */ public static Optional reachable(EntityPlayerSP entity, BlockPos pos, double blockReachDistance) { IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); - if (pos.equals(baritone.getPlayerContext().getSelectedBlock().orElse(null))) { + if (baritone.getPlayerContext().isLookingAt(pos)) { /* * why add 0.0001? * to indicate that we actually have a desired pitch @@ -196,7 +199,7 @@ public final class RotationUtils { * @return The optional rotation */ public static Optional reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance) { - Rotation rotation = calcRotationFromVec3d(entity.getPositionEyes(1.0F), offsetPos); + Rotation rotation = calcRotationFromVec3d(entity.getPositionEyes(1.0F), offsetPos, new Rotation(entity.rotationYaw, entity.rotationPitch)); RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance); //System.out.println(result); if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 07b4cd98..f9cb7136 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -17,20 +17,25 @@ package baritone.api.utils; +import baritone.api.BaritoneAPI; import baritone.api.Settings; +import net.minecraft.block.Block; import net.minecraft.item.Item; -import net.minecraft.util.ResourceLocation; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.Vec3i; import java.awt.*; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Matcher; @@ -43,9 +48,7 @@ import static net.minecraft.client.Minecraft.getMinecraft; public class SettingsUtil { private static final Path SETTINGS_PATH = getMinecraft().gameDir.toPath().resolve("baritone").resolve("settings.txt"); - private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?[^ ]+)"); // 2 words separated by spaces - - private static final Map, SettingsIO> map; + private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?.+)"); // key and value split by the first space private static boolean isComment(String line) { return line.startsWith("#") || line.startsWith("//"); @@ -81,6 +84,8 @@ public class SettingsUtil { ex.printStackTrace(); } }); + } catch (NoSuchFileException ignored) { + System.out.println("Baritone settings file not found, resetting."); } catch (Exception ex) { System.out.println("Exception while reading Baritone settings, some settings may be reset to default values!"); ex.printStackTrace(); @@ -89,22 +94,8 @@ public class SettingsUtil { public static synchronized void save(Settings settings) { try (BufferedWriter out = Files.newBufferedWriter(SETTINGS_PATH)) { - for (Settings.Setting setting : settings.allSettings) { - if (setting.get() == null) { - System.out.println("NULL SETTING?" + setting.getName()); - continue; - } - if (setting.getName().equals("logger")) { - continue; // NO - } - if (setting.value == setting.defaultValue) { - continue; - } - SettingsIO io = map.get(setting.getValueClass()); - if (io == null) { - throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting + " " + setting.getName()); - } - out.write(setting.getName() + " " + io.toString.apply(setting.get()) + "\n"); + for (Settings.Setting setting : modifiedSettings(settings)) { + out.write(settingToString(setting) + "\n"); } } catch (Exception ex) { System.out.println("Exception thrown while saving Baritone settings!"); @@ -112,51 +103,207 @@ public class SettingsUtil { } } - private static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { + public static List modifiedSettings(Settings settings) { + List modified = new ArrayList<>(); + for (Settings.Setting setting : settings.allSettings) { + if (setting.value == null) { + System.out.println("NULL SETTING?" + setting.getName()); + continue; + } + if (setting.getName().equals("logger")) { + continue; // NO + } + if (setting.value == setting.defaultValue) { + continue; + } + modified.add(setting); + } + 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"; + } + + return setting.getName() + " " + settingValueToString(setting); + } + + public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { Settings.Setting setting = settings.byLowerName.get(settingName); if (setting == null) { throw new IllegalStateException("No setting by that name"); } Class intendedType = setting.getValueClass(); - SettingsIO ioMethod = map.get(intendedType); - Object parsed = ioMethod.parser.apply(settingValue); + ISettingParser ioMethod = Parser.getParser(setting.getType()); + Object parsed = ioMethod.parse(new ParserContext(setting), settingValue); if (!intendedType.isInstance(parsed)) { throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass()); } setting.value = parsed; } - private enum SettingsIO { + private interface ISettingParser { + + T parse(ParserContext context, String raw); + + String toString(ParserContext context, T value); + + boolean accepts(Type type); + } + + private static class ParserContext { + + private final Settings.Setting setting; + + private ParserContext(Settings.Setting setting) { + this.setting = setting; + } + + private Settings.Setting getSetting() { + return this.setting; + } + } + + private enum Parser implements ISettingParser { + DOUBLE(Double.class, Double::parseDouble), BOOLEAN(Boolean.class, Boolean::parseBoolean), 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, + str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), + color -> color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ), + VEC3I( + Vec3i.class, + str -> new Vec3i(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), + vec -> vec.getX() + "," + vec.getY() + "," + vec.getZ() + ), + BLOCK( + Block.class, + str -> BlockUtils.stringToBlockRequired(str.trim()), + BlockUtils::blockToString + ), + ITEM( + Item.class, + str -> Item.getByNameOrId(str.trim()), + item -> Item.REGISTRY.getNameForObject(item).toString() + ), + LIST() { + @Override + public Object parse(ParserContext context, String raw) { + Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Parser parser = Parser.getParser(type); - ITEM_LIST(ArrayList.class, str -> Stream.of(str.split(",")).map(Item::getByNameOrId).collect(Collectors.toCollection(ArrayList::new)), list -> ((ArrayList) list).stream().map(Item.REGISTRY::getNameForObject).map(ResourceLocation::toString).collect(Collectors.joining(","))), - COLOR(Color.class, str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), color -> color.getRed() + "," + color.getGreen() + "," + color.getBlue()); + return Stream.of(raw.split(",")) + .map(s -> parser.parse(context, s)) + .collect(Collectors.toList()); + } + @Override + public String toString(ParserContext context, Object value) { + Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Parser parser = Parser.getParser(type); - Class klass; - Function parser; - Function toString; + return ((List) value).stream() + .map(o -> parser.toString(context, o)) + .collect(Collectors.joining(",")); + } - SettingsIO(Class klass, Function parser) { - this(klass, parser, Object::toString); + @Override + public boolean accepts(Type type) { + return List.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); + } + }; + + private final Class cla$$; + private final Function parser; + private final Function toString; + + Parser() { + this.cla$$ = null; + this.parser = null; + this.toString = null; } - SettingsIO(Class klass, Function parser, Function toString) { - this.klass = klass; + Parser(Class cla$$, Function parser) { + this(cla$$, parser, Object::toString); + } + + Parser(Class cla$$, Function parser, Function toString) { + this.cla$$ = cla$$; this.parser = parser::apply; this.toString = x -> toString.apply((T) x); } - } - static { - HashMap, SettingsIO> tempMap = new HashMap<>(); - for (SettingsIO type : SettingsIO.values()) { - tempMap.put(type.klass, type); + @Override + public Object parse(ParserContext context, String raw) { + Object parsed = this.parser.apply(raw); + Objects.requireNonNull(parsed); + return parsed; + } + + @Override + public String toString(ParserContext context, Object value) { + return this.toString.apply(value); + } + + @Override + public boolean accepts(Type type) { + return type instanceof Class && this.cla$$.isAssignableFrom((Class) type); + } + + public static Parser getParser(Type type) { + return Stream.of(values()) + .filter(parser -> parser.accepts(type)) + .findFirst().orElse(null); } - map = Collections.unmodifiableMap(tempMap); } } diff --git a/src/api/java/baritone/api/utils/TypeUtils.java b/src/api/java/baritone/api/utils/TypeUtils.java new file mode 100644 index 00000000..457cc449 --- /dev/null +++ b/src/api/java/baritone/api/utils/TypeUtils.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; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * @author Brady + * @since 4/20/2019 + */ +public final class TypeUtils { + + private TypeUtils() {} + + /** + * Resolves the "base type" for the specified type. For example, if the specified + * type is {@code List}, then {@code List.class} will be returned. If the + * specified type is already a class, then it is directly returned. + * + * @param type The type to resolve + * @return The base class + */ + public static Class resolveBaseClass(Type type) { + return type instanceof Class ? (Class) type + : type instanceof ParameterizedType ? (Class) ((ParameterizedType) type).getRawType() + : null; + } +} diff --git a/src/api/java/baritone/api/utils/command/Command.java b/src/api/java/baritone/api/utils/command/Command.java new file mode 100644 index 00000000..ba4ed938 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/Command.java @@ -0,0 +1,86 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.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.helpers.arguments.IArgConsumer; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class Command implements Helper { + + protected IBaritone baritone; + protected IPlayerContext ctx; + + /** + * The names of this command. This is what you put after the command prefix. + */ + 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)); + } + + /** + * Called when this command is executed. + */ + public abstract void execute(String label, IArgConsumer args) throws CommandException; + + /** + * Called when the command needs to tab complete. Return a Stream representing the entries to put in the completions + * list. + */ + public abstract Stream tabComplete(String label, IArgConsumer args) throws CommandException; + + /** + * @return A single-line string containing a short description of this command's purpose. + */ + public abstract String getShortDesc(); + + /** + * @return A list of lines that will be printed by the help command when the user wishes to view them. + */ + public abstract List getLongDesc(); + + /** + * @return {@code true} if this command should be hidden from the help menu + */ + public boolean hiddenFromHelp() { + return false; + } +} diff --git a/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java b/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java new file mode 100644 index 00000000..63b6a445 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/IBaritoneChatControl.java @@ -0,0 +1,42 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command; + +import baritone.api.Settings; + +import java.util.UUID; + +/** + * @author Brady + * @since 9/26/2019 + */ +public interface IBaritoneChatControl { + + /** + * In certain cases chat components need to execute commands for you. For example, the paginator automatically runs + * commands when you click the forward and back arrows to show you the previous/next page. + *

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

+ * If used right (by both developers and users), it should be impossible to expose a command accidentally to the + * server. As a rule of thumb, if you have a clickable chat component, always use this prefix. If you're suggesting + * a command (a component that puts text into your text box, or something else), use {@link Settings#prefix}. + */ + String FORCE_COMMAND_PREFIX = String.format("<<%s>>", UUID.randomUUID().toString()); +} diff --git a/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java new file mode 100644 index 00000000..2b864f42 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java @@ -0,0 +1,104 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argparser; + +import baritone.api.utils.command.argument.ICommandArgument; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.exception.CommandNoParserForTypeException; +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, ICommandArgument 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, ICommandArgument arg, S state) throws CommandInvalidTypeException { + IArgParser.Stated parser = getParserStated(type, stateKlass); + if (parser == null) { + throw new CommandNoParserForTypeException(type); + } + try { + return parser.parseArg(arg, state); + } catch (Exception exc) { + throw new CommandInvalidTypeException(arg, type.getSimpleName()); + } + } +} diff --git a/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java b/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java new file mode 100644 index 00000000..b734f47d --- /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.ICommandArgument; + +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(ICommandArgument arg) throws RuntimeException { + return Integer.parseInt(arg.getValue()); + } + } + + public enum LongArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Long.class; + } + + @Override + public Long parseArg(ICommandArgument arg) throws RuntimeException { + return Long.parseLong(arg.getValue()); + } + } + + public enum FloatArgumentParser implements IArgParser.Stateless { + INSTANCE; + + @Override + public Class getTarget() { + return Float.class; + } + + @Override + public Float parseArg(ICommandArgument arg) throws RuntimeException { + String value = arg.getValue(); + 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(ICommandArgument arg) throws RuntimeException { + String value = arg.getValue(); + 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(ICommandArgument arg) throws RuntimeException { + String value = arg.getValue(); + if (TRUTHY_VALUES.contains(value.toLowerCase(Locale.US))) { + return true; + } else if (FALSY_VALUES.contains(value.toLowerCase(Locale.US))) { + return false; + } else { + throw new IllegalArgumentException("invalid boolean"); + } + } + } + + public static final List> ALL = Arrays.asList( + IntArgumentParser.INSTANCE, + LongArgumentParser.INSTANCE, + FloatArgumentParser.INSTANCE, + DoubleArgumentParser.INSTANCE, + BooleanArgumentParser.INSTANCE + ); +} diff --git a/src/api/java/baritone/api/utils/command/argparser/IArgParser.java b/src/api/java/baritone/api/utils/command/argparser/IArgParser.java new file mode 100644 index 00000000..c1f615f6 --- /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.ICommandArgument; + +public interface IArgParser { + + /** + * @return the class of this parser. + */ + Class getTarget(); + + /** + * A stateless argument parser is just that. It takes a {@link ICommandArgument} and outputs its type. + * + * @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(ICommandArgument arg) throws Exception; + } + + /** + * A stated argument parser is similar to a stateless one. It also takes a {@link ICommandArgument}, but it also + * takes a second argument that can be any type, referred to as the state. + * + * @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(ICommandArgument arg, S state) throws Exception; + } +} diff --git a/src/api/java/baritone/api/utils/command/argument/ICommandArgument.java b/src/api/java/baritone/api/utils/command/argument/ICommandArgument.java new file mode 100644 index 00000000..7cbe2540 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argument/ICommandArgument.java @@ -0,0 +1,102 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argument; + +import baritone.api.utils.command.argparser.IArgParser; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.IArgConsumer; +import net.minecraft.util.EnumFacing; + +/** + * A {@link ICommandArgument} is an immutable object representing one command argument. It contains data on the index of + * that argument, its value, and the rest of the string that argument was found in + *

+ * You're recommended to use {@link IArgConsumer}}s to handle these. + * + * @author Brady + * @since 10/2/2019 + */ +public interface ICommandArgument { + + /** + * @return The index of this command argument in the list of command arguments generated + */ + int getIndex(); + + /** + * @return The raw value of just this argument + */ + String getValue(); + + /** + * @return The raw value of the remaining arguments after this one was captured + */ + String getRawRest(); + + /** + * Gets an enum value from the enum class with the same name as this argument's value + *

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

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

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

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

+ * Anything implementing this interface should be assignable to {@link Exception}. + * + * @author Brady + * @since 9/20/2019 + */ +public interface ICommandException { + + /** + * @see Exception#getMessage() + * @return The exception details + */ + String getMessage(); + + /** + * Called when this exception is thrown, to handle the exception. + * + * @param command The command that threw it. + * @param args The arguments the command was called with. + */ + default void handle(Command command, List args) { + HELPER.logDirect(this.getMessage(), TextFormatting.RED); + } +} diff --git a/src/api/java/baritone/api/utils/command/helpers/arguments/IArgConsumer.java b/src/api/java/baritone/api/utils/command/helpers/arguments/IArgConsumer.java new file mode 100644 index 00000000..1e50a530 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/arguments/IArgConsumer.java @@ -0,0 +1,595 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.helpers.arguments; + +import baritone.api.utils.Helper; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argparser.IArgParser; +import baritone.api.utils.command.argument.ICommandArgument; +import baritone.api.utils.command.datatypes.IDatatype; +import baritone.api.utils.command.datatypes.IDatatypeFor; +import baritone.api.utils.command.datatypes.IDatatypePost; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.exception.CommandNotEnoughArgumentsException; +import baritone.api.utils.command.exception.CommandTooManyArgumentsException; +import net.minecraft.util.EnumFacing; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.stream.Stream; + +/** + * The {@link IArgConsumer} is how {@link Command}s read the arguments passed to them. This class has many benefits: + * + *

    + *
  • Mutability. The whole concept of the {@link IArgConsumer}} is to let you gradually consume arguments in any way + * you'd like. You can change your consumption based on earlier arguments, for subcommands for example.
  • + *
  • You don't need to keep track of your consumption. The {@link IArgConsumer}} keeps track of the arguments you + * consume so that it can throw detailed exceptions whenever something is out of the ordinary. Additionally, if you + * need to retrieve an argument after you've already consumed it - look no further than {@link #consumed()}!
  • + *
  • Easy retrieval of many different types. If you need to retrieve an instance of an int or float for example, + * look no further than {@link #getAs(Class)}. If you need a more powerful way of retrieving data, try out the many + * {@code getDatatype...} methods.
  • + *
  • It's very easy to throw detailed exceptions. The {@link IArgConsumer}} has many different methods that can + * enforce the number of arguments, the type of arguments, and more, throwing different types of + * {@link CommandException}s if something seems off. You're recommended to do all validation and store all needed + * data in variables BEFORE logging any data to chat via {@link Helper#logDirect(String)}, so that the error + * handlers can do their job and log the error to chat.
  • + *
+ */ +public interface IArgConsumer { + + LinkedList getArgs(); + + Deque getConsumed(); + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are at least {@code num} arguments left in this {@link IArgConsumer}} + * @see #hasAny() + * @see #hasAtMost(int) + * @see #hasExactly(int) + */ + boolean has(int num); + + /** + * @return {@code true} if there is at least 1 argument left in this {@link IArgConsumer}} + * @see #has(int) + * @see #hasAtMostOne() + * @see #hasExactlyOne() + */ + boolean hasAny(); + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are at most {@code num} arguments left in this {@link IArgConsumer}} + * @see #has(int) + * @see #hasAtMost(int) + * @see #hasExactly(int) + */ + boolean hasAtMost(int num); + + /** + * @return {@code true} if there is at most 1 argument left in this {@link IArgConsumer}} + * @see #hasAny() + * @see #hasAtMostOne() + * @see #hasExactlyOne() + */ + boolean hasAtMostOne(); + + /** + * @param num The number of arguments to check for + * @return {@code true} if there are exactly {@code num} arguments left in this {@link IArgConsumer}} + * @see #has(int) + * @see #hasAtMost(int) + */ + boolean hasExactly(int num); + + /** + * @return {@code true} if there is exactly 1 argument left in this {@link IArgConsumer}} + * @see #hasAny() + * @see #hasAtMostOne() + */ + boolean hasExactlyOne(); + + /** + * @param index The index to peek + * @return The argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one. This does not + * mutate the {@link IArgConsumer}} + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #peekString(int) + * @see #peekAs(Class, int) + * @see #get() + */ + ICommandArgument peek(int index) throws CommandNotEnoughArgumentsException; + + /** + * @return The next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}} + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peek(int) + * @see #peekString() + * @see #peekAs(Class) + * @see #get() + */ + ICommandArgument peek() throws CommandNotEnoughArgumentsException; + + /** + * @param index The index to peek + * @param type The type to check for + * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next + * argument + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #getAs(Class) + */ + boolean is(Class type, int index) throws CommandNotEnoughArgumentsException; + + /** + * @param type The type to check for + * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next + * argument + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peek() + * @see #getAs(Class) + */ + boolean is(Class type) throws CommandNotEnoughArgumentsException; + + /** + * @param index The index to peek + * @return The value of the argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one + * This does not mutate the {@link IArgConsumer}} + * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left + * @see #peek() + * @see #peekString() + */ + String peekString(int index) throws CommandNotEnoughArgumentsException; + + /** + * @return The value of the next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}} + * @throws CommandNotEnoughArgumentsException If there is less than one argument left + * @see #peekString(int) + * @see #getString() + */ + String peekString() throws CommandNotEnoughArgumentsException; + + /** + * @param index The index to peek + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value + * @throws java.util.NoSuchElementException If the constant couldn't be found + * @see #peekEnumOrNull(Class) + * @see #getEnum(Class) + * @see ICommandArgument#getEnum(Class) + */ + > E peekEnum(Class enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; + + /** + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value + * @throws CommandInvalidTypeException If the constant couldn't be found + * @see #peekEnumOrNull(Class) + * @see #getEnum(Class) + * @see ICommandArgument#getEnum(Class) + */ + > E peekEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; + + /** + * @param index The index to peek + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value. If no constant could be found, null + * @see #peekEnum(Class) + * @see #getEnumOrNull(Class) + * @see ICommandArgument#getEnum(Class) + */ + > E peekEnumOrNull(Class enumClass, int index) throws CommandNotEnoughArgumentsException; + + /** + * @param enumClass The class to search + * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the + * next argument's value. If no constant could be found, null + * @see #peekEnum(Class) + * @see #getEnumOrNull(Class) + * @see ICommandArgument#getEnum(Class) + */ + > E peekEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException; + + /** + * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified + * class + *

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

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

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

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

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

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

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

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

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

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

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

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

+ * For example if you getEnum as an {@link 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 ICommandArgument#getEnum(Class) + */ + > E getEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; + + /** + * Gets an enum value from the enum class with the same name as the next argument's value + *

+ * For example if you getEnum as an {@link 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 ICommandArgument#getEnum(Class) + */ + > E getEnumOrDefault(Class enumClass, E def) throws CommandNotEnoughArgumentsException; + + /** + * Gets an enum value from the enum class with the same name as the next argument's value + *

+ * For example if you getEnum as an {@link 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 ICommandArgument#getEnum(Class) + */ + > E getEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException; + + /** + * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class + *

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

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

+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take. + * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire + * {@link IArgConsumer}}. + * + * @param type The type to peek as + * @return An instance of the specified type, or {@code null} if it couldn't be parsed + * @see IArgParser + * @see #get() + * @see #getAs(Class) + * @see #getAsOrDefault(Class, Object) + * @see #peekAs(Class) + * @see #peekAsOrDefault(Class, Object, int) + * @see #peekAsOrNull(Class, int) + */ + T getAsOrNull(Class type) throws CommandNotEnoughArgumentsException; + + > T getDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; + + > T getDatatypePostOrDefault(D datatype, O original, T _default); + + > T getDatatypePostOrNull(D datatype, O original); + + > T getDatatypeFor(D datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException; + + > T getDatatypeForOrDefault(D datatype, T def); + + > T getDatatypeForOrNull(D datatype); + + Stream tabCompleteDatatype(T datatype); + + /** + * Returns the "raw rest" of the string. For example, from a string arg1 arg2  arg3, split + * into three {@link ICommandArgument}s {@code "arg1"}, {@code "arg2"}, and {@code "arg3"}: + * + *

    + *
  • {@code rawRest()} would return arg1 arg2  arg3
  • + *
  • After calling {@link #get()}, {@code rawRest()} would return arg2  arg3 (note the + * double space - it is preserved!)
  • + *
  • After calling {@link #get()} again, {@code rawRest()} would return {@code "arg3"}
  • + *
  • After calling {@link #get()} one last time, {@code rawRest()} would return {@code ""}
  • + *
+ * + * @return The "raw rest" of the string. + */ + String rawRest(); + + /** + * @param min The minimum amount of arguments to require. + * @throws CommandNotEnoughArgumentsException If there are less than {@code min} arguments left. + * @see #requireMax(int) + * @see #requireExactly(int) + */ + void requireMin(int min) throws CommandNotEnoughArgumentsException; + + /** + * @param max The maximum amount of arguments allowed. + * @throws CommandTooManyArgumentsException If there are more than {@code max} arguments left. + * @see #requireMin(int) + * @see #requireExactly(int) + */ + void requireMax(int max) throws CommandTooManyArgumentsException; + + /** + * @param args The exact amount of arguments to require. + * @throws CommandNotEnoughArgumentsException If there are less than {@code args} arguments left. + * @throws CommandTooManyArgumentsException If there are more than {@code args} arguments left. + * @see #requireMin(int) + * @see #requireMax(int) + */ + void requireExactly(int args) throws CommandException; + + /** + * @return If this {@link IArgConsumer}} has consumed at least one argument. + * @see #consumed() + * @see #consumedString() + */ + boolean hasConsumed(); + + /** + * @return The last argument this {@link IArgConsumer}} has consumed, or an "unknown" argument, indicated by a + * comamnd argument index that has a value of {@code -1}, if no arguments have been consumed yet. + * @see #consumedString() + * @see #hasConsumed() + */ + ICommandArgument consumed(); + + /** + * @return The value of thelast argument this {@link IArgConsumer}} has consumed, or an empty string if no arguments + * have been consumed yet + * @see #consumed() + * @see #hasConsumed() + */ + String consumedString(); + + /** + * @return A copy of this {@link IArgConsumer}}. It has the same arguments (both consumed and not), but does not + * affect or mutate this instance. Useful for the various {@code peek} functions + */ + IArgConsumer copy(); +} diff --git a/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java b/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java new file mode 100644 index 00000000..cf5d5d7c --- /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.IArgConsumer; +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(IArgConsumer consumer, Paginator pagi, Runnable pre, Function transform, String commandPrefix) throws CommandException { + int page = 1; + consumer.requireMax(1); + if (consumer.hasAny()) { + page = consumer.getAs(Integer.class); + if (!pagi.validPage(page)) { + throw new CommandInvalidTypeException( + consumer.consumed(), + String.format( + "a valid page (1-%d)", + pagi.getMaxPage() + ), + consumer.consumed().getValue() + ); + } + } + pagi.skipPages(page - pagi.page); + if (pre != null) { + pre.run(); + } + pagi.display(transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, List elems, Runnable pre, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, new Paginator<>(elems), pre, transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, Arrays.asList(elems), pre, transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, Paginator pagi, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, pagi, null, transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, List elems, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, new Paginator<>(elems), null, transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, T[] elems, Function transform, String commandPrefix) throws CommandException { + paginate(consumer, Arrays.asList(elems), null, transform, commandPrefix); + } + + public static void paginate(IArgConsumer consumer, Paginator pagi, Runnable pre, Function transform) throws CommandException { + paginate(consumer, pagi, pre, transform, null); + } + + public static void paginate(IArgConsumer consumer, List elems, Runnable pre, Function transform) throws CommandException { + paginate(consumer, new Paginator<>(elems), pre, transform, null); + } + + public static void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function transform) throws CommandException { + paginate(consumer, Arrays.asList(elems), pre, transform, null); + } + + public static void paginate(IArgConsumer consumer, Paginator pagi, Function transform) throws CommandException { + paginate(consumer, pagi, null, transform, null); + } + + public static void paginate(IArgConsumer consumer, List elems, Function transform) throws CommandException { + paginate(consumer, new Paginator<>(elems), null, transform, null); + } + + public static void paginate(IArgConsumer consumer, T[] elems, Function transform) throws CommandException { + paginate(consumer, Arrays.asList(elems), null, transform, null); + } +} diff --git a/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java b/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java new file mode 100644 index 00000000..9be9dfb3 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java @@ -0,0 +1,288 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.helpers.arguments.IArgConsumer; +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 IArgConsumer}#tabCompleteDatatype(IDatatype)})
  • + *
+ *

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

+ * Assumes every element in this {@link TabCompleteHelper} is a {@link ResourceLocation} + * + * @param prefix The prefix to filter for + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper filterPrefixNamespaced(String prefix) { + return filterPrefix(new ResourceLocation(prefix).toString()); + } + + /** + * @return An array containing every element in this {@link TabCompleteHelper} + * @see #stream() + */ + public String[] build() { + return stream.toArray(String[]::new); + } + + /** + * @return A stream containing every element in this {@link TabCompleteHelper} + * @see #build() + */ + public Stream stream() { + return stream; + } + + /** + * Appends every command in the specified {@link ICommandManager} to this {@link TabCompleteHelper} + * + * @param manager A command manager + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addCommands(ICommandManager manager) { + return append(manager.getRegistry().descendingStream() + .flatMap(command -> command.names.stream()) + .distinct() + ); + } + + /** + * Appends every setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addSettings() { + return append( + BaritoneAPI.getSettings().allSettings.stream() + .map(Settings.Setting::getName) + .filter(s -> !s.equalsIgnoreCase("logger")) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + /** + * Appends every modified setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addModifiedSettings() { + return append( + SettingsUtil.modifiedSettings(BaritoneAPI.getSettings()).stream() + .map(Settings.Setting::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + /** + * Appends every {@link Boolean} setting in the {@link Settings} to this {@link TabCompleteHelper} + * + * @return This {@link TabCompleteHelper} + */ + public TabCompleteHelper addToggleableSettings() { + return append( + BaritoneAPI.getSettings().getAllValuesByType(Boolean.class).stream() + .map(Settings.Setting::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/manager/ICommandManager.java b/src/api/java/baritone/api/utils/command/manager/ICommandManager.java new file mode 100644 index 00000000..d3fac415 --- /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.ICommandArgument; +import baritone.api.utils.command.registry.Registry; +import net.minecraft.util.Tuple; + +import java.util.List; +import java.util.stream.Stream; + +/** + * @author Brady + * @since 9/21/2019 + */ +public interface ICommandManager { + + IBaritone getBaritone(); + + Registry getRegistry(); + + /** + * @param name The command name to search for. + * @return The command, if found. + */ + Command getCommand(String name); + + boolean execute(String string); + + boolean execute(Tuple> expanded); + + Stream tabComplete(Tuple> expanded); + + Stream tabComplete(String prefix); +} diff --git a/src/api/java/baritone/api/utils/command/registry/Registry.java b/src/api/java/baritone/api/utils/command/registry/Registry.java new file mode 100644 index 00000000..bac50dcf --- /dev/null +++ b/src/api/java/baritone/api/utils/command/registry/Registry.java @@ -0,0 +1,135 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.registry; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * This registry class allows for registration and unregistration of a certain type. This is mainly designed for use by + * event handlers where newly registered ones are encountered first during iteration and can therefore override older + * ones. In Baritone, this is used for commands and argument parsers so that mods and addons can extend Baritone's + * functionality without resorting to hacks, wrappers, or mixins. + * + * @param The entry type that will be stored in this registry. This can be anything, really - preferably anything + * that works as a HashMap key, as that's what's used to keep track of which entries are registered or not. + */ +public class Registry { + + /** + * An internal linked list of all the entries that are currently registered. This is a linked list so that entries + * can be inserted at the beginning, which means that newer entries are encountered first during iteration. This is + * an important property of the registry that makes it more useful than a simple list, and also the reason it does + * not just use a map. + */ + private final Deque _entries = new LinkedList<>(); + /** + * A HashSet containing every entry currently registered. Entries are added to this set when something is registered + * and removed from the set when they are unregistered. An entry being present in this set indicates that it is + * currently registered, can be removed, and should not be reregistered until it is removed. + */ + private final Set registered = new HashSet<>(); + /** + * The collection of entries that are currently in this registry. This is a collection (and not a list) because, + * internally, entries are stored in a linked list, which is not the same as a normal list. + */ + public final Collection entries = Collections.unmodifiableCollection(_entries); + + /** + * @param entry The entry to check. + * @return If this entry is currently registered in this registry. + */ + public boolean registered(V entry) { + return registered.contains(entry); + } + + /** + * Ensures that the entry {@code entry} is registered. + * + * @param entry The entry to register. + * @return A boolean indicating whether or not this is a new registration. No matter the value of this boolean, the + * entry is always guaranteed to now be in this registry. This boolean simply indicates if the entry was not + * in the map prior to this method call. + */ + public boolean register(V entry) { + if (!registered(entry)) { + _entries.addFirst(entry); + registered.add(entry); + return true; + } + return false; + } + + /** + * Unregisters this entry from this registry. After this method call, the entry is guaranteed to be removed from the + * registry, since each entry only ever appears once. + * + * @param entry The entry to unregister. + */ + public void unregister(V entry) { + if (registered(entry)) { + return; + } + _entries.remove(entry); + registered.remove(entry); + } + + /** + * Returns an iterator that iterates over each entry in this registry, with the newest elements iterated over first. + * Internally, as new elements are prepended to the registry rather than appended to the end, this order is the best + * way to search through the registry if you want to discover newer items first. + */ + public Iterator iterator() { + return _entries.iterator(); + } + + /** + * Returns an iterator that iterates over each entry in this registry, in the order they were added. Internally, + * this iterates through the registry backwards, as new elements are prepended to the registry rather than appended + * to the end. You should only do this when you need to, for example, list elements in order - it is almost always + * fine to simply use {@link Iterable#forEach(Consumer) forEach} on the {@link #entries} collection instead. + */ + public Iterator descendingIterator() { + return _entries.descendingIterator(); + } + + /** + * Returns a stream that contains each entry in this registry, with the newest elements ordered first. Internally, + * as new elements are prepended to the registry rather than appended to the end, this order is the best way to + * search through the registry if you want to discover newer items first. + */ + public Stream stream() { + return _entries.stream(); + } + + /** + * Returns a stream that returns each entry in this registry, in the order they were added. Internally, this orders + * the registry backwards, as new elements are prepended to the registry rather than appended to the end. You should + * only use this when you need to, for example, list elements in order - it is almost always fine to simply use the + * regular {@link #stream()} method instead. + */ + public Stream descendingStream() { + return StreamSupport.stream(Spliterators.spliterator( + descendingIterator(), + _entries.size(), + Spliterator.SIZED | Spliterator.SUBSIZED + ), false); + } +} diff --git a/src/api/java/baritone/api/utils/input/Input.java b/src/api/java/baritone/api/utils/input/Input.java index 1e8d44b5..c44f3352 100644 --- a/src/api/java/baritone/api/utils/input/Input.java +++ b/src/api/java/baritone/api/utils/input/Input.java @@ -17,15 +17,6 @@ package baritone.api.utils.input; -import net.minecraft.client.Minecraft; -import net.minecraft.client.settings.GameSettings; -import net.minecraft.client.settings.KeyBinding; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - /** * An {@link Enum} representing the inputs that control the player's * behavior. This includes moving, interacting with blocks, jumping, @@ -36,84 +27,45 @@ public enum Input { /** * The move forward input */ - MOVE_FORWARD(s -> s.keyBindForward), + MOVE_FORWARD, /** * The move back input */ - MOVE_BACK(s -> s.keyBindBack), + MOVE_BACK, /** * The move left input */ - MOVE_LEFT(s -> s.keyBindLeft), + MOVE_LEFT, /** * The move right input */ - MOVE_RIGHT(s -> s.keyBindRight), + MOVE_RIGHT, /** * The attack input */ - CLICK_LEFT(s -> s.keyBindAttack), + CLICK_LEFT, /** * The use item input */ - CLICK_RIGHT(s -> s.keyBindUseItem), + CLICK_RIGHT, /** * The jump input */ - JUMP(s -> s.keyBindJump), + JUMP, /** * The sneak input */ - SNEAK(s -> s.keyBindSneak), + SNEAK, /** * The sprint input */ - SPRINT(s -> s.keyBindSprint); - - /** - * Map of {@link KeyBinding} to {@link Input}. Values should be queried through {@link #getInputForBind(KeyBinding)} - */ - private static final Map bindToInputMap = new HashMap<>(); - - /** - * The actual game {@link KeyBinding} being forced. - */ - private final KeyBinding keyBinding; - - Input(Function keyBindingMapper) { - /* - - Here, a Function is used because referring to a static field in this enum for the game instance, - as it was before, wouldn't be possible in an Enum constructor unless the static field was in an - interface that this class implemented. (Helper acted as this interface) I didn't feel like making - an interface with a game instance field just to not have to do this. - - */ - this.keyBinding = keyBindingMapper.apply(Minecraft.getMinecraft().gameSettings); - } - - /** - * @return The actual game {@link KeyBinding} being forced. - */ - public final KeyBinding getKeyBinding() { - return this.keyBinding; - } - - /** - * Finds the {@link Input} constant that is associated with the specified {@link KeyBinding}. - * - * @param binding The {@link KeyBinding} to find the associated {@link Input} for - * @return The {@link Input} associated with the specified {@link KeyBinding} - */ - public static Input getInputForBind(KeyBinding binding) { - return bindToInputMap.computeIfAbsent(binding, b -> Arrays.stream(values()).filter(input -> input.keyBinding == b).findFirst().orElse(null)); - } + SPRINT } diff --git a/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java b/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java index 5bbbc007..13e7e686 100644 --- a/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java +++ b/src/api/java/baritone/api/utils/interfaces/IGoalRenderPos.java @@ -20,5 +20,6 @@ package baritone.api.utils.interfaces; import net.minecraft.util.math.BlockPos; public interface IGoalRenderPos { + BlockPos getGoalPos(); } diff --git a/src/launch/java/baritone/launch/mixins/MixinBitArray.java b/src/launch/java/baritone/launch/mixins/MixinBitArray.java new file mode 100644 index 00000000..9383c9aa --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinBitArray.java @@ -0,0 +1,91 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.IBitArray; +import net.minecraft.util.BitArray; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(BitArray.class) +public abstract class MixinBitArray implements IBitArray { + + @Shadow + @Final + private long[] longArray; + + @Shadow + @Final + private int bitsPerEntry; + + @Shadow + @Final + private long maxEntryValue; + + @Shadow + @Final + private int arraySize; + + /** + * why did mojang divide by 64 instead of shifting right by 6 (2^6=64)? + * why did mojang modulo by 64 instead of ANDing with 63? + * also removed validation check + * + * @author LoganDark + */ + @Override + @Unique + public int getAtFast(int index) { + final int b = bitsPerEntry; + final long mev = maxEntryValue; + final int i = index * b; + final int j = i >> 6; + final int l = i & 63; + final int k = ((index + 1) * b - 1) >> 6; + + if (j == k) { + return (int) (this.longArray[j] >>> l & mev); + } else { + return (int) ((this.longArray[j] >>> l | longArray[k] << (64 - l)) & mev); + } + } + + @Override + @Unique + public int[] toArray() { + int[] out = new int[arraySize]; + + for (int idx = 0, kl = bitsPerEntry - 1; idx < arraySize; idx++, kl += bitsPerEntry) { + final int i = idx * bitsPerEntry; + final int j = i >> 6; + final int l = i & 63; + final int k = kl >> 6; + final long jl = longArray[j] >>> l; + + if (j == k) { + out[idx] = (int) (jl & maxEntryValue); + } else { + out[idx] = (int) ((jl | longArray[k] << (64 - l)) & maxEntryValue); + } + } + + return out; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.java b/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.java new file mode 100644 index 00000000..8c08b03f --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinBlockStateContainer.java @@ -0,0 +1,63 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.IBitArray; +import baritone.utils.accessor.IBlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BitArray; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.IBlockStatePalette; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(BlockStateContainer.class) +public abstract class MixinBlockStateContainer implements IBlockStateContainer { + + @Shadow + protected BitArray storage; + + @Shadow + protected IBlockStatePalette palette; + + @Override + @Accessor + public abstract BitArray getStorage(); + + @Override + @Accessor + public abstract IBlockStatePalette getPalette(); + + @Override + @Unique + public IBlockState getFast(int index) { + return palette.getBlockState(((IBitArray) storage).getAtFast(index)); + } + + @Override + public IBlockState getAtPalette(int index) { + return palette.getBlockState(index); + } + + @Override + public int[] storageArray() { + return ((IBitArray) storage).toArray(); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java b/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java new file mode 100644 index 00000000..797b6210 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import net.minecraft.client.gui.GuiChat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiChat.ChatTabCompleter.class) +public abstract class MixinChatTabCompleter extends MixinTabCompleter { + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onConstruction(CallbackInfo ci) { + isChatCompleter = true; + } + + @Inject( + method = "complete", + at = @At("HEAD"), + cancellable = true + ) + private void onComplete(CallbackInfo ci) { + if (dontComplete) { + ci.cancel(); + } + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinChunkRenderContainer.java b/src/launch/java/baritone/launch/mixins/MixinChunkRenderContainer.java new file mode 100644 index 00000000..0d228db9 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinChunkRenderContainer.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.launch.mixins; + +import baritone.Baritone; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ChunkRenderContainer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.chunk.RenderChunk; +import net.minecraft.util.math.BlockPos; +import org.lwjgl.opengl.GL14; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static org.lwjgl.opengl.GL11.*; + +@Mixin(ChunkRenderContainer.class) +public class MixinChunkRenderContainer { + + @Redirect( // avoid creating CallbackInfo at all costs; this is called 40k times per second + method = "preRenderChunk", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/renderer/chunk/RenderChunk.getPosition()Lnet/minecraft/util/math/BlockPos;" + ) + ) + private BlockPos getPosition(RenderChunk renderChunkIn) { + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer() && Minecraft.getMinecraft().world.getChunk(renderChunkIn.getPosition()).isEmpty()) { + GlStateManager.enableAlpha(); + GlStateManager.enableBlend(); + GL14.glBlendColor(0, 0, 0, Baritone.settings().cachedChunksOpacity.value); + GlStateManager.tryBlendFuncSeparate(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ONE, GL_ZERO); + } + return renderChunkIn.getPosition(); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinChunkRenderWorker.java b/src/launch/java/baritone/launch/mixins/MixinChunkRenderWorker.java new file mode 100644 index 00000000..d72fdd41 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinChunkRenderWorker.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.launch.mixins; + +import baritone.Baritone; +import baritone.api.BaritoneAPI; +import baritone.api.utils.IPlayerContext; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.ChunkRenderWorker; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ChunkRenderWorker.class) +public abstract class MixinChunkRenderWorker { + + @Shadow + protected abstract boolean isChunkExisting(BlockPos pos, World worldIn); + + @Redirect( + method = "processTask", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/renderer/chunk/ChunkRenderWorker.isChunkExisting(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;)Z" + ) + ) + private boolean isChunkExisting(ChunkRenderWorker worker, BlockPos pos, World world) { + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) { + Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone(); + IPlayerContext ctx = baritone.getPlayerContext(); + if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) { + return baritone.bsi.isLoaded(pos.getX(), pos.getZ()) || this.isChunkExisting(pos, world); + } + } + + return this.isChunkExisting(pos, world); + } +} + diff --git a/src/launch/java/baritone/launch/mixins/MixinEntity.java b/src/launch/java/baritone/launch/mixins/MixinEntity.java deleted file mode 100644 index fe498202..00000000 --- a/src/launch/java/baritone/launch/mixins/MixinEntity.java +++ /dev/null @@ -1,74 +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.launch.mixins; - -import baritone.api.BaritoneAPI; -import baritone.api.event.events.RotationMoveEvent; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.entity.Entity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import static org.spongepowered.asm.lib.Opcodes.GETFIELD; - -/** - * @author Brady - * @since 8/21/2018 - */ -@Mixin(Entity.class) -public class MixinEntity { - - @Shadow - public float rotationYaw; - - /** - * Event called to override the movement direction when walking - */ - private RotationMoveEvent motionUpdateRotationEvent; - - @Inject( - method = "moveRelative", - at = @At("HEAD") - ) - private void preMoveRelative(float strafe, float up, float forward, float friction, CallbackInfo ci) { - // noinspection ConstantConditions - if (EntityPlayerSP.class.isInstance(this)) { - this.motionUpdateRotationEvent = new RotationMoveEvent((EntityPlayerSP) (Object) this, RotationMoveEvent.Type.MOTION_UPDATE, this.rotationYaw); - BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onPlayerRotationMove(this.motionUpdateRotationEvent); - } - } - - @Redirect( - method = "moveRelative", - at = @At( - value = "FIELD", - opcode = GETFIELD, - target = "net/minecraft/entity/Entity.rotationYaw:F" - ) - ) - private float overrideYaw(Entity self) { - if (self instanceof EntityPlayerSP) { - return this.motionUpdateRotationEvent.getYaw(); - } - return self.rotationYaw; - } -} diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java b/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java index 1a7d298f..0fd2436c 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java @@ -18,6 +18,7 @@ package baritone.launch.mixins; import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; import baritone.api.event.events.RotationMoveEvent; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.Entity; @@ -55,8 +56,11 @@ public abstract class MixinEntityLivingBase extends Entity { private void preMoveRelative(CallbackInfo ci) { // noinspection ConstantConditions if (EntityPlayerSP.class.isInstance(this)) { - this.jumpRotationEvent = new RotationMoveEvent((EntityPlayerSP) (Object) this, RotationMoveEvent.Type.JUMP, this.rotationYaw); - BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone != null) { + this.jumpRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.JUMP, this.rotationYaw); + baritone.getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); + } } } @@ -69,9 +73,30 @@ public abstract class MixinEntityLivingBase extends Entity { ) ) private float overrideYaw(EntityLivingBase self) { - if (self instanceof EntityPlayerSP) { + if (self instanceof EntityPlayerSP && BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this) != null) { return this.jumpRotationEvent.getYaw(); } return self.rotationYaw; } + + @Redirect( + method = "travel", + at = @At( + value = "INVOKE", + target = "net/minecraft/entity/EntityLivingBase.moveRelative(FFFF)V" + ) + ) + private void travel(EntityLivingBase self, float strafe, float up, float forward, float friction) { + // noinspection ConstantConditions + if (!EntityPlayerSP.class.isInstance(this) || BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this) == null) { + moveRelative(strafe, up, forward, friction); + return; + } + RotationMoveEvent motionUpdateRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.rotationYaw); + BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onPlayerRotationMove(motionUpdateRotationEvent); + float originalYaw = this.rotationYaw; + this.rotationYaw = motionUpdateRotationEvent.getYaw(); + this.moveRelative(strafe, up, forward, friction); + this.rotationYaw = originalYaw; + } } diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java index 3caffaab..7c1225b9 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java @@ -19,11 +19,11 @@ package baritone.launch.mixins; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; -import baritone.api.behavior.IPathingBehavior; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.SprintStateEvent; import baritone.api.event.events.type.EventState; +import baritone.behavior.LookBehavior; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.player.PlayerCapabilities; @@ -46,12 +46,12 @@ public class MixinEntityPlayerSP { cancellable = true ) private void sendChatMessage(String msg, CallbackInfo ci) { - ChatEvent event = new ChatEvent((EntityPlayerSP) (Object) this, msg); - for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { - System.out.println("Sending chat event to baritone " + ibaritone); - ibaritone.getGameEventHandler().onSendChatMessage(event); + ChatEvent event = new ChatEvent(msg); + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone == null) { + return; } - //BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onSendChatMessage(event); + baritone.getGameEventHandler().onSendChatMessage(event); if (event.isCancelled()) { ci.cancel(); } @@ -67,7 +67,10 @@ public class MixinEntityPlayerSP { ) ) private void onPreUpdate(CallbackInfo ci) { - BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent((EntityPlayerSP) (Object) this, EventState.PRE)); + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone != null) { + baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.PRE)); + } } @Inject( @@ -80,7 +83,10 @@ public class MixinEntityPlayerSP { ) ) private void onPostUpdate(CallbackInfo ci) { - BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent((EntityPlayerSP) (Object) this, EventState.POST)); + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone != null) { + baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST)); + } } @Redirect( @@ -91,8 +97,11 @@ public class MixinEntityPlayerSP { ) ) private boolean isAllowFlying(PlayerCapabilities capabilities) { - IPathingBehavior pathingBehavior = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getPathingBehavior(); - return !pathingBehavior.isPathing() && capabilities.allowFlying; + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone == null) { + return capabilities.allowFlying; + } + return !baritone.getPathingBehavior().isPathing() && capabilities.allowFlying; } @Redirect( @@ -103,9 +112,32 @@ public class MixinEntityPlayerSP { ) ) private boolean isKeyDown(KeyBinding keyBinding) { - EntityPlayerSP self = (EntityPlayerSP) (Object) this; - SprintStateEvent event = new SprintStateEvent(self); - BaritoneAPI.getProvider().getBaritoneForPlayer(self).getGameEventHandler().onPlayerSprintState(event); - return event.getState() == null ? keyBinding.isKeyDown() : event.getState(); + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone == null) { + return keyBinding.isKeyDown(); + } + SprintStateEvent event = new SprintStateEvent(); + baritone.getGameEventHandler().onPlayerSprintState(event); + if (event.getState() != null) { + return event.getState(); + } + if (baritone != BaritoneAPI.getProvider().getPrimaryBaritone()) { + // hitting control shouldn't make all bots sprint + return false; + } + return keyBinding.isKeyDown(); + } + + @Inject( + method = "updateRidden", + at = @At( + value = "HEAD" + ) + ) + private void updateRidden(CallbackInfo cb) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this); + if (baritone != null) { + ((LookBehavior) baritone.getLookBehavior()).pig(); + } } } diff --git a/src/launch/java/baritone/launch/mixins/MixinGuiChat.java b/src/launch/java/baritone/launch/mixins/MixinGuiChat.java new file mode 100644 index 00000000..9883c176 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiChat.java @@ -0,0 +1,45 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.ITabCompleter; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.util.TabCompleter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiChat.class) +public abstract class MixinGuiChat implements net.minecraft.util.ITabCompleter { + + @Shadow + private TabCompleter tabCompleter; + + @Inject( + method = "setCompletions", + at = @At("HEAD"), + cancellable = true + ) + private void onSetCompletions(String[] newCompl, CallbackInfo ci) { + if (((ITabCompleter) tabCompleter).onGuiChatSetCompletions(newCompl)) { + ci.cancel(); + } + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java b/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java new file mode 100644 index 00000000..9f91beaa --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java @@ -0,0 +1,33 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.api.accessor.IGuiScreen; +import net.minecraft.client.gui.GuiScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.net.URI; + +@Mixin(GuiScreen.class) +public abstract class MixinGuiScreen implements IGuiScreen { + + @Override + @Invoker("openWebLink") + public abstract void openLink(URI url); +} diff --git a/src/launch/java/baritone/launch/mixins/MixinItemStack.java b/src/launch/java/baritone/launch/mixins/MixinItemStack.java new file mode 100644 index 00000000..2ad4c187 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinItemStack.java @@ -0,0 +1,68 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.api.accessor.IItemStack; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemStack.class) +public abstract class MixinItemStack implements IItemStack { + + @Shadow + @Final + private Item item; + + @Shadow + private int itemDamage; + + @Unique + private int baritoneHash; + + private void recalculateHash() { + baritoneHash = item == null ? -1 : item.hashCode() + itemDamage; + } + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onInit(CallbackInfo ci) { + recalculateHash(); + } + + @Inject( + method = "setItemDamage", + at = @At("TAIL") + ) + private void onItemDamageSet(CallbackInfo ci) { + recalculateHash(); + } + + @Override + public int getBaritoneHash() { + return baritoneHash; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinKeyBinding.java b/src/launch/java/baritone/launch/mixins/MixinKeyBinding.java deleted file mode 100644 index cf832533..00000000 --- a/src/launch/java/baritone/launch/mixins/MixinKeyBinding.java +++ /dev/null @@ -1,68 +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.launch.mixins; - -import baritone.api.BaritoneAPI; -import baritone.utils.Helper; -import net.minecraft.client.settings.KeyBinding; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -/** - * @author Brady - * @since 7/31/2018 - */ -@Mixin(KeyBinding.class) -public class MixinKeyBinding { - - @Shadow - public int pressTime; - - @Inject( - method = "isKeyDown", - at = @At("HEAD"), - cancellable = true - ) - private void isKeyDown(CallbackInfoReturnable cir) { - // only the primary baritone forces keys - Boolean force = BaritoneAPI.getProvider().getPrimaryBaritone().getInputOverrideHandler().isInputForcedDown((KeyBinding) (Object) this); - if (force != null) { - cir.setReturnValue(force); // :sunglasses: - } - } - - @Inject( - method = "isPressed", - at = @At("HEAD"), - cancellable = true - ) - private void isPressed(CallbackInfoReturnable cir) { - // only the primary baritone forces keys - Boolean force = BaritoneAPI.getProvider().getPrimaryBaritone().getInputOverrideHandler().isInputForcedDown((KeyBinding) (Object) this); - if (force != null && force == false) { // <-- cursed - if (pressTime > 0) { - Helper.HELPER.logDirect("You're trying to press this mouse button but I won't let you"); - pressTime--; - } - cir.setReturnValue(force); // :sunglasses: - } - } -} diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java index 6ee1a79b..9dc835cd 100644 --- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java +++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java @@ -17,7 +17,6 @@ package baritone.launch.mixins; -import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.BlockInteractEvent; @@ -59,7 +58,7 @@ public class MixinMinecraft { at = @At("RETURN") ) private void postInit(CallbackInfo ci) { - ((Baritone) BaritoneAPI.getProvider().getPrimaryBaritone()).init(); + BaritoneAPI.getProvider().getPrimaryBaritone(); } @Inject( diff --git a/src/launch/java/baritone/launch/mixins/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 b0aa75ba..88c4abec 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/java/baritone/launch/mixins/MixinRenderChunk.java b/src/launch/java/baritone/launch/mixins/MixinRenderChunk.java new file mode 100644 index 00000000..f67d5d22 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinRenderChunk.java @@ -0,0 +1,89 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.Baritone; +import baritone.api.BaritoneAPI; +import baritone.api.utils.IPlayerContext; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.RenderChunk; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * @author Brady + * @since 1/29/2019 + */ +@Mixin(RenderChunk.class) +public class MixinRenderChunk { + + @Redirect( + method = "rebuildChunk", + at = @At( + value = "INVOKE", + target = "net/minecraft/world/ChunkCache.isEmpty()Z" + ) + ) + private boolean isEmpty(ChunkCache chunkCache) { + if (!chunkCache.isEmpty()) { + return false; + } + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) { + Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone(); + IPlayerContext ctx = baritone.getPlayerContext(); + if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) { + BlockPos position = ((RenderChunk) (Object) this).getPosition(); + // RenderChunk extends from -1,-1,-1 to +16,+16,+16 + // then the constructor of ChunkCache extends it one more (presumably to get things like the connected status of fences? idk) + // so if ANY of the adjacent chunks are loaded, we are unempty + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (baritone.bsi.isLoaded(16 * dx + position.getX(), 16 * dz + position.getZ())) { + return false; + } + } + } + } + } + + return true; + } + + @Redirect( + method = "rebuildChunk", + at = @At( + value = "INVOKE", + target = "net/minecraft/world/ChunkCache.getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/state/IBlockState;" + ) + ) + private IBlockState getBlockState(ChunkCache chunkCache, BlockPos pos) { + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) { + Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone(); + IPlayerContext ctx = baritone.getPlayerContext(); + if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) { + return baritone.bsi.get0(pos); + } + } + + return chunkCache.getBlockState(pos); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinRenderList.java b/src/launch/java/baritone/launch/mixins/MixinRenderList.java new file mode 100644 index 00000000..3efb3a10 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinRenderList.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.Baritone; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static org.lwjgl.opengl.GL11.*; + +@Mixin(RenderList.class) +public class MixinRenderList { + + @Redirect( // avoid creating CallbackInfo at all costs; this is called 40k times per second + method = "renderChunkLayer", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/renderer/GlStateManager.popMatrix()V" + ) + ) + private void popMatrix() { + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) { + // reset the blend func to normal (not dependent on constant alpha) + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + } + GlStateManager.popMatrix(); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java b/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java new file mode 100644 index 00000000..fef47bbe --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinStateImplementation.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import com.google.common.collect.ImmutableMap; +import net.minecraft.block.properties.IProperty; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net.minecraft.block.state.BlockStateContainer$StateImplementation") +public abstract class MixinStateImplementation { + + @Shadow + @Final + private ImmutableMap, Comparable> properties; + + /** + * Block states are fucking immutable + */ + @Unique + private int hashCode; + + @Inject( + method = "*", + at = @At("RETURN") + ) + private void onInit(CallbackInfo ci) { + hashCode = properties.hashCode(); + } + + /** + * Cache this instead of using the fucking map every time + * + * @author LoganDark + * @reason Regular IBlockState generates a new hash every fucking time. This is not needed when scanning millions + * per second + */ + @Override + @Overwrite + public int hashCode() { + return hashCode; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java b/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java new file mode 100644 index 00000000..cdbae3b4 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java @@ -0,0 +1,130 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; +import baritone.api.event.events.TabCompleteEvent; +import baritone.utils.accessor.ITabCompleter; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.util.TabCompleter; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TabCompleter.class) +public abstract class MixinTabCompleter implements ITabCompleter { + + @Shadow + @Final + protected GuiTextField textField; + + @Shadow + protected boolean requestedCompletions; + + @Shadow + public abstract void setCompletions(String... newCompl); + + @Unique + protected boolean isChatCompleter = false; + + @Unique + protected boolean dontComplete = false; + + @Override + public String getPrefix() { + return textField.getText().substring(0, textField.getCursorPosition()); + } + + @Override + public void setPrefix(String prefix) { + textField.setText(prefix + textField.getText().substring(textField.getCursorPosition())); + textField.setCursorPosition(prefix.length()); + } + + @Inject( + method = "requestCompletions", + at = @At("HEAD"), + cancellable = true + ) + private void onRequestCompletions(String prefix, CallbackInfo ci) { + if (!isChatCompleter) { + return; + } + + IBaritone baritone = BaritoneAPI.getProvider().getPrimaryBaritone(); + + TabCompleteEvent.Pre event = new TabCompleteEvent.Pre(prefix); + baritone.getGameEventHandler().onPreTabComplete(event); + + if (event.isCancelled()) { + ci.cancel(); + return; + } + + if (event.prefix.wasModified()) { + setPrefix(event.prefix.get()); + } + + if (event.completions.wasModified()) { + ci.cancel(); + + dontComplete = true; + + try { + requestedCompletions = true; + setCompletions(event.completions.get()); + } finally { + dontComplete = false; + } + } + } + + @Override + public boolean onGuiChatSetCompletions(String[] newCompl) { + IBaritone baritone = BaritoneAPI.getProvider().getPrimaryBaritone(); + + if (baritone == null) { + return false; + } + + TabCompleteEvent.Post event = new TabCompleteEvent.Post(getPrefix(), newCompl); + baritone.getGameEventHandler().onPostTabComplete(event); + + if (event.isCancelled()) { + return true; + } + + if (event.prefix.wasModified()) { + String prefix = event.prefix.get(); + textField.setText(prefix + textField.getText().substring(textField.getCursorPosition())); + textField.setCursorPosition(prefix.length()); + } + + if (event.completions.wasModified()) { + setCompletions(event.completions.get()); + return true; + } + + return false; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinVboRenderList.java b/src/launch/java/baritone/launch/mixins/MixinVboRenderList.java new file mode 100644 index 00000000..cec62336 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinVboRenderList.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.Baritone; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.VboRenderList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static org.lwjgl.opengl.GL11.*; + +@Mixin(VboRenderList.class) +public class MixinVboRenderList { + + @Redirect( // avoid creating CallbackInfo at all costs; this is called 40k times per second + method = "renderChunkLayer", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/renderer/GlStateManager.popMatrix()V" + ) + ) + private void popMatrix() { + if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) { + // reset the blend func to normal (not dependent on constant alpha) + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + } + GlStateManager.popMatrix(); + } +} diff --git a/src/launch/resources/mixins.baritone.json b/src/launch/resources/mixins.baritone.json index 09ebbafd..82c1dd7f 100644 --- a/src/launch/resources/mixins.baritone.json +++ b/src/launch/resources/mixins.baritone.json @@ -9,17 +9,28 @@ }, "client": [ "MixinAnvilChunkLoader", - "MixinBlockPos", + "MixinBitArray", + "MixinBlockStateContainer", + "MixinChatTabCompleter", "MixinChunkProviderClient", "MixinChunkProviderServer", - "MixinEntity", + "MixinChunkRenderContainer", + "MixinChunkRenderWorker", "MixinEntityLivingBase", "MixinEntityPlayerSP", "MixinEntityRenderer", - "MixinKeyBinding", + "MixinGuiChat", + "MixinGuiScreen", + "MixinItemStack", "MixinMinecraft", "MixinNetHandlerPlayClient", "MixinNetworkManager", + "MixinPlayerControllerMP", + "MixinRenderChunk", + "MixinRenderList", + "MixinStateImplementation", + "MixinTabCompleter", + "MixinVboRenderList", "MixinWorldClient" ] } \ No newline at end of file diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index 8cf5b9f2..bff164c1 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -21,25 +21,20 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.event.listener.IEventBus; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import baritone.behavior.*; import baritone.cache.WorldProvider; import baritone.event.GameEventHandler; -import baritone.process.CustomGoalProcess; -import baritone.process.FollowProcess; -import baritone.process.GetToBlockProcess; -import baritone.process.MineProcess; -import baritone.utils.BaritoneAutoTest; -import baritone.utils.ExampleBaritoneControl; -import baritone.utils.InputOverrideHandler; -import baritone.utils.PathingControlManager; +import baritone.process.*; +import baritone.selection.SelectionManager; +import baritone.utils.*; +import baritone.utils.command.manager.CommandManager; import net.minecraft.client.Minecraft; import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -65,50 +60,45 @@ public class Baritone implements IBaritone { } } - /** - * Whether or not {@link Baritone#init()} has been called yet - */ - private boolean initialized; + private GameEventHandler gameEventHandler; - private final GameEventHandler gameEventHandler; - - private List behaviors; - private ControllerBehavior controllerBehavior; private PathingBehavior pathingBehavior; private LookBehavior lookBehavior; private MemoryBehavior memoryBehavior; + private InventoryBehavior inventoryBehavior; private InputOverrideHandler inputOverrideHandler; private FollowProcess followProcess; private MineProcess mineProcess; private GetToBlockProcess getToBlockProcess; private CustomGoalProcess customGoalProcess; + private BuilderProcess builderProcess; + private ExploreProcess exploreProcess; + private BackfillProcess backfillProcess; + private FarmProcess farmProcess; private PathingControlManager pathingControlManager; + private SelectionManager selectionManager; + private CommandManager commandManager; - private final IPlayerContext playerContext; + private IPlayerContext playerContext; private WorldProvider worldProvider; - public Baritone(IPlayerContext playerContext) { + public BlockStateInterface bsi; + + Baritone(IPlayerContext playerContext) { this.gameEventHandler = new GameEventHandler(this); + + // Define this before behaviors try and get it, or else it will be null and the builds will fail! this.playerContext = playerContext; - } - public synchronized void init() { - if (initialized) { - return; - } - - this.behaviors = new ArrayList<>(); { // the Behavior constructor calls baritone.registerBehavior(this) so this populates the behaviors arraylist - controllerBehavior = new ControllerBehavior(this); pathingBehavior = new PathingBehavior(this); lookBehavior = new LookBehavior(this); memoryBehavior = new MemoryBehavior(this); - new InventoryBehavior(this); + inventoryBehavior = new InventoryBehavior(this); inputOverrideHandler = new InputOverrideHandler(this); - new ExampleBaritoneControl(this); } this.pathingControlManager = new PathingControlManager(this); @@ -117,15 +107,19 @@ public class Baritone implements IBaritone { mineProcess = new MineProcess(this); customGoalProcess = new CustomGoalProcess(this); // very high iq getToBlockProcess = new GetToBlockProcess(this); + builderProcess = new BuilderProcess(this); + exploreProcess = new ExploreProcess(this); + backfillProcess = new BackfillProcess(this); + farmProcess = new FarmProcess(this); } 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 @@ -133,12 +127,7 @@ public class Baritone implements IBaritone { return this.pathingControlManager; } - public List getBehaviors() { - return this.behaviors; - } - public void registerBehavior(Behavior behavior) { - this.behaviors.add(behavior); this.gameEventHandler.registerEventListener(behavior); } @@ -147,39 +136,67 @@ public class Baritone implements IBaritone { return this.inputOverrideHandler; } - public ControllerBehavior getControllerBehavior() { - return this.controllerBehavior; - } - @Override - public CustomGoalProcess getCustomGoalProcess() { // Iffy + public CustomGoalProcess getCustomGoalProcess() { return this.customGoalProcess; } @Override - public GetToBlockProcess getGetToBlockProcess() { // Iffy + public GetToBlockProcess getGetToBlockProcess() { return this.getToBlockProcess; } - @Override - public PathingBehavior getPathingBehavior() { - return this.pathingBehavior; - } - - public MemoryBehavior getMemoryBehavior() { - return this.memoryBehavior; - } - @Override public IPlayerContext getPlayerContext() { return this.playerContext; } + public MemoryBehavior getMemoryBehavior() { + return this.memoryBehavior; + } + @Override public FollowProcess getFollowProcess() { return this.followProcess; } + @Override + public BuilderProcess getBuilderProcess() { + return this.builderProcess; + } + + public InventoryBehavior getInventoryBehavior() { + return this.inventoryBehavior; + } + + @Override + public LookBehavior getLookBehavior() { + return this.lookBehavior; + } + + public ExploreProcess getExploreProcess() { + return this.exploreProcess; + } + + @Override + public MineProcess getMineProcess() { + return this.mineProcess; + } + + public FarmProcess getFarmProcess() { + return this.farmProcess; + } + + @Override + public PathingBehavior getPathingBehavior() { + return this.pathingBehavior; + } + + @Override + public SelectionManager getSelectionManager() { + return selectionManager; + } + @Override public WorldProvider getWorldProvider() { return this.worldProvider; @@ -191,17 +208,18 @@ public class Baritone implements IBaritone { } @Override - public LookBehavior getLookBehavior() { - return this.lookBehavior; + public CommandManager getCommandManager() { + return this.commandManager; } @Override - public MineProcess getMineProcess() { - return this.mineProcess; - } - - public static Executor getExecutor() { - return threadPool; + public void openClick() { + new Thread(() -> { + try { + Thread.sleep(100); + Helper.mc.addScheduledTask(() -> Helper.mc.displayGuiScreen(new GuiClick())); + } catch (Exception ignored) {} + }).start(); } public static Settings settings() { @@ -211,4 +229,8 @@ public class Baritone implements IBaritone { public static File getDir() { return dir; } + + 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 58a73369..d7268eb4 100644 --- a/src/main/java/baritone/BaritoneProvider.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -23,6 +23,7 @@ import baritone.api.bot.IUserManager; import baritone.api.cache.IWorldScanner; import baritone.api.bot.IBaritoneUser; import baritone.bot.UserManager; +import baritone.utils.command.BaritoneChatControl; import baritone.cache.WorldScanner; import baritone.utils.player.PrimaryPlayerContext; @@ -35,7 +36,14 @@ import java.util.List; */ public final class BaritoneProvider implements IBaritoneProvider { - private final Baritone primary = new Baritone(PrimaryPlayerContext.INSTANCE); + private final Baritone primary; + + { + this.primary = new Baritone(PrimaryPlayerContext.INSTANCE); + + // 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 8a1ea943..63e6c91e 100644 --- a/src/main/java/baritone/behavior/InventoryBehavior.java +++ b/src/main/java/baritone/behavior/InventoryBehavior.java @@ -21,21 +21,28 @@ import baritone.Baritone; import baritone.api.event.events.TickEvent; import baritone.utils.ToolSet; import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.init.Blocks; import net.minecraft.inventory.ClickType; -import net.minecraft.item.ItemPickaxe; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemTool; +import net.minecraft.item.*; +import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; -public class InventoryBehavior extends Behavior { +import java.util.ArrayList; +import java.util.OptionalInt; +import java.util.Random; +import java.util.function.Predicate; + +public final class InventoryBehavior extends Behavior { + public InventoryBehavior(Baritone baritone) { super(baritone); } @Override public void onTick(TickEvent event) { - if (!Baritone.settings().allowInventory.get()) { + if (!Baritone.settings().allowInventory.value) { return; } if (event.getType() == TickEvent.Type.OUT) { @@ -54,6 +61,34 @@ public class InventoryBehavior extends Behavior { } } + public void attemptToPutOnHotbar(int inMainInvy, Predicate disallowedHotbar) { + OptionalInt destination = getTempHotbarSlot(disallowedHotbar); + if (destination.isPresent()) { + swapWithHotBar(inMainInvy, destination.getAsInt()); + } + } + + public OptionalInt getTempHotbarSlot(Predicate disallowedHotbar) { + // we're using 0 and 8 for pickaxe and throwaway + ArrayList candidates = new ArrayList<>(); + for (int i = 1; i < 8; i++) { + if (ctx.player().inventory.mainInventory.get(i).isEmpty() && !disallowedHotbar.test(i)) { + candidates.add(i); + } + } + if (candidates.isEmpty()) { + for (int i = 1; i < 8; i++) { + if (!disallowedHotbar.test(i)) { + candidates.add(i); + } + } + } + if (candidates.isEmpty()) { + return OptionalInt.empty(); + } + return OptionalInt.of(candidates.get(new Random().nextInt(candidates.size()))); + } + private void swapWithHotBar(int inInventory, int inHotbar) { ctx.playerController().windowClick(ctx.player().inventoryContainer.windowId, inInventory < 9 ? inInventory + 36 : inInventory, inHotbar, ClickType.SWAP, ctx.player()); } @@ -61,14 +96,14 @@ public class InventoryBehavior extends Behavior { private int firstValidThrowaway() { // TODO offhand idk NonNullList invy = ctx.player().inventory.mainInventory; for (int i = 0; i < invy.size(); i++) { - if (Baritone.settings().acceptableThrowawayItems.get().contains(invy.get(i).getItem())) { + if (Baritone.settings().acceptableThrowawayItems.value.contains(invy.get(i).getItem())) { return i; } } 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; @@ -77,7 +112,7 @@ public 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; @@ -87,4 +122,65 @@ public class InventoryBehavior extends Behavior { } return bestInd; } + + public boolean hasGenericThrowaway() { + for (Item item : Baritone.settings().acceptableThrowawayItems.value) { + if (throwaway(false, stack -> item.equals(stack.getItem()))) { + return true; + } + } + return false; + } + + public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) { + IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z)); + if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && maybe.equals(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player())))) { + return true; // gotem + } + if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && ((ItemBlock) stack.getItem()).getBlock().equals(maybe.getBlock()))) { + return true; + } + for (Item item : Baritone.settings().acceptableThrowawayItems.value) { + if (throwaway(select, stack -> item.equals(stack.getItem()))) { + return true; + } + } + return false; + } + + public boolean throwaway(boolean select, Predicate desired) { + EntityPlayerSP p = ctx.player(); + NonNullList inv = p.inventory.mainInventory; + for (byte i = 0; i < 9; i++) { + ItemStack item = inv.get(i); + // this usage of settings() is okay because it's only called once during pathing + // (while creating the CalculationContext at the very beginning) + // and then it's called during execution + // since this function is never called during cost calculation, we don't need to migrate + // acceptableThrowawayItems to the CalculationContext + if (desired.test(item)) { + if (select) { + p.inventory.currentItem = i; + } + return true; + } + } + if (desired.test(p.inventory.offHandInventory.get(0))) { + // main hand takes precedence over off hand + // that means that if we have block A selected in main hand and block B in off hand, right clicking places block B + // we've already checked above ^ and the main hand can't possible have an acceptablethrowawayitem + // so we need to select in the main hand something that doesn't right click + // so not a shovel, not a hoe, not a block, etc + for (byte i = 0; i < 9; i++) { + ItemStack item = inv.get(i); + if (item.isEmpty() || item.getItem() instanceof ItemPickaxe) { + if (select) { + p.inventory.currentItem = i; + } + return true; + } + } + } + return false; + } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 31e76913..32e5c22f 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -53,7 +53,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { @Override public void updateTarget(Rotation target, boolean force) { this.target = target; - this.force = force || !Baritone.settings().freeLook.get(); + this.force = force || !Baritone.settings().freeLook.value; } @Override @@ -63,7 +63,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { } // Whether or not we're going to silently set our angles - boolean silent = Baritone.settings().antiCheatCompatibility.get() && !this.force; + boolean silent = Baritone.settings().antiCheatCompatibility.value && !this.force; switch (event.getState()) { case PRE: { @@ -72,7 +72,9 @@ public final class LookBehavior extends Behavior implements ILookBehavior { float oldPitch = ctx.player().rotationPitch; float desiredPitch = this.target.getPitch(); ctx.player().rotationPitch = desiredPitch; - if (desiredPitch == oldPitch) { + 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(); } this.target = null; @@ -95,15 +97,21 @@ public final class LookBehavior extends Behavior implements ILookBehavior { } } + public void pig() { + if (this.target != null) { + ctx.player().rotationYaw = this.target.getYaw(); + } + } + @Override public void onPlayerRotationMove(RotationMoveEvent event) { - if (this.target != null && !this.force) { + if (this.target != null) { event.setYaw(this.target.getYaw()); // If we have antiCheatCompatibility on, we're going to use the target value later in onPlayerUpdate() // Also the type has to be MOTION_UPDATE because that is called after JUMP - if (!Baritone.settings().antiCheatCompatibility.get() && event.getType() == RotationMoveEvent.Type.MOTION_UPDATE) { + if (!Baritone.settings().antiCheatCompatibility.value && event.getType() == RotationMoveEvent.Type.MOTION_UPDATE && !this.force) { this.target = null; } } diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index 5a54fa44..eff86215 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -18,14 +18,14 @@ package baritone.behavior; import baritone.Baritone; +import baritone.api.cache.Waypoint; import baritone.api.event.events.BlockInteractEvent; import baritone.api.event.events.PacketEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.type.EventState; +import baritone.api.utils.BetterBlockPos; import baritone.cache.ContainerMemory; -import baritone.cache.Waypoint; -import baritone.pathing.movement.CalculationContext; import baritone.utils.BlockStateInterface; import cabaletta.comms.upward.MessageEchestConfirmed; import net.minecraft.block.Block; @@ -51,6 +51,8 @@ import java.nio.file.Path; import java.util.*; /** + * doesn't work for horse inventories :^) + * * @author Brady * @since 8/6/2018 */ @@ -66,6 +68,9 @@ public final class MemoryBehavior extends Behavior { @Override public synchronized void onTick(TickEvent event) { + if (!Baritone.settings().containerMemory.value) { + return; + } if (event.getType() == TickEvent.Type.OUT) { enderChestWindowId = null; futureInventories.clear(); @@ -81,6 +86,9 @@ public final class MemoryBehavior extends Behavior { @Override public synchronized void onSendPacket(PacketEvent event) { + if (!Baritone.settings().containerMemory.value) { + return; + } Packet p = event.getPacket(); if (event.getState() == EventState.PRE) { @@ -95,8 +103,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 @@ -110,7 +118,6 @@ public final class MemoryBehavior extends Behavior { } if (p instanceof CPacketCloseWindow) { - updateInventory(); getCurrent().save(); } @@ -123,6 +130,9 @@ public final class MemoryBehavior extends Behavior { @Override public synchronized void onReceivePacket(PacketEvent event) { + if (!Baritone.settings().containerMemory.value) { + return; + } Packet p = event.getPacket(); if (event.getState() == EventState.PRE) { @@ -150,7 +160,6 @@ public final class MemoryBehavior extends Behavior { } if (p instanceof SPacketCloseWindow) { - updateInventory(); getCurrent().save(); } @@ -178,7 +187,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()))); } } @@ -189,6 +198,9 @@ public final class MemoryBehavior extends Behavior { private void updateInventory() { + if (!Baritone.settings().containerMemory.value) { + return; + } int windowId = ctx.player().openContainer.windowId; if (enderChestWindowId != null) { if (windowId == enderChestWindowId) { @@ -211,7 +223,7 @@ public final class MemoryBehavior extends Behavior { } private BlockPos neighboringConnectedBlock(BlockPos in) { - BlockStateInterface bsi = new CalculationContext(baritone).bsi; + BlockStateInterface bsi = baritone.bsi; Block block = bsi.get0(in).getBlock(); if (block != Blocks.TRAPPED_CHEST && block != Blocks.CHEST) { return null; // other things that have contents, but can be placed adjacent without combining @@ -255,7 +267,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)); } } @@ -269,6 +282,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 a636614c..86fd9505 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -23,20 +23,20 @@ import baritone.api.event.events.*; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalXZ; +import baritone.api.process.PathingCommand; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; import baritone.api.utils.PathCalculationResult; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; -import baritone.pathing.path.CutoffPath; import baritone.pathing.path.PathExecutor; -import baritone.utils.Helper; import baritone.utils.PathRenderer; +import baritone.utils.PathingCommandContext; import baritone.utils.pathing.Favoring; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.EmptyChunk; import java.util.ArrayList; import java.util.Comparator; @@ -50,9 +50,12 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private PathExecutor next; private Goal goal; + private CalculationContext context; private boolean safeToCancel; private boolean pauseRequestedLastTick; + private boolean unpausedLastTick; + private boolean pausedThisTick; private boolean cancelRequested; private boolean calcFailedLastTick; @@ -63,6 +66,8 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private boolean lastAutoJump; + private BetterBlockPos expectedSegmentStart; + private final LinkedBlockingQueue toDispatch = new LinkedBlockingQueue<>(); public PathingBehavior(Baritone baritone) { @@ -90,6 +95,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, baritone.getPathingControlManager().cancelEverything(); return; } + expectedSegmentStart = pathStart(); baritone.getPathingControlManager().preTick(); tickPath(); dispatchEvents(); @@ -103,30 +109,54 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } private void tickPath() { + pausedThisTick = false; if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; - baritone.getInputOverrideHandler().clearAllKeys(); - baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + if (unpausedLastTick) { + baritone.getInputOverrideHandler().clearAllKeys(); + baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + } + unpausedLastTick = false; + pausedThisTick = true; return; } + unpausedLastTick = true; if (cancelRequested) { cancelRequested = false; baritone.getInputOverrideHandler().clearAllKeys(); } - if (current == null) { - return; - } - safeToCancel = current.onTick(); synchronized (pathPlanLock) { + synchronized (pathCalcLock) { + if (inProgress != null) { + // we are calculating + // are we calculating the right thing though? 🤔 + BetterBlockPos calcFrom = inProgress.getStart(); + Optional currentBest = inProgress.bestPathSoFar(); + if ((current == null || !current.getPath().getDest().equals(calcFrom)) // if current ends in inProgress's start, then we're ok + && !calcFrom.equals(ctx.playerFeet()) && !calcFrom.equals(expectedSegmentStart) // if current starts in our playerFeet or pathStart, then we're ok + && (!currentBest.isPresent() || (!currentBest.get().positions().contains(ctx.playerFeet()) && !currentBest.get().positions().contains(expectedSegmentStart))) // if + ) { + // when it was *just* started, currentBest will be empty so we need to also check calcFrom since that's always present + inProgress.cancel(); // cancellation doesn't dispatch any events + } + } + } + if (current == null) { + return; + } + safeToCancel = current.onTick(); if (current.failed() || current.finished()) { current = null; if (goal == null || goal.isInGoal(ctx.playerFeet())) { logDebug("All done. At " + goal); queuePathEvent(PathEvent.AT_GOAL); next = null; + if (Baritone.settings().disconnectOnArrival.value) { + ctx.world().sendQuittingDisconnectingPacket(); + } return; } - if (next != null && !next.getPath().positions().contains(ctx.playerFeet())) { + if (next != null && !next.getPath().positions().contains(ctx.playerFeet()) && !next.getPath().positions().contains(expectedSegmentStart)) { // can contain either one // if the current path failed, we may not actually be on the next one, so make sure logDebug("Discarding next path as it does not contain current position"); // for example if we had a nicely planned ahead path that starts where current ends @@ -142,18 +172,18 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, queuePathEvent(PathEvent.CONTINUING_ONTO_PLANNED_NEXT); current = next; next = null; - current.onTick(); + current.onTick(); // don't waste a tick doing nothing, get started right away return; } // at this point, current just ended, but we aren't in the goal and have no plan for the future synchronized (pathCalcLock) { if (inProgress != null) { queuePathEvent(PathEvent.PATH_FINISHED_NEXT_STILL_CALCULATING); - // if we aren't calculating right now return; } + // we aren't calculating queuePathEvent(PathEvent.CALC_STARTED); - findPathInNewThread(pathStart(), true); + findPathInNewThread(expectedSegmentStart, true, context); } return; } @@ -167,7 +197,9 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, current.onTick(); return; } - current = current.trySplice(next); + if (Baritone.settings().splicePath.value) { + current = current.trySplice(next); + } if (next != null && current.getPath().getDest().equals(next.getPath().getDest())) { next = null; } @@ -181,14 +213,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return; } if (goal == null || goal.isInGoal(current.getPath().getDest())) { - // and this path dosen't get us all the way there + // and this path doesn't get us all the way there return; } - if (ticksRemainingInSegment().get() < Baritone.settings().planningTickLookAhead.get()) { - // and this path has 5 seconds or less left + if (ticksRemainingInSegment(false).get() < Baritone.settings().planningTickLookahead.value) { + // and this path has 7.5 seconds or less left + // don't include the current movement so a very long last movement (e.g. descend) doesn't trip it up + // if we actually included current, it wouldn't start planning ahead until the last movement was done, if the last movement took more than 7.5 seconds on its own logDebug("Path almost over. Planning ahead..."); queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_STARTED); - findPathInNewThread(current.getPath().getDest(), false); + findPathInNewThread(current.getPath().getDest(), false, context); } } } @@ -211,21 +245,36 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } } - @Override - public Optional ticksRemainingInSegment() { - if (current == null) { - return Optional.empty(); - } - return Optional.of(current.getPath().ticksRemainingFrom(current.getPosition())); - } - public void secretInternalSetGoal(Goal goal) { this.goal = goal; } - public boolean secretInternalSetGoalAndPath(Goal goal) { - secretInternalSetGoal(goal); - return secretInternalPath(); + public boolean secretInternalSetGoalAndPath(PathingCommand command) { + secretInternalSetGoal(command.goal); + if (command instanceof PathingCommandContext) { + context = ((PathingCommandContext) command).desiredCalcContext; + } else { + context = new CalculationContext(baritone, true); + } + if (goal == null) { + return false; + } + if (goal.isInGoal(ctx.playerFeet()) || goal.isInGoal(expectedSegmentStart)) { + return false; + } + synchronized (pathPlanLock) { + if (current != null) { + return false; + } + synchronized (pathCalcLock) { + if (inProgress != null) { + return false; + } + queuePathEvent(PathEvent.CALC_STARTED); + findPathInNewThread(expectedSegmentStart, true, context); + return true; + } + } } @Override @@ -233,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; @@ -248,11 +302,6 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return Optional.ofNullable(inProgress); } - @Override - public boolean isPathing() { - return this.current != null; - } - public boolean isSafeToCancel() { return current == null || safeToCancel; } @@ -275,7 +324,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, if (doIt) { secretInternalSegmentCancel(); } - baritone.getPathingControlManager().cancelEverything(); + baritone.getPathingControlManager().cancelEverything(); // regardless of if we can stop the current segment, we can still stop the processes return doIt; } @@ -285,6 +334,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, public void softCancelIfSafe() { synchronized (pathPlanLock) { + getInProgress().ifPresent(AbstractNodeCostSearch::cancel); // only cancel ours if (!isSafeToCancel()) { return; } @@ -292,59 +342,34 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, next = null; } cancelRequested = true; - getInProgress().ifPresent(AbstractNodeCostSearch::cancel); // only cancel ours // do everything BUT clear keys } // just cancel the current path - public void secretInternalSegmentCancel() { + private void secretInternalSegmentCancel() { queuePathEvent(PathEvent.CANCELED); synchronized (pathPlanLock) { - current = null; - next = null; + getInProgress().ifPresent(AbstractNodeCostSearch::cancel); + if (current != null) { + current = null; + next = null; + baritone.getInputOverrideHandler().clearAllKeys(); + baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + } } - baritone.getInputOverrideHandler().clearAllKeys(); - getInProgress().ifPresent(AbstractNodeCostSearch::cancel); - baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); } - public void forceCancel() { // NOT exposed on public api + @Override + public void forceCancel() { // exposed on public api because :sob: cancelEverything(); secretInternalSegmentCancel(); - inProgress = null; - } - - /** - * Start calculating a path if we aren't already - * - * @return true if this call started path calculation, false if it was already calculating or executing a path - */ - public boolean secretInternalPath() { - if (goal == null) { - return false; - } - if (goal.isInGoal(ctx.playerFeet())) { - return false; - } - synchronized (pathPlanLock) { - if (current != null) { - return false; - } - synchronized (pathCalcLock) { - if (inProgress != null) { - return false; - } - queuePathEvent(PathEvent.CALC_STARTED); - findPathInNewThread(pathStart(), true); - return true; - } + synchronized (pathCalcLock) { + inProgress = null; } } - public void secretCursedFunctionDoNotCall(IPath path) { - synchronized (pathPlanLock) { - current = new PathExecutor(this, path); - } + public CalculationContext secretInternalGetCalculationContext() { + return context; } /** @@ -398,7 +423,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, * @param start * @param talkAboutIt */ - private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt) { + private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt, CalculationContext context) { // this must be called with synchronization on pathCalcLock! // actually, we can check this, muahaha if (!Thread.holdsLock(pathCalcLock)) { @@ -408,6 +433,9 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, if (inProgress != null) { throw new IllegalStateException("Already doing it"); // should have been checked by caller } + if (!context.safeForThreadedUse) { + throw new IllegalStateException("Improper context thread safety level"); + } Goal goal = this.goal; if (goal == null) { logDebug("no goal"); // TODO should this be an exception too? definitely should be checked by caller @@ -416,13 +444,12 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, long primaryTimeout; long failureTimeout; if (current == null) { - primaryTimeout = Baritone.settings().primaryTimeoutMS.get(); - failureTimeout = Baritone.settings().failureTimeoutMS.get(); + primaryTimeout = Baritone.settings().primaryTimeoutMS.value; + failureTimeout = Baritone.settings().failureTimeoutMS.value; } else { - primaryTimeout = Baritone.settings().planAheadPrimaryTimeoutMS.get(); - failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.get(); + primaryTimeout = Baritone.settings().planAheadPrimaryTimeoutMS.value; + failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.value; } - CalculationContext context = new CalculationContext(baritone, true); // not safe to create on the other thread, it looks up a lot of stuff in minecraft AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context); if (!Objects.equals(pathfinder.getGoal(), goal)) { // will return the exact same object if simplification didn't happen logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); @@ -434,37 +461,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } PathCalculationResult calcResult = pathfinder.calculate(primaryTimeout, failureTimeout); - Optional path = calcResult.getPath(); - if (Baritone.settings().cutoffAtLoadBoundary.get()) { - path = path.map(p -> { - IPath result = p.cutoffAtLoadedChunks(context.bsi); - - if (result instanceof CutoffPath) { - logDebug("Cutting off path at edge of loaded chunks"); - logDebug("Length decreased by " + (p.length() - result.length())); - } else { - logDebug("Path ends within loaded chunks"); - } - - return result; - }); - } - - Optional executor = path.map(p -> { - IPath result = p.staticCutoff(goal); - - if (result instanceof CutoffPath) { - logDebug("Static cutoff " + p.length() + " to " + result.length()); - } - - return result; - }).map(p -> new PathExecutor(this, p)); - synchronized (pathPlanLock) { + Optional executor = calcResult.getPath().map(p -> new PathExecutor(PathingBehavior.this, p)); if (current == null) { if (executor.isPresent()) { - queuePathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING); - current = executor.get(); + if (executor.get().getPath().positions().contains(expectedSegmentStart)) { + queuePathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING); + current = executor.get(); + } else { + logDebug("Warning: discarding orphan path segment with incorrect start"); + } } else { if (calcResult.getType() != PathCalculationResult.Type.CANCELLATION && calcResult.getType() != PathCalculationResult.Type.EXCEPTION) { // don't dispatch CALC_FAILED on cancellation @@ -474,8 +480,12 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } else { if (next == null) { if (executor.isPresent()) { - queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_FINISHED); - next = executor.get(); + if (executor.get().getPath().getSrc().equals(current.getPath().getDest())) { + queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_FINISHED); + next = executor.get(); + } else { + logDebug("Warning: discarding orphan next segment with incorrect start"); + } } else { queuePathEvent(PathEvent.NEXT_CALC_FAILED); } @@ -486,7 +496,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } } if (talkAboutIt && current != null && current.getPath() != null) { - if (goal == null || goal.isInGoal(current.getPath().getDest())) { + if (goal.isInGoal(current.getPath().getDest())) { logDebug("Finished finding a path from " + start + " to " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered"); } else { logDebug("Found path segment from " + start + " towards " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered"); @@ -499,15 +509,15 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, }); } - public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { + private static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { Goal transformed = goal; - if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos) { + if (Baritone.settings().simplifyUnloadedYCoord.value && goal instanceof IGoalRenderPos) { BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); if (!context.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ())) { transformed = new GoalXZ(pos.getX(), pos.getZ()); } } - Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous); + Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous, context); return new AStarPathFinder(start.getX(), start.getY(), start.getZ(), transformed, favoring, context); } diff --git a/src/main/java/baritone/cache/CachedChunk.java b/src/main/java/baritone/cache/CachedChunk.java index ebf0bfc9..67ac6942 100644 --- a/src/main/java/baritone/cache/CachedChunk.java +++ b/src/main/java/baritone/cache/CachedChunk.java @@ -17,13 +17,19 @@ package baritone.cache; +import baritone.api.utils.BlockUtils; import baritone.utils.pathing.PathingBlockType; +import com.google.common.collect.ImmutableSet; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; -import java.util.*; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.Map; /** * @author Brady @@ -31,62 +37,60 @@ import java.util.*; */ public final class CachedChunk { - public static final Set BLOCKS_TO_KEEP_TRACK_OF; + public static final ImmutableSet BLOCKS_TO_KEEP_TRACK_OF = ImmutableSet.of( + Blocks.DIAMOND_BLOCK, + //Blocks.COAL_ORE, + Blocks.COAL_BLOCK, + //Blocks.IRON_ORE, + Blocks.IRON_BLOCK, + //Blocks.GOLD_ORE, + Blocks.GOLD_BLOCK, + Blocks.EMERALD_ORE, + Blocks.EMERALD_BLOCK, - static { - HashSet temp = new HashSet<>(); - //temp.add(Blocks.DIAMOND_ORE); - temp.add(Blocks.DIAMOND_BLOCK); - //temp.add(Blocks.COAL_ORE); - temp.add(Blocks.COAL_BLOCK); - //temp.add(Blocks.IRON_ORE); - temp.add(Blocks.IRON_BLOCK); - //temp.add(Blocks.GOLD_ORE); - temp.add(Blocks.GOLD_BLOCK); - temp.add(Blocks.EMERALD_ORE); - temp.add(Blocks.EMERALD_BLOCK); + Blocks.ENDER_CHEST, + Blocks.FURNACE, + Blocks.CHEST, + Blocks.TRAPPED_CHEST, + Blocks.END_PORTAL, + Blocks.END_PORTAL_FRAME, + Blocks.MOB_SPAWNER, + Blocks.BARRIER, + Blocks.OBSERVER, + Blocks.WHITE_SHULKER_BOX, + Blocks.ORANGE_SHULKER_BOX, + Blocks.MAGENTA_SHULKER_BOX, + Blocks.LIGHT_BLUE_SHULKER_BOX, + Blocks.YELLOW_SHULKER_BOX, + Blocks.LIME_SHULKER_BOX, + Blocks.PINK_SHULKER_BOX, + Blocks.GRAY_SHULKER_BOX, + Blocks.SILVER_SHULKER_BOX, + Blocks.CYAN_SHULKER_BOX, + Blocks.PURPLE_SHULKER_BOX, + Blocks.BLUE_SHULKER_BOX, + Blocks.BROWN_SHULKER_BOX, + Blocks.GREEN_SHULKER_BOX, + Blocks.RED_SHULKER_BOX, + Blocks.BLACK_SHULKER_BOX, + Blocks.PORTAL, + Blocks.HOPPER, + Blocks.BEACON, + Blocks.BREWING_STAND, + Blocks.SKULL, + Blocks.ENCHANTING_TABLE, + Blocks.ANVIL, + Blocks.LIT_FURNACE, + Blocks.BED, + Blocks.DRAGON_EGG, + Blocks.JUKEBOX, + Blocks.END_GATEWAY, + Blocks.WEB, + Blocks.NETHER_WART, + Blocks.LADDER, + Blocks.VINE + ); - temp.add(Blocks.ENDER_CHEST); - temp.add(Blocks.FURNACE); - temp.add(Blocks.CHEST); - temp.add(Blocks.TRAPPED_CHEST); - temp.add(Blocks.END_PORTAL); - temp.add(Blocks.END_PORTAL_FRAME); - temp.add(Blocks.MOB_SPAWNER); - temp.add(Blocks.BARRIER); - temp.add(Blocks.OBSERVER); - temp.add(Blocks.WHITE_SHULKER_BOX); - temp.add(Blocks.ORANGE_SHULKER_BOX); - temp.add(Blocks.MAGENTA_SHULKER_BOX); - temp.add(Blocks.LIGHT_BLUE_SHULKER_BOX); - temp.add(Blocks.YELLOW_SHULKER_BOX); - temp.add(Blocks.LIME_SHULKER_BOX); - temp.add(Blocks.PINK_SHULKER_BOX); - temp.add(Blocks.GRAY_SHULKER_BOX); - temp.add(Blocks.SILVER_SHULKER_BOX); - temp.add(Blocks.CYAN_SHULKER_BOX); - temp.add(Blocks.PURPLE_SHULKER_BOX); - temp.add(Blocks.BLUE_SHULKER_BOX); - temp.add(Blocks.BROWN_SHULKER_BOX); - temp.add(Blocks.GREEN_SHULKER_BOX); - temp.add(Blocks.RED_SHULKER_BOX); - temp.add(Blocks.BLACK_SHULKER_BOX); - temp.add(Blocks.PORTAL); - temp.add(Blocks.HOPPER); - temp.add(Blocks.BEACON); - temp.add(Blocks.BREWING_STAND); - temp.add(Blocks.SKULL); - temp.add(Blocks.ENCHANTING_TABLE); - temp.add(Blocks.ANVIL); - temp.add(Blocks.LIT_FURNACE); - temp.add(Blocks.BED); - temp.add(Blocks.DRAGON_EGG); - temp.add(Blocks.JUKEBOX); - temp.add(Blocks.END_GATEWAY); - temp.add(Blocks.WEB); - temp.add(Blocks.NETHER_WART); - BLOCKS_TO_KEEP_TRACK_OF = Collections.unmodifiableSet(temp); - } /** * The size of the chunk data in bits. Equal to 16 KiB. @@ -117,6 +121,8 @@ public final class CachedChunk { */ private final BitSet data; + private final Int2ObjectOpenHashMap special; + /** * The block names of each surface level block for generating an overview */ @@ -138,12 +144,30 @@ public final class CachedChunk { this.heightMap = new int[256]; this.specialBlockLocations = specialBlockLocations; this.cacheTimestamp = cacheTimestamp; + if (specialBlockLocations.isEmpty()) { + this.special = null; + } else { + this.special = new Int2ObjectOpenHashMap<>(); + setSpecial(); + } calculateHeightMap(); } + private final void setSpecial() { + for (Map.Entry> entry : specialBlockLocations.entrySet()) { + for (BlockPos pos : entry.getValue()) { + special.put(getPositionIndex(pos.getX(), pos.getY(), pos.getZ()), entry.getKey()); + } + } + } + public final IBlockState getBlock(int x, int y, int z, int dimension) { + int index = getPositionIndex(x, y, z); + PathingBlockType type = getType(index); int internalPos = z << 4 | x; - if (heightMap[internalPos] == y) { + if (heightMap[internalPos] == y && type != PathingBlockType.AVOID) { + // if the top block in a column is water, we cache it as AVOID but we don't want to just return default state water (which is not flowing) beacuse then it would try to path through it + // we have this exact block, it's a surface block /*System.out.println("Saying that " + x + "," + y + "," + z + " is " + state); if (!Minecraft.getMinecraft().world.getBlockState(new BlockPos(x + this.x * 16, y, z + this.z * 16)).getBlock().equals(state.getBlock())) { @@ -151,15 +175,29 @@ public final class CachedChunk { }*/ return overview[internalPos]; } - PathingBlockType type = getType(x, y, z); - if (type == PathingBlockType.SOLID && y == 127 && dimension == -1) { - return Blocks.BEDROCK.getDefaultState(); + if (special != null) { + String str = special.get(index); + if (str != null) { + return BlockUtils.stringToBlockRequired(str).getDefaultState(); + } + } + + if (type == PathingBlockType.SOLID) { + if (y == 127 && dimension == -1) { + // nether roof is always unbreakable + return Blocks.BEDROCK.getDefaultState(); + } + if (y < 5 && dimension == 0) { + // solid blocks below 5 are commonly bedrock + // however, returning bedrock always would be a little yikes + // discourage paths that include breaking blocks below 5 a little more heavily just so that it takes paths breaking what's known to be stone (at 5 or above) instead of what could maybe be bedrock (below 5) + return Blocks.OBSIDIAN.getDefaultState(); + } } return ChunkPacker.pathingTypeToBlock(type, dimension); } - private PathingBlockType getType(int x, int y, int z) { - int index = getPositionIndex(x, y, z); + private PathingBlockType getType(int index) { return PathingBlockType.fromBits(data.get(index), data.get(index + 1)); } @@ -187,11 +225,11 @@ public final class CachedChunk { return specialBlockLocations; } - public final LinkedList getAbsoluteBlocks(String blockType) { + public final ArrayList getAbsoluteBlocks(String blockType) { if (specialBlockLocations.get(blockType) == null) { return null; } - LinkedList res = new LinkedList<>(); + ArrayList res = new ArrayList<>(); for (BlockPos pos : specialBlockLocations.get(blockType)) { res.add(new BlockPos(pos.getX() + x * 16, pos.getY(), pos.getZ() + z * 16)); } diff --git a/src/main/java/baritone/cache/CachedRegion.java b/src/main/java/baritone/cache/CachedRegion.java index d8426197..630dcc9e 100644 --- a/src/main/java/baritone/cache/CachedRegion.java +++ b/src/main/java/baritone/cache/CachedRegion.java @@ -19,6 +19,7 @@ package baritone.cache; import baritone.Baritone; import baritone.api.cache.ICachedRegion; +import baritone.api.utils.BlockUtils; import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; @@ -87,19 +88,16 @@ public final class CachedRegion implements ICachedRegion { return chunks[x >> 4][z >> 4] != null; } - public final LinkedList getLocationsOf(String block) { - LinkedList res = new LinkedList<>(); + public final ArrayList getLocationsOf(String block) { + ArrayList res = new ArrayList<>(); for (int chunkX = 0; chunkX < 32; chunkX++) { for (int chunkZ = 0; chunkZ < 32; chunkZ++) { if (chunks[chunkX][chunkZ] == null) { continue; } - List locs = chunks[chunkX][chunkZ].getAbsoluteBlocks(block); - if (locs == null) { - continue; - } - for (BlockPos pos : locs) { - res.add(pos); + ArrayList locs = chunks[chunkX][chunkZ].getAbsoluteBlocks(block); + if (locs != null) { + res.addAll(locs); } } } @@ -152,7 +150,7 @@ public final class CachedRegion implements ICachedRegion { for (int z = 0; z < 32; z++) { if (chunks[x][z] != null) { for (int i = 0; i < 256; i++) { - out.writeUTF(ChunkPacker.blockToString(chunks[x][z].getOverview()[i].getBlock())); + out.writeUTF(BlockUtils.blockToString(chunks[x][z].getOverview()[i].getBlock())); } } } @@ -243,7 +241,7 @@ public final class CachedRegion implements ICachedRegion { for (int z = 0; z < 32; z++) { if (present[x][z]) { for (int i = 0; i < 256; i++) { - overview[x][z][i] = ChunkPacker.stringToBlock(in.readUTF()).getDefaultState(); + overview[x][z][i] = BlockUtils.stringToBlockRequired(in.readUTF()).getDefaultState(); } } } @@ -258,6 +256,7 @@ public final class CachedRegion implements ICachedRegion { int numSpecialBlockTypes = in.readShort() & 0xffff; for (int i = 0; i < numSpecialBlockTypes; i++) { String blockName = in.readUTF(); + BlockUtils.stringToBlockRequired(blockName); List locs = new ArrayList<>(); location[x][z].put(blockName, locs); int numLocations = in.readShort() & 0xffff; @@ -306,7 +305,7 @@ public final class CachedRegion implements ICachedRegion { } public synchronized final void removeExpired() { - long expiry = Baritone.settings().cachedChunksExpirySeconds.get(); + long expiry = Baritone.settings().cachedChunksExpirySeconds.value; if (expiry < 0) { return; } diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index b604ce48..e7405390 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -22,7 +22,7 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldData; -import baritone.utils.Helper; +import baritone.api.utils.Helper; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.util.math.BlockPos; @@ -32,7 +32,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; @@ -45,7 +44,7 @@ public final class CachedWorld implements ICachedWorld, Helper { /** * The maximum number of regions in any direction from (0,0) */ - private static final int REGION_MAX = 58594; + private static final int REGION_MAX = 30_000_000 / 512 + 1; /** * A map of all of the cached regions. @@ -71,8 +70,6 @@ public final class CachedWorld implements ICachedWorld, Helper { this.directory = directory.toString(); this.dimension = dimension; System.out.println("Cached world directory: " + directory); - // Insert an invalid region element - cachedRegions.put(0, null); Baritone.getExecutor().execute(new PackerThread()); Baritone.getExecutor().execute(() -> { try { @@ -108,9 +105,13 @@ public final class CachedWorld implements ICachedWorld, Helper { return region.isCached(blockX & 511, blockZ & 511); } + public final boolean regionLoaded(int blockX, int blockZ) { + return getRegion(blockX >> 9, blockZ >> 9) != null; + } + @Override - public final LinkedList getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq) { - LinkedList res = new LinkedList<>(); + public final ArrayList getLocationsOf(String block, int maximum, int centerX, int centerZ, int maxRegionDistanceSq) { + ArrayList res = new ArrayList<>(); int centerRegionX = centerX >> 9; int centerRegionZ = centerZ >> 9; @@ -127,7 +128,7 @@ public final class CachedWorld implements ICachedWorld, Helper { CachedRegion region = getOrCreateRegion(regionX, regionZ); if (region != null) { // TODO: 100% verify if this or addAll is faster. - region.getLocationsOf(block).forEach(res::add); + res.addAll(region.getLocationsOf(block)); } } } @@ -146,7 +147,7 @@ public final class CachedWorld implements ICachedWorld, Helper { @Override public final void save() { - if (!Baritone.settings().chunkCaching.get()) { + if (!Baritone.settings().chunkCaching.value) { System.out.println("Not saving to disk; chunk caching is disabled."); allRegions().forEach(region -> { if (region != null) { @@ -171,7 +172,7 @@ public final class CachedWorld implements ICachedWorld, Helper { * Delete regions that are too far from the player */ private synchronized void prune() { - if (!Baritone.settings().pruneRegionsFromRAM.get()) { + if (!Baritone.settings().pruneRegionsFromRAM.value) { return; } BlockPos pruneCenter = guessPosition(); @@ -179,8 +180,8 @@ public final class CachedWorld implements ICachedWorld, Helper { if (region == null) { continue; } - int distX = (region.getX() << 9 + 256) - pruneCenter.getX(); - int distZ = (region.getZ() << 9 + 256) - pruneCenter.getZ(); + int distX = ((region.getX() << 9) + 256) - pruneCenter.getX(); + int distZ = ((region.getZ() << 9) + 256) - pruneCenter.getZ(); double dist = Math.sqrt(distX * distX + distZ * distZ); if (dist > 1024) { logDebug("Deleting cached region " + region.getX() + "," + region.getZ() + " from ram"); @@ -215,7 +216,7 @@ public final class CachedWorld implements ICachedWorld, Helper { if (mostRecentlyModified == null) { return new BlockPos(0, 0, 0); } - return new BlockPos(mostRecentlyModified.x << 4 + 8, 0, mostRecentlyModified.z << 4 + 8); + return new BlockPos((mostRecentlyModified.x << 4) + 8, 0, (mostRecentlyModified.z << 4) + 8); } private synchronized List allRegions() { @@ -287,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 @@ -302,6 +304,9 @@ public final class CachedWorld implements ICachedWorld, Helper { } catch (InterruptedException e) { e.printStackTrace(); break; + } catch (Throwable th) { + // in the case of an exception, keep consuming from the queue so as not to leak memory + th.printStackTrace(); } } } diff --git a/src/main/java/baritone/cache/ChunkPacker.java b/src/main/java/baritone/cache/ChunkPacker.java index 60ac6331..bfeeabd7 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -17,15 +17,12 @@ package baritone.cache; +import baritone.api.utils.BlockUtils; import baritone.pathing.movement.MovementHelper; import baritone.utils.pathing.PathingBlockType; -import net.minecraft.block.Block; -import net.minecraft.block.BlockDoublePlant; -import net.minecraft.block.BlockFlower; -import net.minecraft.block.BlockTallGrass; +import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; -import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.BlockStateContainer; import net.minecraft.world.chunk.Chunk; @@ -39,8 +36,6 @@ import java.util.*; */ public final class ChunkPacker { - private static final Map resourceCache = new HashMap<>(); - private ChunkPacker() {} public static CachedChunk pack(Chunk chunk) { @@ -73,12 +68,12 @@ public final class ChunkPacker { for (int x = 0; x < 16; x++) { int index = CachedChunk.getPositionIndex(x, y, z); IBlockState state = bsc.get(x, y1, z); - boolean[] bits = getPathingBlockType(state).getBits(); + boolean[] bits = getPathingBlockType(state, chunk, x, y, z).getBits(); bitSet.set(index, bits[0]); bitSet.set(index + 1, bits[1]); Block block = state.getBlock(); if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { - String name = blockToString(block); + String name = BlockUtils.blockToString(block); specialBlocks.computeIfAbsent(name, b -> new ArrayList<>()).add(new BlockPos(x, y, z)); } } @@ -92,6 +87,7 @@ public final class ChunkPacker { //System.out.println("Chunk packing took " + (end - start) + "ms for " + chunk.x + "," + chunk.z); IBlockState[] blocks = new IBlockState[256]; + // @formatter:off for (int z = 0; z < 16; z++) { https://www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html for (int x = 0; x < 16; x++) { @@ -105,31 +101,37 @@ 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()); } - public static String blockToString(Block block) { - ResourceLocation loc = Block.REGISTRY.getNameForObject(block); - String name = loc.getPath(); // normally, only write the part after the minecraft: - if (!loc.getNamespace().equals("minecraft")) { - // Baritone is running on top of forge with mods installed, perhaps? - name = loc.toString(); // include the namespace with the colon - } - return name; - } - public static Block stringToBlock(String name) { - return resourceCache.computeIfAbsent(name, n -> Block.getBlockFromName(n.contains(":") ? n : "minecraft:" + n)); - } - - private static PathingBlockType getPathingBlockType(IBlockState state) { + private static PathingBlockType getPathingBlockType(IBlockState state, Chunk chunk, int x, int y, int z) { Block block = state.getBlock(); - if (block == Blocks.WATER && !MovementHelper.isFlowing(state)) { + if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) { // only water source blocks are plausibly usable, flowing water should be avoid + // FLOWING_WATER is a waterfall, it doesn't really matter and caching it as AVOID just makes it look wrong + if (MovementHelper.possiblyFlowing(state)) { + return PathingBlockType.AVOID; + } + if ( + (x != 15 && MovementHelper.possiblyFlowing(chunk.getBlockState(x + 1, y, z))) + || (x != 0 && MovementHelper.possiblyFlowing(chunk.getBlockState(x - 1, y, z))) + || (z != 15 && MovementHelper.possiblyFlowing(chunk.getBlockState(x, y, z + 1))) + || (z != 0 && MovementHelper.possiblyFlowing(chunk.getBlockState(x, y, z - 1))) + ) { + return PathingBlockType.AVOID; + } + if (x == 0 || x == 15 || z == 0 || z == 15) { + if (BlockLiquid.getSlopeAngle(chunk.getWorld(), new BlockPos(x + chunk.x << 4, y, z + chunk.z << 4), state.getMaterial(), state) == -1000.0F) { + return PathingBlockType.WATER; + } + return PathingBlockType.AVOID; + } return PathingBlockType.WATER; } - if (MovementHelper.avoidWalkingInto(block) || block == Blocks.FLOWING_WATER || MovementHelper.isBottomSlab(state)) { + if (MovementHelper.avoidWalkingInto(block) || MovementHelper.isBottomSlab(state)) { return PathingBlockType.AVOID; } // We used to do an AABB check here diff --git a/src/main/java/baritone/cache/ContainerMemory.java b/src/main/java/baritone/cache/ContainerMemory.java index f2b69e5c..e79435e3 100644 --- a/src/main/java/baritone/cache/ContainerMemory.java +++ b/src/main/java/baritone/cache/ContainerMemory.java @@ -17,6 +17,7 @@ package baritone.cache; +import baritone.Baritone; import baritone.api.cache.IContainerMemory; import baritone.api.cache.IRememberedInventory; import baritone.api.utils.IPlayerContext; @@ -28,6 +29,7 @@ import net.minecraft.util.math.BlockPos; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.*; @@ -44,6 +46,8 @@ public class ContainerMemory implements IContainerMemory { this.saveTo = saveTo; try { read(Files.readAllBytes(saveTo)); + } catch (NoSuchFileException ignored) { + inventories.clear(); } catch (Exception ex) { ex.printStackTrace(); inventories.clear(); @@ -69,6 +73,9 @@ public class ContainerMemory implements IContainerMemory { } public synchronized void save() throws IOException { + if (!Baritone.settings().containerMemory.value) { + return; + } ByteBuf buf = Unpooled.buffer(0, Integer.MAX_VALUE); PacketBuffer out = new PacketBuffer(buf); out.writeInt(inventories.size()); diff --git a/src/main/java/baritone/cache/WaypointCollection.java b/src/main/java/baritone/cache/WaypointCollection.java index ef319602..222adfdf 100644 --- a/src/main/java/baritone/cache/WaypointCollection.java +++ b/src/main/java/baritone/cache/WaypointCollection.java @@ -19,7 +19,8 @@ package baritone.cache; import baritone.api.cache.IWaypoint; import baritone.api.cache.IWaypointCollection; -import net.minecraft.util.math.BlockPos; +import baritone.api.cache.Waypoint; +import baritone.api.utils.BetterBlockPos; import java.io.*; import java.nio.file.Files; @@ -85,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/WorldProvider.java b/src/main/java/baritone/cache/WorldProvider.java index 83cf460d..c277a0d0 100644 --- a/src/main/java/baritone/cache/WorldProvider.java +++ b/src/main/java/baritone/cache/WorldProvider.java @@ -19,11 +19,12 @@ package baritone.cache; import baritone.Baritone; import baritone.api.cache.IWorldProvider; -import baritone.utils.Helper; +import baritone.api.utils.Helper; import baritone.utils.accessor.IAnvilChunkLoader; import baritone.utils.accessor.IChunkProviderServer; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.world.WorldServer; +import org.apache.commons.lang3.SystemUtils; import java.io.File; import java.io.FileOutputStream; @@ -61,7 +62,7 @@ public class WorldProvider implements IWorldProvider, Helper { IntegratedServer integratedServer = mc.getIntegratedServer(); // If there is an integrated server running (Aka Singleplayer) then do magic to find the world save file - if (integratedServer != null) { + if (mc.isSingleplayer()) { WorldServer localServerWorld = integratedServer.getWorld(dimension); IChunkProviderServer provider = (IChunkProviderServer) localServerWorld.getChunkProvider(); IAnvilChunkLoader loader = (IAnvilChunkLoader) provider.getChunkLoader(); @@ -76,7 +77,11 @@ public class WorldProvider implements IWorldProvider, Helper { directory = new File(directory, "baritone"); readme = directory; } else { // Otherwise, the server must be remote... - directory = new File(Baritone.getDir(), mc.getCurrentServerData().serverIP); + String folderName = mc.getCurrentServerData().serverIP; + if (SystemUtils.IS_OS_WINDOWS) { + folderName = folderName.replace(":", "_"); + } + directory = new File(Baritone.getDir(), folderName); readme = Baritone.getDir(); } diff --git a/src/main/java/baritone/cache/WorldScanner.java b/src/main/java/baritone/cache/WorldScanner.java index 463dd22f..d31c17ab 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -17,35 +17,37 @@ package baritone.cache; +import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldScanner; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; -import net.minecraft.block.Block; +import baritone.utils.accessor.IBlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.chunk.BlockStateContainer; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.stream.IntStream; public enum WorldScanner implements IWorldScanner { INSTANCE; + 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; @@ -53,6 +55,9 @@ public enum WorldScanner implements IWorldScanner { int playerChunkZ = ctx.playerFeet().getZ() >> 4; int playerY = ctx.playerFeet().getY(); + int playerYBlockStateContainerIndex = playerY >> 4; + int[] coordinateIterationOrder = IntStream.range(0, 16).boxed().sorted(Comparator.comparingInt(y -> Math.abs(y - playerYBlockStateContainerIndex))).mapToInt(x -> x).toArray(); + int searchRadiusSq = 0; boolean foundWithinY = false; while (true) { @@ -72,7 +77,9 @@ public enum WorldScanner implements IWorldScanner { continue; } allUnloaded = false; - scanChunkInto(chunkX << 4, chunkZ << 4, chunk, blocks, res, max, yLevelThreshold, playerY); + if (scanChunkInto(chunkX << 4, chunkZ << 4, chunk, filter, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { + foundWithinY = true; + } } } if ((allUnloaded && foundChunks) @@ -86,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(); } @@ -100,35 +107,66 @@ public enum WorldScanner implements IWorldScanner { } ArrayList res = new ArrayList<>(); - scanChunkInto(pos.x << 4, pos.z << 4, chunk, blocks, res, max, yLevelThreshold, playerY); + scanChunkInto(pos.x << 4, pos.z << 4, chunk, filter, res, max, yLevelThreshold, playerY, DEFAULT_COORDINATE_ITERATION_ORDER); return res; } - public void scanChunkInto(int chunkX, int chunkZ, Chunk chunk, List search, Collection result, int max, int yLevelThreshold, int playerY) { + private boolean scanChunkInto(int chunkX, int chunkZ, Chunk chunk, BlockOptionalMetaLookup filter, Collection result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { ExtendedBlockStorage[] chunkInternalStorageArray = chunk.getBlockStorageArray(); - for (int y0 = 0; y0 < 16; y0++) { + boolean foundWithinY = false; + for (int yIndex = 0; yIndex < 16; yIndex++) { + int y0 = coordinateIterationOrder[yIndex]; ExtendedBlockStorage extendedblockstorage = chunkInternalStorageArray[y0]; if (extendedblockstorage == null) { 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; - result.add(new BlockPos(chunkX | x, yy, chunkZ | z)); - if (result.size() >= max && Math.abs(yy - playerY) < yLevelThreshold) { - return; + 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 | (i & 15), y, chunkZ | ((i >> 4) & 15))); } } } + return foundWithinY; + } + + public int repack(IPlayerContext ctx) { + IChunkProvider chunkProvider = ctx.world().getChunkProvider(); + ICachedWorld cachedWorld = ctx.worldData().getCachedWorld(); + + BetterBlockPos playerPos = ctx.playerFeet(); + int playerChunkX = playerPos.getX() >> 4; + int playerChunkZ = playerPos.getZ() >> 4; + int queued = 0; + for (int x = playerChunkX - 40; x <= playerChunkX + 40; x++) { + for (int z = playerChunkZ - 40; z <= playerChunkZ + 40; z++) { + Chunk chunk = chunkProvider.getLoadedChunk(x, z); + + if (chunk != null && !chunk.isEmpty()) { + queued++; + cachedWorld.queueForPacking(chunk); + } + } + } + + return queued; } } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 846d7746..be374f21 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -23,8 +23,9 @@ import baritone.api.event.events.type.EventState; import baritone.api.event.listener.IEventBus; import baritone.api.event.listener.IGameEventListener; import baritone.bot.UserManager; +import baritone.api.utils.Helper; import baritone.cache.WorldProvider; -import baritone.utils.Helper; +import baritone.utils.BlockStateInterface; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; @@ -48,15 +49,20 @@ public final class GameEventHandler implements IEventBus, Helper { @Override public final void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.IN) { + try { + baritone.bsi = new BlockStateInterface(baritone.getPlayerContext(), true); + } catch (Exception ex) { + baritone.bsi = null; + } + } else { + baritone.bsi = null; + } listeners.forEach(l -> l.onTick(event)); } @Override public final void onPlayerUpdate(PlayerUpdateEvent event) { - // TODO temporary bot event call prevention - if (event.getPlayer() != baritone.getPlayerContext().player()) - return; - listeners.forEach(l -> l.onPlayerUpdate(event)); } @@ -68,6 +74,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(); @@ -127,10 +143,6 @@ public final class GameEventHandler implements IEventBus, Helper { @Override public void onPlayerRotationMove(RotationMoveEvent event) { - // TODO temporary bot event call prevention - if (event.getPlayer() != baritone.getPlayerContext().player()) - return; - listeners.forEach(l -> l.onPlayerRotationMove(event)); } diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index b32fc99f..3e38ff41 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -25,7 +25,6 @@ import baritone.api.utils.BetterBlockPos; import baritone.pathing.calc.openset.BinaryHeapOpenSet; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Moves; -import baritone.utils.Helper; import baritone.utils.pathing.BetterWorldBorder; import baritone.utils.pathing.Favoring; import baritone.utils.pathing.MutableMoveResult; @@ -37,7 +36,7 @@ import java.util.Optional; * * @author leijurv */ -public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { +public final class AStarPathFinder extends AbstractNodeCostSearch { private final Favoring favoring; private final CalculationContext calcContext; @@ -55,30 +54,29 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel startNode.combinedCost = startNode.estimatedCostToGoal; BinaryHeapOpenSet openSet = new BinaryHeapOpenSet(); openSet.insert(startNode); - bestSoFar = new PathNode[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) - double[] bestHeuristicSoFar = new double[COEFFICIENTS.length]; + double[] bestHeuristicSoFar = new double[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) for (int i = 0; i < bestHeuristicSoFar.length; i++) { bestHeuristicSoFar[i] = startNode.estimatedCostToGoal; bestSoFar[i] = startNode; } MutableMoveResult res = new MutableMoveResult(); - Favoring favored = favoring; BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world.getWorldBorder()); long startTime = System.currentTimeMillis(); - boolean slowPath = Baritone.settings().slowPath.get(); + boolean slowPath = Baritone.settings().slowPath.value; if (slowPath) { - logDebug("slowPath is on, path timeout will be " + Baritone.settings().slowPathTimeoutMS.get() + "ms instead of " + primaryTimeout + "ms"); + logDebug("slowPath is on, path timeout will be " + Baritone.settings().slowPathTimeoutMS.value + "ms instead of " + primaryTimeout + "ms"); } - long primaryTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.get() : primaryTimeout); - long failureTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.get() : failureTimeout); + long primaryTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.value : primaryTimeout); + long failureTimeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS.value : failureTimeout); boolean failing = true; int numNodes = 0; int numMovementsConsidered = 0; int numEmptyChunk = 0; - boolean favoring = !favored.isEmpty(); + boolean isFavoring = !favoring.isEmpty(); int timeCheckInterval = 1 << 6; - int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior - boolean minimumImprovementRepropagation = Baritone.settings().minimumImprovementRepropagation.get(); + int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.value; // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior + double minimumImprovement = Baritone.settings().minimumImprovementRepropagation.value ? MIN_IMPROVEMENT : 0; + Moves[] allMoves = Moves.values(); while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) { if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond) long now = System.currentTimeMillis(); // since nanoTime is slow on windows (takes many microseconds) @@ -88,9 +86,8 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel } if (slowPath) { try { - Thread.sleep(Baritone.settings().slowPathTimeDelayMS.get()); - } catch (InterruptedException ex) { - } + Thread.sleep(Baritone.settings().slowPathTimeDelayMS.value); + } catch (InterruptedException ignored) {} } PathNode currentNode = openSet.removeLowest(); mostRecentConsidered = currentNode; @@ -99,7 +96,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered"); return Optional.of(new Path(startNode, currentNode, numNodes, goal, calcContext)); } - for (Moves moves : Moves.values()) { + for (Moves moves : allMoves) { int newX = currentNode.x + moves.xOffset; int newZ = currentNode.z + moves.zOffset; if ((newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) && !calcContext.isLoaded(newX, newZ)) { @@ -136,21 +133,13 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel throw new IllegalStateException(moves + " " + res.y + " " + (currentNode.y + moves.yOffset)); } long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z); - if (favoring) { + if (isFavoring) { // see issue #18 - actionCost *= favored.calculate(hashCode); + actionCost *= favoring.calculate(hashCode); } PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode); double tentativeCost = currentNode.cost + actionCost; - if (tentativeCost < neighbor.cost) { - double improvementBy = neighbor.cost - tentativeCost; - // there are floating point errors caused by random combinations of traverse and diagonal over a flat area - // that means that sometimes there's a cost improvement of like 10 ^ -16 - // it's not worth the time to update the costs, decrease-key the heap, potentially repropagate, etc - if (improvementBy < 0.01 && minimumImprovementRepropagation) { - // who cares about a hundredth of a tick? that's half a millisecond for crying out loud! - continue; - } + if (neighbor.cost - tentativeCost > minimumImprovement) { neighbor.previous = currentNode; neighbor.cost = tentativeCost; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; @@ -159,15 +148,12 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel } else { openSet.insert(neighbor);//dont double count, dont insert into open set if it's already there } - for (int i = 0; i < bestSoFar.length; i++) { + for (int i = 0; i < COEFFICIENTS.length; i++) { double heuristic = neighbor.estimatedCostToGoal + neighbor.cost / COEFFICIENTS[i]; - if (heuristic < bestHeuristicSoFar[i]) { - if (bestHeuristicSoFar[i] - heuristic < 0.01 && minimumImprovementRepropagation) { - continue; - } + if (bestHeuristicSoFar[i] - heuristic > minimumImprovement) { bestHeuristicSoFar[i] = heuristic; bestSoFar[i] = neighbor; - if (getDistFromStartSq(neighbor) > MIN_DIST_PATH * MIN_DIST_PATH) { + if (failing && getDistFromStartSq(neighbor) > MIN_DIST_PATH * MIN_DIST_PATH) { failing = false; } } @@ -182,28 +168,10 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel System.out.println("Open set size: " + openSet.size()); System.out.println("PathNode map size: " + mapSize()); System.out.println((int) (numNodes * 1.0 / ((System.currentTimeMillis() - startTime) / 1000F)) + " nodes per second"); - double bestDist = 0; - for (int i = 0; i < bestSoFar.length; i++) { - if (bestSoFar[i] == null) { - continue; - } - double dist = getDistFromStartSq(bestSoFar[i]); - if (dist > bestDist) { - bestDist = dist; - } - if (dist > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared - logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, A* cost coefficient " + COEFFICIENTS[i] + ", " + numMovementsConsidered + " movements considered"); - if (COEFFICIENTS[i] >= 3) { - System.out.println("Warning: cost coefficient is greater than three! Probably means that"); - System.out.println("the path I found is pretty terrible (like sneak-bridging for dozens of blocks)"); - System.out.println("But I'm going to do it anyway, because yolo"); - } - System.out.println("Path goes for " + Math.sqrt(dist) + " blocks"); - return Optional.of(new Path(startNode, bestSoFar[i], numNodes, goal, calcContext)); - } + Optional result = bestSoFar(true, numNodes); + if (result.isPresent()) { + logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered"); } - logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); - logDebug("No path found =("); - return Optional.empty(); + return result; } } diff --git a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java index 78710204..a67384ac 100644 --- a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java @@ -22,9 +22,9 @@ import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; import baritone.api.utils.PathCalculationResult; import baritone.pathing.movement.CalculationContext; -import baritone.utils.Helper; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.Optional; @@ -34,7 +34,7 @@ import java.util.Optional; * * @author leijurv */ -public abstract class AbstractNodeCostSearch implements IPathFinder { +public abstract class AbstractNodeCostSearch implements IPathFinder, Helper { protected final int startX; protected final int startY; @@ -53,7 +53,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { protected PathNode mostRecentConsidered; - protected PathNode[] bestSoFar; + protected final PathNode[] bestSoFar = new PathNode[COEFFICIENTS.length]; private volatile boolean isFinished; @@ -63,13 +63,23 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { * This is really complicated and hard to explain. I wrote a comment in the old version of MineBot but it was so * long it was easier as a Google Doc (because I could insert charts). * - * @see + * @see here */ - protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; // big TODO tune + protected static final double[] COEFFICIENTS = {1.5, 2, 2.5, 3, 4, 5, 10}; + /** * If a path goes less than 5 blocks and doesn't make it to its goal, it's not worth considering. */ - protected final static double MIN_DIST_PATH = 5; + protected static final double MIN_DIST_PATH = 5; + + /** + * there are floating point errors caused by random combinations of traverse and diagonal over a flat area + * that means that sometimes there's a cost improvement of like 10 ^ -16 + * it's not worth the time to update the costs, decrease-key the heap, potentially repropagate, etc + *

+ * who cares about a hundredth of a tick? that's half a millisecond for crying out loud! + */ + protected static final double MIN_IMPROVEMENT = 0.01; AbstractNodeCostSearch(int startX, int startY, int startZ, Goal goal, CalculationContext context) { this.startX = startX; @@ -77,7 +87,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { this.startZ = startZ; this.goal = goal; this.context = context; - this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.get()); + this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.value); } public void cancel() { @@ -92,20 +102,32 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { cancelRequested = false; try { IPath path = calculate0(primaryTimeout, failureTimeout).map(IPath::postProcess).orElse(null); - isFinished = true; if (cancelRequested) { - return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION, path); + return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION); } if (path == null) { return new PathCalculationResult(PathCalculationResult.Type.FAILURE); } + int previousLength = path.length(); + path = path.cutoffAtLoadedChunks(context.bsi); + if (path.length() < previousLength) { + Helper.HELPER.logDebug("Cutting off path at edge of loaded chunks"); + Helper.HELPER.logDebug("Length decreased by " + (previousLength - path.length())); + } else { + Helper.HELPER.logDebug("Path ends within loaded chunks"); + } + previousLength = path.length(); + path = path.staticCutoff(goal); + if (path.length() < previousLength) { + Helper.HELPER.logDebug("Static cutoff " + previousLength + " to " + path.length()); + } if (goal.isInGoal(path.getDest())) { return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_TO_GOAL, path); } else { return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path); } } catch (Exception e) { - Helper.HELPER.logDebug("Pathing exception: " + e); + Helper.HELPER.logDirect("Pathing exception: " + e); e.printStackTrace(); return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION); } finally { @@ -158,25 +180,43 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(startNode, node, 0, goal, context)); } - protected int mapSize() { - return map.size(); - } - @Override public Optional bestPathSoFar() { - if (startNode == null || bestSoFar == null) { + return bestSoFar(false, 0); + } + + protected Optional bestSoFar(boolean logInfo, int numNodes) { + if (startNode == null) { return Optional.empty(); } - for (int i = 0; i < bestSoFar.length; i++) { + double bestDist = 0; + for (int i = 0; i < COEFFICIENTS.length; i++) { if (bestSoFar[i] == null) { continue; } - if (getDistFromStartSq(bestSoFar[i]) > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared - return Optional.of(new Path(startNode, bestSoFar[i], 0, goal, context)); + double dist = getDistFromStartSq(bestSoFar[i]); + if (dist > bestDist) { + bestDist = dist; + } + if (dist > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared + if (logInfo) { + if (COEFFICIENTS[i] >= 3) { + System.out.println("Warning: cost coefficient is greater than three! Probably means that"); + System.out.println("the path I found is pretty terrible (like sneak-bridging for dozens of blocks)"); + System.out.println("But I'm going to do it anyway, because yolo"); + } + System.out.println("Path goes for " + Math.sqrt(dist) + " blocks"); + logDebug("A* cost coefficient " + COEFFICIENTS[i]); + } + return Optional.of(new Path(startNode, bestSoFar[i], numNodes, goal, context)); } } // instead of returning bestSoFar[0], be less misleading // if it actually won't find any path, don't make them think it will by rendering a dark blue that will never actually happen + if (logInfo) { + logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); + logDebug("No path found =("); + } return Optional.empty(); } @@ -189,4 +229,12 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { public final Goal getGoal() { return goal; } + + public BetterBlockPos getStart() { + return new BetterBlockPos(startX, startY, startZ); + } + + protected int mapSize() { + return map.size(); + } } diff --git a/src/main/java/baritone/pathing/calc/Path.java b/src/main/java/baritone/pathing/calc/Path.java index 37441743..f7bfbaa2 100644 --- a/src/main/java/baritone/pathing/calc/Path.java +++ b/src/main/java/baritone/pathing/calc/Path.java @@ -21,11 +21,11 @@ import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.Moves; import baritone.pathing.path.CutoffPath; -import baritone.utils.Helper; import baritone.utils.pathing.PathBase; import java.util.ArrayList; @@ -72,31 +72,12 @@ class Path extends PathBase { this.start = new BetterBlockPos(start.x, start.y, start.z); this.end = new BetterBlockPos(end.x, end.y, end.z); this.numNodes = numNodes; - this.path = new ArrayList<>(); this.movements = new ArrayList<>(); - this.nodes = new ArrayList<>(); this.goal = goal; this.context = context; - assemblePath(end); - } - - @Override - public Goal getGoal() { - return goal; - } - - /** - * Assembles this path given the end node. - * - * @param end The end node - */ - private void assemblePath(PathNode end) { - if (!path.isEmpty() || !movements.isEmpty()) { - throw new IllegalStateException(); - } PathNode current = end; LinkedList tempPath = new LinkedList<>(); - LinkedList tempNodes = new LinkedList(); + LinkedList tempNodes = new LinkedList<>(); // Repeatedly inserting to the beginning of an arraylist is O(n^2) // Instead, do it into a linked list, then convert at the end while (current != null) { @@ -107,8 +88,13 @@ class Path extends PathBase { // Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is // inserting into a LinkedList keeps track of length, then when we addall (which calls .toArray) it's able // to performantly do that conversion since it knows the length. - path.addAll(tempPath); - nodes.addAll(tempNodes); + this.path = new ArrayList<>(tempPath); + this.nodes = new ArrayList<>(tempNodes); + } + + @Override + public Goal getGoal() { + return goal; } private boolean assembleMovements() { diff --git a/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java b/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java index 6de8290d..2f7a0af5 100644 --- a/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java +++ b/src/main/java/baritone/pathing/calc/openset/LinkedListOpenSet.java @@ -27,6 +27,7 @@ import baritone.pathing.calc.PathNode; * @author leijurv */ class LinkedListOpenSet implements IOpenSet { + private Node first = null; @Override @@ -83,6 +84,7 @@ class LinkedListOpenSet implements IOpenSet { } public static class Node { //wrapper with next + private Node nextOpen; private PathNode val; } diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index 4c3b299d..8e2fc518 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -34,6 +34,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + /** * @author Brady * @since 8/7/2018 @@ -42,6 +44,7 @@ public class CalculationContext { private static final ItemStack STACK_BUCKET_WATER = new ItemStack(Items.WATER_BUCKET); + public final boolean safeForThreadedUse; public final IBaritone baritone; public final World world; public final WorldData worldData; @@ -50,18 +53,22 @@ public class CalculationContext { public final boolean hasWaterBucket; public final boolean hasThrowaway; public final boolean canSprint; - public final double placeBlockCost; + protected final double placeBlockCost; // protected because you should call the function instead public final boolean allowBreak; public final boolean allowParkour; public final boolean allowParkourPlace; public final boolean allowJumpAt256; + 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; public final double waterWalkSpeed; public final double breakBlockAdditionalCost; - public final double jumpPenalty; + public double backtrackCostFavoringCoefficient; + public double jumpPenalty; public final double walkOnWaterOnePenalty; public final BetterWorldBorder worldBorder; @@ -70,33 +77,38 @@ public class CalculationContext { } public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { + this.safeForThreadedUse = forUseOnAnotherThread; this.baritone = baritone; EntityPlayerSP player = baritone.getPlayerContext().player(); this.world = baritone.getPlayerContext().world(); this.worldData = (WorldData) baritone.getWorldProvider().getCurrentWorld(); - this.bsi = new BlockStateInterface(world, worldData, forUseOnAnotherThread); // TODO TODO TODO + this.bsi = new BlockStateInterface(world, worldData, forUseOnAnotherThread); this.toolSet = new ToolSet(player); - this.hasThrowaway = Baritone.settings().allowPlace.get() && MovementHelper.throwaway(baritone.getPlayerContext(), false); - this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.get() && InventoryPlayer.isHotbar(player.inventory.getSlotFor(STACK_BUCKET_WATER)) && !world.provider.isNether(); - this.canSprint = Baritone.settings().allowSprint.get() && player.getFoodStats().getFoodLevel() > 6; - this.placeBlockCost = Baritone.settings().blockPlacementPenalty.get(); - this.allowBreak = Baritone.settings().allowBreak.get(); - this.allowParkour = Baritone.settings().allowParkour.get(); - this.allowParkourPlace = Baritone.settings().allowParkourPlace.get(); - this.allowJumpAt256 = Baritone.settings().allowJumpAt256.get(); - this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.get(); - this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.get(); - this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.get(); - this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.get(); + this.hasThrowaway = Baritone.settings().allowPlace.value && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway(); + this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.value && InventoryPlayer.isHotbar(player.inventory.getSlotFor(STACK_BUCKET_WATER)) && !world.provider.isNether(); + this.canSprint = Baritone.settings().allowSprint.value && player.getFoodStats().getFoodLevel() > 6; + this.placeBlockCost = Baritone.settings().blockPlacementPenalty.value; + this.allowBreak = Baritone.settings().allowBreak.value; + this.allowParkour = Baritone.settings().allowParkour.value; + this.allowParkourPlace = Baritone.settings().allowParkourPlace.value; + this.allowJumpAt256 = Baritone.settings().allowJumpAt256.value; + 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; int depth = EnchantmentHelper.getDepthStriderModifier(player); if (depth > 3) { depth = 3; } float mult = depth / 3.0F; this.waterWalkSpeed = ActionCosts.WALK_ONE_IN_WATER_COST * (1 - mult) + ActionCosts.WALK_ONE_BLOCK_COST * mult; - this.breakBlockAdditionalCost = Baritone.settings().blockBreakAdditionalPenalty.get(); - this.jumpPenalty = Baritone.settings().jumpPenalty.get(); - this.walkOnWaterOnePenalty = Baritone.settings().walkOnWaterOnePenalty.get(); + this.breakBlockAdditionalCost = Baritone.settings().blockBreakAdditionalPenalty.value; + this.backtrackCostFavoringCoefficient = Baritone.settings().backtrackCostFavoringCoefficient.value; + this.jumpPenalty = Baritone.settings().jumpPenalty.value; + this.walkOnWaterOnePenalty = Baritone.settings().walkOnWaterOnePenalty.value; // why cache these things here, why not let the movements just get directly from settings? // because if some movements are calculated one way and others are calculated another way, // then you get a wildly inconsistent path that isn't optimal for either scenario. @@ -123,21 +135,32 @@ public class CalculationContext { return get(x, y, z).getBlock(); } - public boolean canPlaceThrowawayAt(int x, int y, int z) { + public double costOfPlacingAt(int x, int y, int z) { if (!hasThrowaway) { // only true if allowPlace is true, see constructor - return false; + return COST_INF; } if (isPossiblyProtected(x, y, z)) { - return false; + return COST_INF; } - return worldBorder.canPlaceAt(x, z); // TODO perhaps MovementHelper.canPlaceAgainst could also use this? + if (!worldBorder.canPlaceAt(x, z)) { + // TODO perhaps MovementHelper.canPlaceAgainst could also use this? + return COST_INF; + } + return placeBlockCost; } - public boolean canBreakAt(int x, int y, int z) { + public double breakCostMultiplierAt(int x, int y, int z) { if (!allowBreak) { - return false; + return COST_INF; } - return !isPossiblyProtected(x, y, z); + if (isPossiblyProtected(x, y, z)) { + return COST_INF; + } + return 1; + } + + public double placeBucketCost() { + return placeBlockCost; // shrug } public boolean isPossiblyProtected(int x, int y, int z) { diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index ce78c231..ca4abe9e 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -17,24 +17,25 @@ package baritone.pathing.movement; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.*; import baritone.api.utils.input.Input; +import baritone.behavior.PathingBehavior; import baritone.utils.BlockStateInterface; import net.minecraft.block.BlockLiquid; +import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; public abstract class Movement implements IMovement, MovementHelper { - protected static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; + public static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; protected final IBaritone baritone; protected final IPlayerContext ctx; @@ -61,6 +62,8 @@ public abstract class Movement implements IMovement, MovementHelper { public List toPlaceCached = null; public List toWalkIntoCached = null; + private Set validPositionsCached = null; + private Boolean calculatedWhileLoaded; protected Movement(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak, BetterBlockPos toPlace) { @@ -76,29 +79,40 @@ public abstract class Movement implements IMovement, MovementHelper { this(baritone, src, dest, toBreak, null); } - @Override - public double getCost() { + public double getCost() throws NullPointerException { + return cost; + } + + public double getCost(CalculationContext context) { if (cost == null) { - cost = calculateCost(new CalculationContext(baritone)); + cost = calculateCost(context); } return cost; } public abstract double calculateCost(CalculationContext context); - @Override - public double recalculateCost() { + public double recalculateCost(CalculationContext context) { cost = null; - return getCost(); + return getCost(context); } public void override(double cost) { this.cost = cost; } - @Override - public double calculateCostWithoutCaching() { - return calculateCost(new CalculationContext(baritone)); + protected abstract Set calculateValidPositions(); + + public Set getValidPositions() { + if (validPositionsCached == null) { + validPositionsCached = calculateValidPositions(); + Objects.requireNonNull(validPositionsCached); + } + return validPositionsCached; + } + + protected boolean playerInValidPosition() { + return getValidPositions().contains(ctx.playerFeet()) || getValidPositions().contains(((PathingBehavior) baritone.getPathingBehavior()).pathStart()); } /** @@ -115,6 +129,7 @@ public abstract class Movement implements IMovement, MovementHelper { currentState.setInput(Input.JUMP, true); } if (ctx.player().isEntityInsideOpaqueBlock()) { + ctx.getSelectedBlock().ifPresent(pos -> MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, pos))); currentState.setInput(Input.CLICK_LEFT, true); } @@ -123,7 +138,7 @@ public abstract class Movement implements IMovement, MovementHelper { baritone.getLookBehavior().updateTarget( rotation, currentState.getTarget().hasToForceRotations())); - + baritone.getInputOverrideHandler().clearAllKeys(); currentState.getInputStates().forEach((input, forced) -> { baritone.getInputOverrideHandler().setInputForceState(input, forced); }); @@ -143,14 +158,17 @@ public abstract class Movement implements IMovement, MovementHelper { } boolean somethingInTheWay = false; for (BetterBlockPos blockPos : positionsToBreak) { + if (!ctx.world().getEntitiesWithinAABB(EntityFallingBlock.class, new AxisAlignedBB(0, 0, 0, 1, 1.1, 1).offset(blockPos)).isEmpty() && Baritone.settings().pauseMiningForFallingBlocks.value) { + return false; + } if (!MovementHelper.canWalkThrough(ctx, blockPos) && !(BlockStateInterface.getBlock(ctx, blockPos) instanceof BlockLiquid)) { // can't break liquid, so don't try somethingInTheWay = true; + MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); Optional reachable = RotationUtils.reachable(ctx.player(), blockPos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); - MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); - if (Objects.equals(ctx.getSelectedBlock().orElse(null), blockPos) || ctx.playerRotations().isReallyCloseTo(rotTowardsBlock)) { + if (ctx.isLookingAt(blockPos) || ctx.playerRotations().isReallyCloseTo(rotTowardsBlock)) { state.setInput(Input.CLICK_LEFT, true); } return false; @@ -160,7 +178,7 @@ public abstract class Movement implements IMovement, MovementHelper { //i dont care if theres snow in the way!!!!!!! //you dont own me!!!! state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.player().getPositionEyes(1.0F), - VecUtils.getBlockPosCenter(blockPos)), true) + VecUtils.getBlockPosCenter(blockPos), ctx.playerRotations()), true) ); // don't check selectedblock on this one, this is a fallback when we can't see any face directly, it's intended to be breaking the "incorrect" block state.setInput(Input.CLICK_LEFT, true); @@ -273,4 +291,8 @@ public abstract class Movement implements IMovement, MovementHelper { } return toWalkIntoCached; } + + public BlockPos[] toBreakAll() { + return positionsToBreak; + } } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index d07f05e7..b8739d1c 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -18,23 +18,27 @@ 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; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.BlockStateInterface; -import baritone.utils.Helper; import baritone.utils.ToolSet; import net.minecraft.block.*; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.state.IBlockState; -import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.init.Blocks; -import net.minecraft.item.ItemPickaxe; -import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; -import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; + +import java.util.Optional; + +import static baritone.pathing.movement.Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP; /** * Static helpers for cost calculation @@ -48,11 +52,27 @@ public interface MovementHelper extends ActionCosts, Helper { return b == Blocks.ICE // ice becomes water, and water can mess up the path || b instanceof BlockSilverfish // obvious reasons // call context.get directly with x,y,z. no need to make 5 new BlockPos for no reason - || bsi.get0(x, y + 1, z).getBlock() instanceof BlockLiquid//don't break anything touching liquid on any side - || bsi.get0(x + 1, y, z).getBlock() instanceof BlockLiquid - || bsi.get0(x - 1, y, z).getBlock() instanceof BlockLiquid - || bsi.get0(x, y, z + 1).getBlock() instanceof BlockLiquid - || bsi.get0(x, y, z - 1).getBlock() instanceof BlockLiquid; + || avoidAdjacentBreaking(bsi, x, y + 1, z, true) + || avoidAdjacentBreaking(bsi, x + 1, y, z, false) + || avoidAdjacentBreaking(bsi, x - 1, y, z, false) + || avoidAdjacentBreaking(bsi, x, y, z + 1, false) + || avoidAdjacentBreaking(bsi, x, y, z - 1, false); + } + + static boolean avoidAdjacentBreaking(BlockStateInterface bsi, int x, int y, int z, boolean directlyAbove) { + // returns true if you should avoid breaking a block that's adjacent to this one (e.g. lava that will start flowing if you give it a path) + // this is only called for north, south, east, west, and up. this is NOT called for down. + // we assume that it's ALWAYS okay to break the block thats ABOVE liquid + IBlockState state = bsi.get0(x, y, z); + Block block = state.getBlock(); + if (!directlyAbove // it is fine to mine a block that has a falling block directly above, this (the cost of breaking the stacked fallings) is included in cost calculations + // therefore if directlyAbove is true, we will actually ignore if this is falling + && block instanceof BlockFalling // obviously, this check is only valid for falling blocks + && Baritone.settings().avoidUpdatingFallingBlocks.value // and if the setting is enabled + && BlockFalling.canFallThrough(bsi.get0(x, y - 1, z))) { // and if it would fall (i.e. it's unsupported) + return true; // dont break a block that is adjacent to unsupported gravel because it can cause really weird stuff + } + return block instanceof BlockLiquid; } static boolean canWalkThrough(IPlayerContext ctx, BetterBlockPos pos) { @@ -68,7 +88,10 @@ public interface MovementHelper extends ActionCosts, Helper { if (block == Blocks.AIR) { // early return for most common case return true; } - if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA) { + if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof BlockSkull || block instanceof BlockTrapDoor) { + return false; + } + if (Baritone.settings().blocksToAvoid.value.contains(block)) { return false; } if (block instanceof BlockDoor || block instanceof BlockFenceGate) { @@ -77,9 +100,10 @@ public interface MovementHelper extends ActionCosts, Helper { // be opened by just interacting. return block != Blocks.IRON_DOOR; } - boolean snow = block instanceof BlockSnow; - boolean trapdoor = block instanceof BlockTrapDoor; - if (snow || trapdoor) { + if (block == Blocks.CARPET) { + return canWalkOn(bsi, x, y - 1, z); + } + if (block instanceof BlockSnow) { // we've already checked doors and fence gates // so the only remaining dynamic isPassables are snow and trapdoor // if they're cached as a top block, we don't know their metadata @@ -87,25 +111,19 @@ public interface MovementHelper extends ActionCosts, Helper { if (!bsi.worldContainsLoadedChunk(x, z)) { return true; } - if (snow) { - // the check in BlockSnow.isPassable is layers < 5 - // while actually, we want < 3 because 3 or greater makes it impassable in a 2 high ceiling - if (state.getValue(BlockSnow.LAYERS) >= 3) { - return false; - } - // ok, it's low enough we could walk through it, but is it supported? - return canWalkOn(bsi, x, y - 1, z); + // the check in BlockSnow.isPassable is layers < 5 + // while actually, we want < 3 because 3 or greater makes it impassable in a 2 high ceiling + if (state.getValue(BlockSnow.LAYERS) >= 3) { + return false; } - if (trapdoor) { - return !state.getValue(BlockTrapDoor.OPEN); // see BlockTrapDoor.isPassable - } - throw new IllegalStateException(); + // ok, it's low enough we could walk through it, but is it supported? + return canWalkOn(bsi, x, y - 1, z); } - if (isFlowing(state)) { + if (isFlowing(x, y, z, state, bsi)) { return false; // Don't walk through flowing liquids } if (block instanceof BlockLiquid) { - if (Baritone.settings().assumeWalkOnWater.get()) { + if (Baritone.settings().assumeWalkOnWater.value) { return false; } IBlockState up = bsi.get0(x, y + 1, z); @@ -117,7 +135,7 @@ public interface MovementHelper extends ActionCosts, Helper { // every block that overrides isPassable with anything more complicated than a "return true;" or "return false;" // has already been accounted for above // therefore it's safe to not construct a blockpos from our x, y, z ints and instead just pass null - return block.isPassable(null, null); + return block.isPassable(null, BlockPos.ORIGIN); } /** @@ -151,14 +169,15 @@ public interface MovementHelper extends ActionCosts, Helper { || block instanceof BlockSnow || block instanceof BlockLiquid || block instanceof BlockTrapDoor - || block instanceof BlockEndPortal) { + || block instanceof BlockEndPortal + || block instanceof BlockSkull) { return false; } // door, fence gate, liquid, trapdoor have been accounted for, nothing else uses the world or pos parameters return block.isPassable(null, null); } - static boolean isReplacable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { + static boolean isReplaceable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case // all other overrides just return true or false @@ -188,6 +207,11 @@ public interface MovementHelper extends ActionCosts, Helper { return state.getMaterial().isReplaceable(); } + @Deprecated + static boolean isReplacable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { + return isReplaceable(x, y, z, state, bsi); + } + static boolean isDoorPassable(IPlayerContext ctx, BlockPos doorPos, BlockPos playerPos) { if (playerPos.equals(doorPos)) { return false; @@ -236,7 +260,6 @@ public interface MovementHelper extends ActionCosts, Helper { static boolean avoidWalkingInto(Block block) { return block instanceof BlockLiquid - || block instanceof BlockDynamicLiquid || block == Blocks.MAGMA || block == Blocks.CACTUS || block == Blocks.FIRE @@ -266,7 +289,7 @@ public interface MovementHelper extends ActionCosts, Helper { if (state.isBlockNormalCube()) { return true; } - if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.get())) { // TODO reconsider this + if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this return true; } if (block == Blocks.FARMLAND || block == Blocks.GRASS_PATH) { @@ -279,22 +302,25 @@ public interface MovementHelper extends ActionCosts, Helper { // since this is called literally millions of times per second, the benefit of not allocating millions of useless "pos.up()" // BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think its a decrease in readability Block up = bsi.get0(x, y + 1, z).getBlock(); - if (up == Blocks.WATERLILY) { + if (up == Blocks.WATERLILY || up == Blocks.CARPET) { return true; } - if (isFlowing(state) || block == Blocks.FLOWING_WATER) { + if (isFlowing(x, y, z, state, bsi) || block == Blocks.FLOWING_WATER) { // the only scenario in which we can walk on flowing water is if it's under still water with jesus off - return isWater(up) && !Baritone.settings().assumeWalkOnWater.get(); + return isWater(up) && !Baritone.settings().assumeWalkOnWater.value; } // if assumeWalkOnWater is on, we can only walk on water if there isn't water above it // if assumeWalkOnWater is off, we can only walk on water if there is water above it - return isWater(up) ^ Baritone.settings().assumeWalkOnWater.get(); + return isWater(up) ^ Baritone.settings().assumeWalkOnWater.value; + } + if (Baritone.settings().assumeWalkOnLava.value && isLava(block) && !isFlowing(x, y, z, state, bsi)) { + return true; } if (block == Blocks.GLASS || block == Blocks.STAINED_GLASS) { return true; } if (block instanceof BlockSlab) { - if (!Baritone.settings().allowWalkOnBottomSlab.get()) { + if (!Baritone.settings().allowWalkOnBottomSlab.value) { if (((BlockSlab) block).isDouble()) { return true; } @@ -309,6 +335,10 @@ public interface MovementHelper extends ActionCosts, Helper { return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z, state); } + static boolean canWalkOn(IPlayerContext ctx, BlockPos pos) { + return canWalkOn(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ()); + } + static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos) { return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z); } @@ -318,20 +348,22 @@ public interface MovementHelper extends ActionCosts, Helper { } static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z) { - return canPlaceAgainst(bsi.get0(x, y, z)); + return canPlaceAgainst(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canPlaceAgainst(BlockStateInterface bsi, BlockPos pos) { - return canPlaceAgainst(bsi.get0(pos.getX(), pos.getY(), pos.getZ())); + return canPlaceAgainst(bsi, pos.getX(), pos.getY(), pos.getZ()); } static boolean canPlaceAgainst(IPlayerContext ctx, BlockPos pos) { return canPlaceAgainst(new BlockStateInterface(ctx), pos); } - static boolean canPlaceAgainst(IBlockState state) { - // TODO isBlockNormalCube isn't the best check for whether or not we can place a block against it. e.g. glass isn't normalCube but we can place against it - return state.isBlockNormalCube(); + static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + // can we look at the center of a side face of this block and likely be able to place? + // (thats how this check is used) + // therefore dont include weird things that we technically could place against (like carpet) but practically can't + return state.isBlockNormalCube() || state.isFullBlock() || state.getBlock() == Blocks.GLASS || state.getBlock() == Blocks.STAINED_GLASS; } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { @@ -341,23 +373,23 @@ public interface MovementHelper extends ActionCosts, Helper { static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); if (!canWalkThrough(context.bsi, x, y, z, state)) { - if (!context.canBreakAt(x, y, z)) { + if (block instanceof BlockLiquid) { + return COST_INF; + } + double mult = context.breakCostMultiplierAt(x, y, z); + if (mult >= COST_INF) { return COST_INF; } if (avoidBreaking(context.bsi, x, y, z, state)) { return COST_INF; } - if (block instanceof BlockLiquid) { - return COST_INF; - } - double m = Blocks.CRAFTING_TABLE.equals(block) ? 10 : 1; // TODO see if this is still necessary. it's from MineBot when we wanted to penalize breaking its crafting table double strVsBlock = context.toolSet.getStrVsBlock(state); if (strVsBlock <= 0) { return COST_INF; } - - double result = m / strVsBlock; + double result = 1 / strVsBlock; result += context.breakBlockAdditionalCost; + result *= mult; if (includeFalling) { IBlockState above = context.get(x, y + 1, z); if (above.getBlock() instanceof BlockFalling) { @@ -382,7 +414,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); } /** @@ -392,52 +424,15 @@ 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 boolean throwaway(IPlayerContext ctx, boolean select) { - EntityPlayerSP p = ctx.player(); - NonNullList inv = p.inventory.mainInventory; - for (byte i = 0; i < 9; i++) { - ItemStack item = inv.get(i); - // this usage of settings() is okay because it's only called once during pathing - // (while creating the CalculationContext at the very beginning) - // and then it's called during execution - // since this function is never called during cost calculation, we don't need to migrate - // acceptableThrowawayItems to the CalculationContext - if (Baritone.settings().acceptableThrowawayItems.get().contains(item.getItem())) { - if (select) { - p.inventory.currentItem = i; - } - return true; - } - } - if (Baritone.settings().acceptableThrowawayItems.get().contains(p.inventory.offHandInventory.get(0).getItem())) { - // main hand takes precedence over off hand - // that means that if we have block A selected in main hand and block B in off hand, right clicking places block B - // we've already checked above ^ and the main hand can't possible have an acceptablethrowawayitem - // so we need to select in the main hand something that doesn't right click - // so not a shovel, not a hoe, not a block, etc - for (byte i = 0; i < 9; i++) { - ItemStack item = inv.get(i); - if (item.isEmpty() || item.getItem() instanceof ItemPickaxe) { - if (select) { - p.inventory.currentItem = i; - } - return true; - } - } - } - return false; + 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) { - EntityPlayerSP player = ctx.player(); state.setTarget(new MovementTarget( - new Rotation(RotationUtils.calcRotationFromVec3d(player.getPositionEyes(1.0F), + new Rotation(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(pos), - new Rotation(player.rotationYaw, player.rotationPitch)).getYaw(), player.rotationPitch), + ctx.playerRotations()).getYaw(), ctx.player().rotationPitch), false )).setInput(Input.MOVE_FORWARD, true); } @@ -480,9 +475,76 @@ public interface MovementHelper extends ActionCosts, Helper { return BlockStateInterface.getBlock(ctx, p) instanceof BlockLiquid; } - static boolean isFlowing(IBlockState state) { + static boolean possiblyFlowing(IBlockState state) { // Will be IFluidState in 1.13 return state.getBlock() instanceof BlockLiquid && state.getValue(BlockLiquid.LEVEL) != 0; } + + static boolean isFlowing(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { + if (!(state.getBlock() instanceof BlockLiquid)) { + return false; + } + if (state.getValue(BlockLiquid.LEVEL) != 0) { + return true; + } + return possiblyFlowing(bsi.get0(x + 1, y, z)) + || possiblyFlowing(bsi.get0(x - 1, y, z)) + || possiblyFlowing(bsi.get0(x, y, z + 1)) + || possiblyFlowing(bsi.get0(x, y, z - 1)); + } + + + static PlaceResult attemptToPlaceABlock(MovementState state, IBaritone baritone, BlockPos placeAt, boolean preferDown) { + IPlayerContext ctx = baritone.getPlayerContext(); + Optional direct = RotationUtils.reachable(ctx, placeAt); // we assume that if there is a block there, it must be replacable + boolean found = false; + if (direct.isPresent()) { + state.setTarget(new MovementState.MovementTarget(direct.get(), true)); + found = true; + } + for (int i = 0; i < 5; i++) { + BlockPos against1 = placeAt.offset(HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i]); + if (MovementHelper.canPlaceAgainst(ctx, against1)) { + if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(false, placeAt.getX(), placeAt.getY(), placeAt.getZ())) { // get ready to place a throwaway block + Helper.HELPER.logDebug("bb pls get me some blocks. dirt, netherrack, cobble"); + state.setStatus(MovementStatus.UNREACHABLE); + return PlaceResult.NO_OPTION; + } + double faceX = (placeAt.getX() + against1.getX() + 1.0D) * 0.5D; + double faceY = (placeAt.getY() + against1.getY() + 0.5D) * 0.5D; + double faceZ = (placeAt.getZ() + against1.getZ() + 1.0D) * 0.5D; + Rotation place = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations()); + RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance()); + if (res != null && res.typeOfHit == RayTraceResult.Type.BLOCK && res.getBlockPos().equals(against1) && res.getBlockPos().offset(res.sideHit).equals(placeAt)) { + state.setTarget(new MovementState.MovementTarget(place, true)); + found = true; + + if (!preferDown) { + // if preferDown is true, we want the last option + // if preferDown is false, we want the first + break; + } + } + } + } + if (ctx.getSelectedBlock().isPresent()) { + BlockPos selectedBlock = ctx.getSelectedBlock().get(); + EnumFacing side = ctx.objectMouseOver().sideHit; + // only way for selectedBlock.equals(placeAt) to be true is if it's replacable + if (selectedBlock.equals(placeAt) || (MovementHelper.canPlaceAgainst(ctx, selectedBlock) && selectedBlock.offset(side).equals(placeAt))) { + ((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, placeAt.getX(), placeAt.getY(), placeAt.getZ()); + return PlaceResult.READY_TO_PLACE; + } + } + if (found) { + ((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, placeAt.getX(), placeAt.getY(), placeAt.getZ()); + return PlaceResult.ATTEMPTING; + } + return PlaceResult.NO_OPTION; + } + + enum PlaceResult { + READY_TO_PLACE, ATTEMPTING, NO_OPTION; + } } diff --git a/src/main/java/baritone/pathing/movement/Moves.java b/src/main/java/baritone/pathing/movement/Moves.java index c626327e..da278436 100644 --- a/src/main/java/baritone/pathing/movement/Moves.java +++ b/src/main/java/baritone/pathing/movement/Moves.java @@ -276,7 +276,7 @@ public enum Moves { } }, - PARKOUR_NORTH(0, 0, -4, true, false) { + PARKOUR_NORTH(0, 0, -4, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, EnumFacing.NORTH); @@ -288,7 +288,7 @@ public enum Moves { } }, - PARKOUR_SOUTH(0, 0, +4, true, false) { + PARKOUR_SOUTH(0, 0, +4, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, EnumFacing.SOUTH); @@ -300,7 +300,7 @@ public enum Moves { } }, - PARKOUR_EAST(+4, 0, 0, true, false) { + PARKOUR_EAST(+4, 0, 0, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, EnumFacing.EAST); @@ -312,7 +312,7 @@ public enum Moves { } }, - PARKOUR_WEST(-4, 0, 0, true, false) { + PARKOUR_WEST(-4, 0, 0, true, true) { @Override public Movement apply0(CalculationContext context, BetterBlockPos src) { return MovementParkour.cost(context, src, EnumFacing.WEST); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 2e20dd6e..66827a35 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -21,21 +21,19 @@ import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; +import com.google.common.collect.ImmutableSet; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import java.util.Objects; +import java.util.Set; public class MovementAscend extends Movement { @@ -56,29 +54,42 @@ public class MovementAscend extends Movement { return cost(context, src.x, src.y, src.z, dest.x, dest.z); } + @Override + protected Set calculateValidPositions() { + BetterBlockPos prior = new BetterBlockPos(src.subtract(getDirection()).up()); // sometimes we back up to place the block, also sprint ascends, also skip descend to straight ascend + return ImmutableSet.of(src, + src.up(), + dest, + prior, + prior.up() + ); + } + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { IBlockState toPlace = context.get(destX, y, destZ); - boolean hasToPlace = false; + double additionalPlacementCost = 0; if (!MovementHelper.canWalkOn(context.bsi, destX, y, destZ, toPlace)) { - if (!context.canPlaceThrowawayAt(destX, y, destZ)) { + additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ); + 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; for (int i = 0; i < 5; i++) { - int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); - int againstY = y + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); - int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); + int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); + int againstY = y + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); + int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); if (againstX == x && againstZ == z) { // we might be able to backplace now, but it doesn't matter because it will have been broken by the time we'd need to use it continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { - hasToPlace = true; + foundPlaceOption = true; break; } } - if (!hasToPlace) { // didn't find a valid place =( + if (!foundPlaceOption) { // didn't find a valid place =( return COST_INF; } } @@ -129,10 +140,7 @@ public class MovementAscend extends Movement { walk += context.jumpPenalty; } - double totalCost = walk; - if (hasToPlace) { - totalCost += context.placeBlockCost; - } + double totalCost = walk + additionalPlacementCost; // start with srcUp2 since we already have its state // includeFalling isn't needed because of the falling check above -- if srcUp3 is falling we will have already exited with COST_INF if we'd actually have to break it totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); @@ -149,6 +157,10 @@ public class MovementAscend extends Movement { @Override public MovementState updateState(MovementState state) { + if (ctx.playerFeet().y < src.y) { + // this check should run even when in preparing state (breaking blocks) + return state.setStatus(MovementStatus.UNREACHABLE); + } super.updateState(state); // TODO incorporate some behavior from ActionClimb (specifically how it waited until it was at most 1.2 blocks away before starting to jump // for efficiency in ascending minimal height staircases, which is just repeated MovementAscend, so that it doesn't bonk its head on the ceiling repeatedly) @@ -156,70 +168,50 @@ public class MovementAscend extends Movement { return state; } - if (ctx.playerFeet().equals(dest)) { + if (ctx.playerFeet().equals(dest) || ctx.playerFeet().equals(dest.add(getDirection().down()))) { return state.setStatus(MovementStatus.SUCCESS); } IBlockState jumpingOnto = BlockStateInterface.get(ctx, positionToPlace); if (!MovementHelper.canWalkOn(ctx, positionToPlace, jumpingOnto)) { - for (int i = 0; i < 5; i++) { - BlockPos anAgainst = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); - if (anAgainst.equals(src)) { - continue; - } - if (MovementHelper.canPlaceAgainst(ctx, anAgainst)) { - if (!MovementHelper.throwaway(ctx, true)) {//get ready to place a throwaway block - return state.setStatus(MovementStatus.UNREACHABLE); - } - double faceX = (dest.getX() + anAgainst.getX() + 1.0D) * 0.5D; - double faceY = (dest.getY() + anAgainst.getY()) * 0.5D; - double faceZ = (dest.getZ() + anAgainst.getZ() + 1.0D) * 0.5D; - state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations()), true)); - EnumFacing side = ctx.objectMouseOver().sideHit; - - ctx.getSelectedBlock().ifPresent(selectedBlock -> { - if (Objects.equals(selectedBlock, anAgainst) && selectedBlock.offset(side).equals(positionToPlace)) { - ticksWithoutPlacement++; - state.setInput(Input.SNEAK, true); - if (ctx.player().isSneaking()) { - state.setInput(Input.CLICK_RIGHT, true); - } - if (ticksWithoutPlacement > 10) { - // After 10 ticks without placement, we might be standing in the way, move back - state.setInput(Input.MOVE_BACK, true); - } - } else { - state.setInput(Input.CLICK_LEFT, true); // break whatever replaceable block is in the way - } - //System.out.println("Trying to look at " + anAgainst + ", actually looking at" + selectedBlock); - }); - return state; + ticksWithoutPlacement++; + if (MovementHelper.attemptToPlaceABlock(state, baritone, dest.down(), false) == PlaceResult.READY_TO_PLACE) { + state.setInput(Input.SNEAK, true); + if (ctx.player().isSneaking()) { + state.setInput(Input.CLICK_RIGHT, true); } } - return state.setStatus(MovementStatus.UNREACHABLE); + if (ticksWithoutPlacement > 10) { + // After 10 ticks without placement, we might be standing in the way, move back + state.setInput(Input.MOVE_BACK, true); + } + + return state; } MovementHelper.moveTowards(ctx, state, dest); if (MovementHelper.isBottomSlab(jumpingOnto) && !MovementHelper.isBottomSlab(BlockStateInterface.get(ctx, src.down()))) { return state; // don't jump while walking from a non double slab into a bottom slab } - if (Baritone.settings().assumeStep.get()) { + if (Baritone.settings().assumeStep.value || ctx.playerFeet().equals(src.up())) { + // no need to hit space if we're already jumping return state; } - if (ctx.playerFeet().equals(src.up())) { - return state; // no need to hit space if we're already jumping - } - - if (headBonkClear()) { - return state.setInput(Input.JUMP, true); - } - int xAxis = Math.abs(src.getX() - dest.getX()); // either 0 or 1 int zAxis = Math.abs(src.getZ() - dest.getZ()); // either 0 or 1 double flatDistToNext = xAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().posX) + zAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().posZ); double sideDist = zAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().posX) + xAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().posZ); - // System.out.println(flatDistToNext + " " + sideDist); + + double lateralMotion = xAxis * ctx.player().motionZ + zAxis * ctx.player().motionX; + if (Math.abs(lateralMotion) > 0.1) { + return state; + } + + if (headBonkClear()) { + return state.setInput(Input.JUMP, true); + } + if (flatDistToNext > 1.2 || sideDist > 0.2) { return state; } @@ -230,7 +222,7 @@ public class MovementAscend extends Movement { return state.setInput(Input.JUMP, true); } - private boolean headBonkClear() { + public boolean headBonkClear() { BetterBlockPos startUp = src.up(2); for (int i = 0; i < 4; i++) { BetterBlockPos check = startUp.offset(EnumFacing.byHorizontalIndex(i)); @@ -241,4 +233,10 @@ public class MovementAscend extends Movement { } return true; } + + @Override + public boolean safeToCancel(MovementState state) { + // if we had to place, don't allow pause + return state.getStatus() != MovementStatus.RUNNING || ticksWithoutPlacement == 0; + } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index d2d2c282..67b7cd95 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -29,6 +29,7 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; +import com.google.common.collect.ImmutableSet; import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; @@ -37,6 +38,8 @@ import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import java.util.Set; + public class MovementDescend extends Movement { private int numTicks = 0; @@ -61,6 +64,11 @@ public class MovementDescend extends Movement { return result.cost; } + @Override + protected Set calculateValidPositions() { + return ImmutableSet.of(src, dest.up(), dest); + } + public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { double totalCost = 0; IBlockState destDown = context.get(destX, y - 1, destZ); @@ -122,7 +130,7 @@ public class MovementDescend extends Movement { // and potentially replace the water we're going to fall into return false; } - if (!MovementHelper.canWalkThrough(context.bsi, destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) { + if (!MovementHelper.canWalkThrough(context.bsi, destX, y - 2, destZ, below)) { return false; } double costSoFar = 0; @@ -137,13 +145,14 @@ public class MovementDescend extends Movement { IBlockState ontoBlock = context.get(destX, newY, destZ); int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar; - if (ontoBlock.getBlock() == Blocks.WATER && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) { - // lilypads are canWalkThrough, but we can't end a fall that should be broken by water if it's covered by a lilypad - // however, don't return impossible in the lilypad scenario, because we could still jump right on it (water that's below a lilypad is canWalkOn so it works) + if (MovementHelper.isWater(ontoBlock.getBlock())) { + if (!MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) { + return false; + } if (context.assumeWalkOnWater) { return false; // TODO fix } - if (MovementHelper.isFlowing(ontoBlock)) { + if (MovementHelper.isFlowing(destX, newY, destZ, ontoBlock, context.bsi)) { return false; // TODO flowing check required here? } if (!MovementHelper.canWalkOn(context.bsi, destX, newY - 1, destZ)) { @@ -157,9 +166,6 @@ public class MovementDescend extends Movement { res.cost = tentativeCost;// TODO incorporate water swim up cost? return false; } - if (ontoBlock.getBlock() == Blocks.FLOWING_WATER) { - return false; - } if (unprotectedFallHeight <= 11 && (ontoBlock.getBlock() == Blocks.VINE || ontoBlock.getBlock() == Blocks.LADDER)) { // if fall height is greater than or equal to 11, we don't actually grab on to vines or ladders. the more you know // this effectively "resets" our falling speed @@ -177,13 +183,6 @@ public class MovementDescend extends Movement { if (MovementHelper.isBottomSlab(ontoBlock)) { return false; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect } - if (context.hasWaterBucket && unprotectedFallHeight <= context.maxFallHeightBucket + 1) { - res.x = destX; - res.y = newY + 1;// this is the block we're falling onto, so dest is +1 - res.z = destZ; - res.cost = tentativeCost + context.placeBlockCost; - return true; - } if (unprotectedFallHeight <= context.maxFallHeightNoWater + 1) { // fallHeight = 4 means onto.up() is 3 blocks down, which is the max res.x = destX; @@ -191,6 +190,13 @@ public class MovementDescend extends Movement { res.z = destZ; res.cost = tentativeCost; return false; + } + if (context.hasWaterBucket && unprotectedFallHeight <= context.maxFallHeightBucket + 1) { + res.x = destX; + res.y = newY + 1;// this is the block we're falling onto, so dest is +1 + res.z = destZ; + res.cost = tentativeCost + context.placeBucketCost(); + return true; } else { return false; } @@ -205,7 +211,8 @@ public class MovementDescend extends Movement { } BlockPos playerFeet = ctx.playerFeet(); - if (playerFeet.equals(dest) && (MovementHelper.isLiquid(ctx, dest) || ctx.player().posY - playerFeet.getY() < 0.094)) { // lilypads + BlockPos fakeDest = new BlockPos(dest.getX() * 2 - src.getX(), dest.getY(), dest.getZ() * 2 - src.getZ()); + if ((playerFeet.equals(dest) || playerFeet.equals(fakeDest)) && (MovementHelper.isLiquid(ctx, dest) || ctx.player().posY - dest.getY() < 0.5)) { // lilypads // Wait until we're actually on the ground before saying we're done because sometimes we continue to fall if the next action starts immediately return state.setStatus(MovementStatus.SUCCESS); /* else { @@ -213,8 +220,8 @@ public class MovementDescend extends Movement { }*/ } if (safeMode()) { - double destX = (src.getX() + 0.5) * 0.19 + (dest.getX() + 0.5) * 0.81; - double destZ = (src.getZ() + 0.5) * 0.19 + (dest.getZ() + 0.5) * 0.81; + double destX = (src.getX() + 0.5) * 0.17 + (dest.getX() + 0.5) * 0.83; + double destZ = (src.getZ() + 0.5) * 0.17 + (dest.getZ() + 0.5) * 0.83; EntityPlayerSP player = ctx.player(); state.setTarget(new MovementState.MovementTarget( new Rotation(RotationUtils.calcRotationFromVec3d(player.getPositionEyes(1.0F), @@ -231,13 +238,8 @@ public class MovementDescend extends Movement { double z = ctx.player().posZ - (src.getZ() + 0.5); double fromStart = Math.sqrt(x * x + z * z); if (!playerFeet.equals(dest) || ab > 0.25) { - BlockPos fakeDest = new BlockPos(dest.getX() * 2 - src.getX(), dest.getY(), dest.getZ() * 2 - src.getZ()); - if (numTicks++ < 20) { + if (numTicks++ < 20 && fromStart < 1.25) { MovementHelper.moveTowards(ctx, state, fakeDest); - if (fromStart > 1.25) { - state.setInput(Input.MOVE_FORWARD, false); - state.setInput(Input.MOVE_BACK, true); - } } else { MovementHelper.moveTowards(ctx, state, dest); } @@ -249,6 +251,10 @@ public class MovementDescend extends Movement { // (dest - src) + dest is offset 1 more in the same direction // so it's the block we'd need to worry about running into if we decide to sprint straight through this descend BlockPos into = dest.subtract(src.down()).add(dest); + if (skipToAscend()) { + // if dest extends into can't walk through, but the two above are can walk through, then we can overshoot and glitch in that weird way + return true; + } for (int y = 0; y <= 2; y++) { // we could hit any of the three blocks if (MovementHelper.avoidWalkingInto(BlockStateInterface.getBlock(ctx, into.up(y)))) { return true; @@ -256,4 +262,9 @@ public class MovementDescend extends Movement { } return false; } + + public boolean skipToAscend() { + BlockPos into = dest.subtract(src.down()).add(dest); + return !MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into)) && MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into).up()) && MovementHelper.canWalkThrough(ctx, new BetterBlockPos(into).up(2)); + } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index a47fc263..450144c0 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; @@ -27,6 +28,7 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; +import com.google.common.collect.ImmutableSet; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; @@ -35,6 +37,7 @@ import net.minecraft.util.math.BlockPos; import java.util.ArrayList; import java.util.List; +import java.util.Set; public class MovementDiagonal extends Movement { @@ -63,18 +66,41 @@ public class MovementDiagonal extends Movement { return result.cost; } + @Override + 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) { + 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 @@ -98,8 +124,42 @@ public class MovementDiagonal extends Movement { if (cuttingOver2 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver2)) { return; } + Block startIn = context.getBlock(x, y, z); + boolean water = false; + if (MovementHelper.isWater(startIn) || MovementHelper.isWater(destInto.getBlock())) { + 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.getBlock()) // bad + || MovementHelper.avoidWalkingInto(pb2.getBlock()) // 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) { @@ -114,7 +174,7 @@ public class MovementDiagonal extends Movement { return; } IBlockState pb3 = context.get(destX, y + 1, z); - if (optionA == 0 && ((MovementHelper.avoidWalkingInto(pb2.getBlock()) && pb2.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb3.getBlock()) && pb3.getBlock() != Blocks.WATER))) { + if (optionA == 0 && ((MovementHelper.avoidWalkingInto(pb2.getBlock()) && pb2.getBlock() != Blocks.WATER) || MovementHelper.avoidWalkingInto(pb3.getBlock()))) { // at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction return; } @@ -123,19 +183,10 @@ public class MovementDiagonal extends Movement { // and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible return; } - if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0.getBlock()) && pb0.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb1.getBlock()) && pb1.getBlock() != Blocks.WATER))) { + if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0.getBlock()) && pb0.getBlock() != Blocks.WATER) || MovementHelper.avoidWalkingInto(pb1.getBlock()))) { // and now that option B is fully calculated, see if we can edge around that way return; } - boolean water = false; - Block startIn = context.getBlock(x, y, z); - if (MovementHelper.isWater(startIn) || MovementHelper.isWater(destInto.getBlock())) { - // 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) { @@ -170,16 +221,32 @@ public class MovementDiagonal extends Movement { } if (ctx.playerFeet().equals(dest)) { - state.setStatus(MovementStatus.SUCCESS); - return state; + return state.setStatus(MovementStatus.SUCCESS); + } else if (!playerInValidPosition() && !(MovementHelper.isLiquid(ctx, src) && getValidPositions().contains(ctx.playerFeet().up()))) { + return state.setStatus(MovementStatus.UNREACHABLE); } - if (!MovementHelper.isLiquid(ctx, ctx.playerFeet())) { + 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); } MovementHelper.moveTowards(ctx, state, dest); return state; } + private boolean sprint() { + if (MovementHelper.isLiquid(ctx, ctx.playerFeet()) && !Baritone.settings().sprintInWater.value) { + return false; + } + for (int i = 0; i < 4; i++) { + if (!MovementHelper.canWalkThrough(ctx, positionsToBreak[i])) { + return false; + } + } + return true; + } + @Override protected boolean prepared(MovementState state) { return true; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index 2bec8fcf..da5e3f89 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -24,10 +24,13 @@ import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; +import com.google.common.collect.ImmutableSet; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; +import java.util.Set; + public class MovementDownward extends Movement { private int numTicks = 0; @@ -47,7 +50,15 @@ public class MovementDownward extends Movement { return cost(context, src.x, src.y, src.z); } + @Override + protected Set calculateValidPositions() { + return ImmutableSet.of(src, dest); + } + public static double cost(CalculationContext context, int x, int y, int z) { + if (!context.allowDownward) { + return COST_INF; + } if (!MovementHelper.canWalkOn(context.bsi, x, y - 2, z)) { return COST_INF; } @@ -70,6 +81,8 @@ public class MovementDownward extends Movement { if (ctx.playerFeet().equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); + } else if (!playerInValidPosition()) { + return state.setStatus(MovementStatus.UNREACHABLE); } double diffX = ctx.player().posX - (dest.getX() + 0.5); double diffZ = ctx.player().posZ - (dest.getZ() + 0.5); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index 6442d7d9..b777b0a6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -30,6 +30,7 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.pathing.MutableMoveResult; +import net.minecraft.block.Block; import net.minecraft.block.BlockLadder; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.InventoryPlayer; @@ -38,11 +39,12 @@ import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; public class MovementFall extends Movement { @@ -63,6 +65,16 @@ public class MovementFall extends Movement { return result.cost; } + @Override + protected Set calculateValidPositions() { + Set set = new HashSet<>(); + set.add(src); + for (int y = src.y - dest.y; y >= 0; y--) { + set.add(dest.up(y)); + } + return set; + } + private boolean willPlaceBucket() { CalculationContext context = new CalculationContext(baritone); MutableMoveResult result = new MutableMoveResult(); @@ -77,9 +89,11 @@ public class MovementFall extends Movement { } BlockPos playerFeet = ctx.playerFeet(); - Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest)); + Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()); Rotation targetRotation = null; - if (!MovementHelper.isWater(ctx, dest) && willPlaceBucket() && !playerFeet.equals(dest)) { + Block destBlock = ctx.world().getBlockState(dest).getBlock(); + boolean isWater = destBlock == Blocks.WATER || destBlock == Blocks.FLOWING_WATER; + if (!isWater && willPlaceBucket() && !playerFeet.equals(dest)) { if (!InventoryPlayer.isHotbar(ctx.player().inventory.getSlotFor(STACK_BUCKET_WATER)) || ctx.world().provider.isNether()) { return state.setStatus(MovementStatus.UNREACHABLE); } @@ -89,8 +103,7 @@ public class MovementFall extends Movement { targetRotation = new Rotation(toDest.getYaw(), 90.0F); - RayTraceResult trace = ctx.objectMouseOver(); - if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && (trace.getBlockPos().equals(dest) || trace.getBlockPos().equals(dest.down()))) { + if (ctx.isLookingAt(dest) || ctx.isLookingAt(dest.down())) { state.setInput(Input.CLICK_RIGHT, true); } } @@ -100,8 +113,8 @@ public class MovementFall extends Movement { } else { state.setTarget(new MovementTarget(toDest, false)); } - if (playerFeet.equals(dest) && (ctx.player().posY - playerFeet.getY() < 0.094 || MovementHelper.isWater(ctx, dest))) { // 0.094 because lilypads - if (MovementHelper.isWater(ctx, dest)) { + if (playerFeet.equals(dest) && (ctx.player().posY - playerFeet.getY() < 0.094 || isWater)) { // 0.094 because lilypads + if (isWater) { // only match water, not flowing water (which we cannot pick up with a bucket) if (InventoryPlayer.isHotbar(ctx.player().inventory.getSlotFor(STACK_BUCKET_EMPTY))) { ctx.player().inventory.currentItem = ctx.player().inventory.getSlotFor(STACK_BUCKET_EMPTY); if (ctx.player().motionY >= 0) { @@ -132,13 +145,13 @@ public class MovementFall extends Movement { double dist = Math.abs(avoid.getX() * (destCenter.x - avoid.getX() / 2.0 - ctx.player().posX)) + Math.abs(avoid.getZ() * (destCenter.z - avoid.getZ() / 2.0 - ctx.player().posZ)); if (dist < 0.6) { state.setInput(Input.MOVE_FORWARD, true); - } else { + } else if (!ctx.player().onGround) { state.setInput(Input.SNEAK, false); } } if (targetRotation == null) { Vec3d destCenterOffset = new Vec3d(destCenter.x + 0.125 * avoid.getX(), destCenter.y, destCenter.z + 0.125 * avoid.getZ()); - state.setTarget(new MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), destCenterOffset), false)); + state.setTarget(new MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), destCenterOffset, ctx.playerRotations()), false)); } return state; } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index ab98d3d0..41a183f8 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -20,9 +20,6 @@ package baritone.pathing.movement.movements; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.RayTraceUtils; -import baritone.api.utils.Rotation; -import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; @@ -31,13 +28,14 @@ import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import net.minecraft.block.Block; +import net.minecraft.block.BlockLiquid; import net.minecraft.block.BlockStairs; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; + +import java.util.HashSet; +import java.util.Set; public class MovementParkour extends Movement { @@ -45,18 +43,20 @@ public class MovementParkour extends Movement { private final EnumFacing direction; private final int dist; + private final boolean ascend; - private MovementParkour(IBaritone baritone, BetterBlockPos src, int dist, EnumFacing dir) { - super(baritone, src, src.offset(dir, dist), EMPTY, src.offset(dir, dist).down()); + private MovementParkour(IBaritone baritone, BetterBlockPos src, int dist, EnumFacing dir, boolean ascend) { + super(baritone, src, src.offset(dir, dist).up(ascend ? 1 : 0), EMPTY, src.offset(dir, dist).down(ascend ? 0 : 1)); this.direction = dir; this.dist = dist; + this.ascend = ascend; } public static MovementParkour cost(CalculationContext context, BetterBlockPos src, EnumFacing direction) { MutableMoveResult res = new MutableMoveResult(); cost(context, src.x, src.y, src.z, direction, res); int dist = Math.abs(res.x - src.x) + Math.abs(res.z - src.z); - return new MovementParkour(context.getBaritone(), src, dist, direction); + return new MovementParkour(context.getBaritone(), src, dist, direction, res.y > src.y); } public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) { @@ -91,7 +91,7 @@ public class MovementParkour extends Movement { return; } IBlockState standingOn = context.get(x, y - 1, z); - if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof BlockStairs || MovementHelper.isBottomSlab(standingOn)) { + if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof BlockStairs || MovementHelper.isBottomSlab(standingOn) || standingOn.getBlock() instanceof BlockLiquid) { return; } int maxJump; @@ -105,17 +105,36 @@ public class MovementParkour extends Movement { } } for (int i = 2; i <= maxJump; i++) { - // TODO perhaps dest.up(3) doesn't need to be fullyPassable, just canWalkThrough, possibly? - for (int y2 = 0; y2 < 4; y2++) { - if (!MovementHelper.fullyPassable(context, x + xDiff * i, y + y2, z + zDiff * i)) { - return; - } + int destX = x + xDiff * i; + int destZ = z + zDiff * i; + if (!MovementHelper.fullyPassable(context, destX, y + 1, destZ)) { + return; } - if (MovementHelper.canWalkOn(context.bsi, x + xDiff * i, y - 1, z + zDiff * i)) { - res.x = x + xDiff * i; - res.y = y; - res.z = z + zDiff * i; - res.cost = costFromJumpDistance(i) + context.jumpPenalty; + if (!MovementHelper.fullyPassable(context, destX, y + 2, destZ)) { + return; + } + IBlockState destInto = context.bsi.get0(destX, y, destZ); + if (!MovementHelper.fullyPassable(destInto)) { + if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { + res.x = destX; + res.y = y + 1; + res.z = destZ; + res.cost = i * SPRINT_ONE_BLOCK_COST + context.jumpPenalty; + } + return; + } + IBlockState landingOn = context.bsi.get0(destX, y - 1, destZ); + // farmland needs to be canwalkon otherwise farm can never work at all, but we want to specifically disallow ending a jumy on farmland haha + if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) { + if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { + res.x = destX; + res.y = y; + res.z = destZ; + res.cost = costFromJumpDistance(i) + context.jumpPenalty; + } + return; + } + if (!MovementHelper.fullyPassable(context, destX, y + 3, destZ)) { return; } } @@ -125,19 +144,24 @@ public class MovementParkour extends Movement { if (!context.allowParkourPlace) { return; } + // time 2 pop off with that dank skynet parkour place int destX = x + 4 * xDiff; int destZ = z + 4 * zDiff; - if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { + double placeCost = context.costOfPlacingAt(destX, y - 1, destZ); + if (placeCost >= COST_INF) { 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)) { return; } for (int i = 0; i < 5; i++) { - int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); - int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); - int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); + int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); + int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); + int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); if (againstX == x + xDiff * 3 && againstZ == z + zDiff * 3) { // we can't turn around that fast continue; } @@ -145,12 +169,17 @@ public class MovementParkour extends Movement { res.x = destX; res.y = y; res.z = destZ; - res.cost = costFromJumpDistance(4) + context.placeBlockCost + context.jumpPenalty; + res.cost = costFromJumpDistance(4) + placeCost + context.jumpPenalty; return; } } } + private static boolean checkOvershootSafety(BlockStateInterface bsi, int x, int y, int z) { + // we're going to walk into these two blocks after the landing of the parkour anyway, so make sure they aren't avoidWalkingInto + return !MovementHelper.avoidWalkingInto(bsi.get0(x, y, z).getBlock()) && !MovementHelper.avoidWalkingInto(bsi.get0(x, y + 1, z).getBlock()); + } + private static double costFromJumpDistance(int dist) { switch (dist) { case 2: @@ -169,12 +198,23 @@ public class MovementParkour extends Movement { public double calculateCost(CalculationContext context) { MutableMoveResult res = new MutableMoveResult(); cost(context, src.x, src.y, src.z, direction, res); - if (res.x != dest.x || res.z != dest.z) { + if (res.x != dest.x || res.y != dest.y || res.z != dest.z) { return COST_INF; } return res.cost; } + @Override + protected Set calculateValidPositions() { + Set set = new HashSet<>(); + for (int i = 0; i <= dist; i++) { + for (int y = 0; y < 2; y++) { + set.add(src.offset(direction, i).up(y)); + } + } + return set; + } + @Override public boolean safeToCancel(MovementState state) { // once this movement is instantiated, the state is default to PREPPING @@ -189,11 +229,12 @@ public class MovementParkour extends Movement { if (state.getStatus() != MovementStatus.RUNNING) { return state; } - if (ctx.player().isHandActive()) { - logDebug("Pausing parkour since hand is active"); - return state; + if (ctx.playerFeet().y < src.y) { + // we have fallen + logDebug("sorry"); + return state.setStatus(MovementStatus.UNREACHABLE); } - if (dist >= 4) { + if (dist >= 4 || ascend) { state.setInput(Input.SPRINT, true); } MovementHelper.moveTowards(ctx, state, dest); @@ -208,38 +249,13 @@ public class MovementParkour extends Movement { state.setStatus(MovementStatus.SUCCESS); } } else if (!ctx.playerFeet().equals(src)) { - if (ctx.playerFeet().equals(src.offset(direction)) || ctx.player().posY - ctx.playerFeet().getY() > 0.0001) { - - if (!MovementHelper.canWalkOn(ctx, dest.down()) && !ctx.player().onGround) { - BlockPos positionToPlace = dest.down(); - for (int i = 4; i >= 0; i--) { // go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory - BlockPos against1 = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); - if (against1.up().equals(src.offset(direction, 3))) { // we can't turn around that fast - continue; - } - if (MovementHelper.canPlaceAgainst(ctx, against1)) { - if (!MovementHelper.throwaway(ctx, true)) {//get ready to place a throwaway block - return state.setStatus(MovementStatus.UNREACHABLE); - } - double faceX = (dest.getX() + against1.getX() + 1.0D) * 0.5D; - double faceY = (dest.getY() + against1.getY()) * 0.5D; - double faceZ = (dest.getZ() + against1.getZ() + 1.0D) * 0.5D; - Rotation place = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations()); - RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance()); - if (res != null && res.typeOfHit == RayTraceResult.Type.BLOCK && res.getBlockPos().equals(against1) && res.getBlockPos().offset(res.sideHit).equals(dest.down())) { - state.setTarget(new MovementState.MovementTarget(place, true)); - break; - } - } - } - ctx.getSelectedBlock().ifPresent(selectedBlock -> { - EnumFacing side = ctx.objectMouseOver().sideHit; - if (MovementHelper.canPlaceAgainst(ctx, selectedBlock) && selectedBlock.offset(side).equals(dest.down())) { - state.setInput(Input.CLICK_RIGHT, true); - } - }); + if (ctx.playerFeet().equals(src.offset(direction)) || ctx.player().posY - src.y > 0.0001) { + if (!MovementHelper.canWalkOn(ctx, dest.down()) && !ctx.player().onGround && MovementHelper.attemptToPlaceABlock(state, baritone, dest.down(), true) == PlaceResult.READY_TO_PLACE) { + // go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory + state.setInput(Input.CLICK_RIGHT, true); } - if (dist == 3) { // this is a 2 block gap, dest = src + direction * 3 + // prevent jumping too late by checking for ascend + if (dist == 3 && !ascend) { // this is a 2 block gap, dest = src + direction * 3 double xDiff = (src.x + 0.5) - ctx.player().posX; double zDiff = (src.z + 0.5) - ctx.player().posZ; double distFromStart = Math.max(Math.abs(xDiff), Math.abs(zDiff)); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index b236f7e2..c444422e 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; @@ -29,12 +30,16 @@ import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; +import com.google.common.collect.ImmutableSet; import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import java.util.Objects; +import java.util.Set; + public class MovementPillar extends Movement { public MovementPillar(IBaritone baritone, BetterBlockPos start, BetterBlockPos end) { @@ -46,6 +51,11 @@ public class MovementPillar extends Movement { return cost(context, src.x, src.y, src.z); } + @Override + protected Set calculateValidPositions() { + return ImmutableSet.of(src, dest); + } + public static double cost(CalculationContext context, int x, int y, int z) { Block from = context.get(x, y, z).getBlock(); boolean ladder = from == Blocks.LADDER || from == Blocks.VINE; @@ -73,8 +83,16 @@ public class MovementPillar extends Movement { return LADDER_UP_ONE_COST; // allow ascending pillars of water, but only if we're already in one } } - if (!ladder && !context.canPlaceThrowawayAt(x, y, z)) { // we need to place a block where we started to jump on it - return COST_INF; + double placeCost = 0; + if (!ladder) { + // we need to place a block where we started to jump on it + placeCost = context.costOfPlacingAt(x, y, z); + if (placeCost >= COST_INF) { + return COST_INF; + } + if (fromDown.getBlock() == Blocks.AIR) { + placeCost += 0.1; // slightly (1/200th of a second) penalize pillaring on what's currently air + } } if (from instanceof BlockLiquid || (fromDown.getBlock() instanceof BlockLiquid && context.assumeWalkOnWater)) { // otherwise, if we're standing in water, we cannot pillar @@ -112,7 +130,7 @@ public class MovementPillar extends Movement { if (ladder) { return LADDER_UP_ONE_COST + hardness * 5; } else { - return JUMP_ONE_BLOCK_COST + context.placeBlockCost + context.jumpPenalty + hardness; + return JUMP_ONE_BLOCK_COST + placeCost + context.jumpPenalty + hardness; } } @@ -146,10 +164,14 @@ public class MovementPillar extends Movement { return state; } + if (ctx.playerFeet().y < src.y) { + return state.setStatus(MovementStatus.UNREACHABLE); + } + IBlockState fromDown = BlockStateInterface.get(ctx, src); if (MovementHelper.isWater(fromDown.getBlock()) && MovementHelper.isWater(ctx, dest)) { // stay centered while swimming up a water column - state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest)), false)); + state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()), false)); Vec3d destCenter = VecUtils.getBlockPosCenter(dest); if (Math.abs(ctx.player().posX - destCenter.x) > 0.2 || Math.abs(ctx.player().posZ - destCenter.z) > 0.2) { state.setInput(Input.MOVE_FORWARD, true); @@ -192,7 +214,7 @@ public class MovementPillar extends Movement { return state; } else { // Get ready to place a throwaway block - if (!MovementHelper.throwaway(ctx, true)) { + if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, src.x, src.y, src.z)) { return state.setStatus(MovementStatus.UNREACHABLE); } @@ -220,11 +242,17 @@ public class MovementPillar extends Movement { if (!blockIsThere) { - Block fr = BlockStateInterface.get(ctx, src).getBlock(); - if (!(fr instanceof BlockAir || fr.isReplaceable(ctx.world(), src))) { + IBlockState frState = BlockStateInterface.get(ctx, src); + Block fr = frState.getBlock(); + // TODO: Evaluate usage of getMaterial().isReplaceable() + if (!(fr instanceof BlockAir || frState.getMaterial().isReplaceable())) { + RotationUtils.reachable(ctx.player(), src, ctx.playerController().getBlockReachDistance()) + .map(rot -> new MovementState.MovementTarget(rot, true)) + .ifPresent(state::setTarget); + state.setInput(Input.JUMP, false); // breaking is like 5x slower when you're jumping state.setInput(Input.CLICK_LEFT, true); blockIsThere = false; - } else if (ctx.player().isSneaking()) { // 1 tick after we're able to place + } else if (ctx.player().isSneaking() && (Objects.equals(src.down(), ctx.objectMouseOver().getBlockPos()) || Objects.equals(src, ctx.objectMouseOver().getBlockPos())) && ctx.player().posY > dest.getY() + 0.1) { state.setInput(Input.CLICK_RIGHT, true); } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 4a87a8a6..c1f5ed01 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -30,17 +30,15 @@ import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; -import net.minecraft.block.Block; -import net.minecraft.block.BlockDoor; -import net.minecraft.block.BlockFenceGate; -import net.minecraft.block.BlockSlab; +import com.google.common.collect.ImmutableSet; +import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; -import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; -import java.util.Objects; +import java.util.Optional; +import java.util.Set; public class MovementTraverse extends Movement { @@ -64,6 +62,11 @@ public class MovementTraverse extends Movement { return cost(context, src.x, src.y, src.z, dest.x, dest.z); } + @Override + protected Set calculateValidPositions() { + return ImmutableSet.of(src, dest); + } + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { IBlockState pb0 = context.get(destX, y + 1, destZ); IBlockState pb1 = context.get(destX, y, destZ); @@ -108,13 +111,14 @@ public class MovementTraverse extends Movement { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { return COST_INF; } - if (MovementHelper.isReplacable(destX, y - 1, destZ, destOn, context.bsi)) { + if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) { boolean throughWater = MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock()); if (MovementHelper.isWater(destOn.getBlock()) && throughWater) { // this happens when assume walk on water is true and this is a traverse in water, which isn't allowed return COST_INF; } - if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { + double placeCost = context.costOfPlacingAt(destX, y - 1, destZ); + if (placeCost >= COST_INF) { return COST_INF; } double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); @@ -124,25 +128,25 @@ public class MovementTraverse extends Movement { double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); // only include falling on the upper block to break double WC = throughWater ? context.waterWalkSpeed : WALK_ONE_BLOCK_COST; for (int i = 0; i < 5; i++) { - int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); - int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); - int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); + int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); + int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); + int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); if (againstX == x && againstZ == z) { // this would be a backplace continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { // found a side place option - return WC + context.placeBlockCost + hardness1 + hardness2; + return WC + placeCost + hardness1 + hardness2; } } // now that we've checked all possible directions to side place, we actually need to backplace if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) { - return COST_INF; // can't sneak and backplace against soul sand or half slabs =/ + return COST_INF; // can't sneak and backplace against soul sand or half slabs (regardless of whether it's top half or bottom half) =/ } if (srcDown == Blocks.FLOWING_WATER || srcDown == Blocks.WATER) { return COST_INF; // this is obviously impossible } WC = WC * (SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST);//since we are sneak backplacing, we are sneaking lol - return WC + context.placeBlockCost + hardness1 + hardness2; + return WC + placeCost + hardness1 + hardness2; } return COST_INF; } @@ -151,9 +155,11 @@ public class MovementTraverse extends Movement { @Override public MovementState updateState(MovementState state) { super.updateState(state); + IBlockState pb0 = BlockStateInterface.get(ctx, positionsToBreak[0]); + IBlockState pb1 = BlockStateInterface.get(ctx, positionsToBreak[1]); if (state.getStatus() != MovementStatus.RUNNING) { // if the setting is enabled - if (!Baritone.settings().walkWhileBreaking.get()) { + if (!Baritone.settings().walkWhileBreaking.value) { return state; } // and if we're prepping (aka mining the block in front) @@ -161,10 +167,10 @@ public class MovementTraverse extends Movement { return state; } // and if it's fine to walk into the blocks in front - if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, positionsToBreak[0]).getBlock())) { + if (MovementHelper.avoidWalkingInto(pb0.getBlock())) { return state; } - if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, positionsToBreak[1]).getBlock())) { + if (MovementHelper.avoidWalkingInto(pb1.getBlock())) { return state; } // and we aren't already pressed up against the block @@ -172,14 +178,23 @@ public class MovementTraverse extends Movement { if (dist < 0.83) { return state; } + if (!state.getTarget().getRotation().isPresent()) { + // this can happen rarely when the server lags and doesn't send the falling sand entity until you've already walked through the block and are now mining the next one + return state; + } // combine the yaw to the center of the destination, and the pitch to the specific block we're trying to break // it's safe to do this since the two blocks we break (in a traverse) are right on top of each other and so will have the same yaw - float yawToDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), dest)).getYaw(); + float yawToDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), dest), ctx.playerRotations()).getYaw(); float pitchToBreak = state.getTarget().getRotation().get().getPitch(); + if ((pb0.isFullCube() || pb0.getBlock() instanceof BlockAir && (pb1.isFullCube() || pb1.getBlock() instanceof BlockAir))) { + // in the meantime, before we're right up against the block, we can break efficiently at this angle + pitchToBreak = 26; + } - state.setTarget(new MovementState.MovementTarget(new Rotation(yawToDest, pitchToBreak), true)); - return state.setInput(Input.MOVE_FORWARD, true); + return state.setTarget(new MovementState.MovementTarget(new Rotation(yawToDest, pitchToBreak), true)) + .setInput(Input.MOVE_FORWARD, true) + .setInput(Input.SPRINT, true); } //sneak may have been set to true in the PREPPING state while mining an adjacent block @@ -187,113 +202,111 @@ public class MovementTraverse extends Movement { Block fd = BlockStateInterface.get(ctx, src.down()).getBlock(); boolean ladder = fd == Blocks.LADDER || fd == Blocks.VINE; - IBlockState pb0 = BlockStateInterface.get(ctx, positionsToBreak[0]); - IBlockState pb1 = BlockStateInterface.get(ctx, positionsToBreak[1]); - boolean door = pb0.getBlock() instanceof BlockDoor || pb1.getBlock() instanceof BlockDoor; - if (door) { - boolean isDoorActuallyBlockingUs = false; - if (pb0.getBlock() instanceof BlockDoor && !MovementHelper.isDoorPassable(ctx, src, dest)) { - isDoorActuallyBlockingUs = true; - } else if (pb1.getBlock() instanceof BlockDoor && !MovementHelper.isDoorPassable(ctx, dest, src)) { - isDoorActuallyBlockingUs = true; - } - if (isDoorActuallyBlockingUs && !(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock()))) { - return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), positionsToBreak[0])), true)) + if (pb0.getBlock() instanceof BlockDoor || pb1.getBlock() instanceof BlockDoor) { + + boolean notPassable = pb0.getBlock() instanceof BlockDoor && !MovementHelper.isDoorPassable(ctx, src, dest) || pb1.getBlock() instanceof BlockDoor && !MovementHelper.isDoorPassable(ctx, dest, src); + boolean canOpen = !(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock())); + + if (notPassable && canOpen) { + return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), positionsToBreak[0]), ctx.playerRotations()), true)) .setInput(Input.CLICK_RIGHT, true); } } if (pb0.getBlock() instanceof BlockFenceGate || pb1.getBlock() instanceof BlockFenceGate) { - BlockPos blocked = null; - if (!MovementHelper.isGatePassable(ctx, positionsToBreak[0], src.up())) { - blocked = positionsToBreak[0]; - } else if (!MovementHelper.isGatePassable(ctx, positionsToBreak[1], src)) { - blocked = positionsToBreak[1]; - } - + BlockPos blocked = !MovementHelper.isGatePassable(ctx, positionsToBreak[0], src.up()) ? positionsToBreak[0] + : !MovementHelper.isGatePassable(ctx, positionsToBreak[1], src) ? positionsToBreak[1] + : null; if (blocked != null) { - return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), blocked)), true)) - .setInput(Input.CLICK_RIGHT, true); + Optional rotation = RotationUtils.reachable(ctx, blocked); + if (rotation.isPresent()) { + return state.setTarget(new MovementState.MovementTarget(rotation.get(), true)).setInput(Input.CLICK_RIGHT, true); + } } } boolean isTheBridgeBlockThere = MovementHelper.canWalkOn(ctx, positionToPlace) || ladder; - BlockPos whereAmI = ctx.playerFeet(); - if (whereAmI.getY() != dest.getY() && !ladder) { + BlockPos feet = ctx.playerFeet(); + if (feet.getY() != dest.getY() && !ladder) { logDebug("Wrong Y coordinate"); - if (whereAmI.getY() < dest.getY()) { - state.setInput(Input.JUMP, true); + if (feet.getY() < dest.getY()) { + return state.setInput(Input.JUMP, true); } return state; } if (isTheBridgeBlockThere) { - if (ctx.playerFeet().equals(dest)) { + if (feet.equals(dest)) { return state.setStatus(MovementStatus.SUCCESS); } + if (Baritone.settings().overshootTraverse.value && (feet.equals(dest.add(getDirection())) || feet.equals(dest.add(getDirection()).add(getDirection())))) { + return state.setStatus(MovementStatus.SUCCESS); + } + Block low = BlockStateInterface.get(ctx, src).getBlock(); + Block high = BlockStateInterface.get(ctx, src.up()).getBlock(); + if (ctx.player().posY > src.y + 0.1D && !ctx.player().onGround && (low == Blocks.VINE || low == Blocks.LADDER || high == Blocks.VINE || high == Blocks.LADDER)) { + // hitting W could cause us to climb the ladder instead of going forward + // wait until we're on the ground + return state; + } BlockPos into = dest.subtract(src).add(dest); Block intoBelow = BlockStateInterface.get(ctx, into).getBlock(); Block intoAbove = BlockStateInterface.get(ctx, into.up()).getBlock(); - if (wasTheBridgeBlockAlwaysThere && !MovementHelper.isLiquid(ctx, ctx.playerFeet()) && !MovementHelper.avoidWalkingInto(intoBelow) && !MovementHelper.avoidWalkingInto(intoAbove)) { + if (wasTheBridgeBlockAlwaysThere && (!MovementHelper.isLiquid(ctx, feet) || Baritone.settings().sprintInWater.value) && (!MovementHelper.avoidWalkingInto(intoBelow) || MovementHelper.isWater(intoBelow)) && !MovementHelper.avoidWalkingInto(intoAbove)) { state.setInput(Input.SPRINT, true); } - Block destDown = BlockStateInterface.get(ctx, dest.down()).getBlock(); - if (whereAmI.getY() != dest.getY() && ladder && (destDown == Blocks.VINE || destDown == Blocks.LADDER)) { - new MovementPillar(baritone, dest.down(), dest).updateState(state); // i'm sorry - return state; + + IBlockState destDown = BlockStateInterface.get(ctx, dest.down()); + BlockPos against = positionsToBreak[0]; + if (feet.getY() != dest.getY() && ladder && (destDown.getBlock() == Blocks.VINE || destDown.getBlock() == Blocks.LADDER)) { + against = destDown.getBlock() == Blocks.VINE ? MovementPillar.getAgainst(new CalculationContext(baritone), dest.down()) : dest.offset(destDown.getValue(BlockLadder.FACING).getOpposite()); } - MovementHelper.moveTowards(ctx, state, positionsToBreak[0]); + MovementHelper.moveTowards(ctx, state, against); return state; } else { wasTheBridgeBlockAlwaysThere = false; - for (int i = 0; i < 5; i++) { - BlockPos against1 = dest.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); - if (against1.equals(src)) { - continue; - } - against1 = against1.down(); - if (MovementHelper.canPlaceAgainst(ctx, against1)) { - if (!MovementHelper.throwaway(ctx, true)) { // get ready to place a throwaway block - logDebug("bb pls get me some blocks. dirt or cobble"); - return state.setStatus(MovementStatus.UNREACHABLE); - } - if (!Baritone.settings().assumeSafeWalk.get()) { - state.setInput(Input.SNEAK, true); - } - Block standingOn = BlockStateInterface.get(ctx, ctx.playerFeet().down()).getBlock(); - if (standingOn.equals(Blocks.SOUL_SAND) || standingOn instanceof BlockSlab) { // see issue #118 - double dist = Math.max(Math.abs(dest.getX() + 0.5 - ctx.player().posX), Math.abs(dest.getZ() + 0.5 - ctx.player().posZ)); - if (dist < 0.85) { // 0.5 + 0.3 + epsilon - MovementHelper.moveTowards(ctx, state, dest); - return state.setInput(Input.MOVE_FORWARD, false) - .setInput(Input.MOVE_BACK, true); - } - } - state.setInput(Input.MOVE_BACK, false); - double faceX = (dest.getX() + against1.getX() + 1.0D) * 0.5D; - double faceY = (dest.getY() + against1.getY()) * 0.5D; - double faceZ = (dest.getZ() + against1.getZ() + 1.0D) * 0.5D; - state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations()), true)); - - EnumFacing side = ctx.objectMouseOver().sideHit; - if (Objects.equals(ctx.getSelectedBlock().orElse(null), against1) && (ctx.player().isSneaking() || Baritone.settings().assumeSafeWalk.get()) && ctx.getSelectedBlock().get().offset(side).equals(positionToPlace)) { - return state.setInput(Input.CLICK_RIGHT, true); - } - //System.out.println("Trying to look at " + against1 + ", actually looking at" + RayTraceUtils.getSelectedBlock()); - return state.setInput(Input.CLICK_LEFT, true); + Block standingOn = BlockStateInterface.get(ctx, feet.down()).getBlock(); + if (standingOn.equals(Blocks.SOUL_SAND) || standingOn instanceof BlockSlab) { // see issue #118 + double dist = Math.max(Math.abs(dest.getX() + 0.5 - ctx.player().posX), Math.abs(dest.getZ() + 0.5 - ctx.player().posZ)); + if (dist < 0.85) { // 0.5 + 0.3 + epsilon + MovementHelper.moveTowards(ctx, state, dest); + return state.setInput(Input.MOVE_FORWARD, false) + .setInput(Input.MOVE_BACK, true); } } - if (!Baritone.settings().assumeSafeWalk.get()) { + double dist1 = Math.max(Math.abs(ctx.player().posX - (dest.getX() + 0.5D)), Math.abs(ctx.player().posZ - (dest.getZ() + 0.5D))); + PlaceResult p = MovementHelper.attemptToPlaceABlock(state, baritone, dest.down(), false); + if ((p == PlaceResult.READY_TO_PLACE || dist1 < 0.6) && !Baritone.settings().assumeSafeWalk.value) { state.setInput(Input.SNEAK, true); } - if (whereAmI.equals(dest)) { + switch (p) { + case READY_TO_PLACE: { + if (ctx.player().isSneaking() || Baritone.settings().assumeSafeWalk.value) { + state.setInput(Input.CLICK_RIGHT, true); + } + return state; + } + case ATTEMPTING: { + if (dist1 > 0.83) { + // might need to go forward a bit + float yaw = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest), ctx.playerRotations()).getYaw(); + if (Math.abs(state.getTarget().rotation.getYaw() - yaw) < 0.1) { + // but only if our attempted place is straight ahead + return state.setInput(Input.MOVE_FORWARD, true); + } + } else if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { + // well i guess theres something in the way + return state.setInput(Input.CLICK_LEFT, true); + } + return state; + } + default: + break; + } + if (feet.equals(dest)) { // If we are in the block that we are trying to get to, we are sneaking over air and we need to place a block beneath us against the one we just walked off of // Out.log(from + " " + to + " " + faceX + "," + faceY + "," + faceZ + " " + whereAmI); - if (!MovementHelper.throwaway(ctx, true)) {// get ready to place a throwaway block - logDebug("bb pls get me some blocks. dirt or cobble"); - return state.setStatus(MovementStatus.UNREACHABLE); - } double faceX = (dest.getX() + src.getX() + 1.0D) * 0.5D; double faceY = (dest.getY() + src.getY() - 1.0D) * 0.5D; double faceZ = (dest.getZ() + src.getZ() + 1.0D) * 0.5D; @@ -302,25 +315,26 @@ public class MovementTraverse extends Movement { Rotation backToFace = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations()); float pitch = backToFace.getPitch(); - double dist = Math.max(Math.abs(ctx.player().posX - faceX), Math.abs(ctx.player().posZ - faceZ)); - if (dist < 0.29) { + double dist2 = Math.max(Math.abs(ctx.player().posX - faceX), Math.abs(ctx.player().posZ - faceZ)); + if (dist2 < 0.29) { // see issue #208 float yaw = RotationUtils.calcRotationFromVec3d(VecUtils.getBlockPosCenter(dest), ctx.playerHead(), ctx.playerRotations()).getYaw(); state.setTarget(new MovementState.MovementTarget(new Rotation(yaw, pitch), true)); state.setInput(Input.MOVE_BACK, true); } else { state.setTarget(new MovementState.MovementTarget(backToFace, true)); } - state.setInput(Input.SNEAK, true); - if (Objects.equals(ctx.getSelectedBlock().orElse(null), goalLook)) { + if (ctx.isLookingAt(goalLook)) { return state.setInput(Input.CLICK_RIGHT, true); // wait to right click until we are able to place } // Out.log("Trying to look at " + goalLook + ", actually looking at" + Baritone.whatAreYouLookingAt()); - return state.setInput(Input.CLICK_LEFT, true); - } else { - MovementHelper.moveTowards(ctx, state, positionsToBreak[0]); + if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { + state.setInput(Input.CLICK_LEFT, true); + } return state; - // TODO MovementManager.moveTowardsBlock(to); // move towards not look at because if we are bridging for a couple blocks in a row, it is faster if we dont spin around and walk forwards then spin around and place backwards for every block } + MovementHelper.moveTowards(ctx, state, positionsToBreak[0]); + return state; + // TODO MovementManager.moveTowardsBlock(to); // move towards not look at because if we are bridging for a couple blocks in a row, it is faster if we dont spin around and walk forwards then spin around and place backwards for every block } } diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index ef856f42..97bee424 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -23,9 +23,7 @@ import baritone.api.pathing.movement.ActionCosts; import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.path.IPathExecutor; -import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.IPlayerContext; -import baritone.api.utils.VecUtils; +import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.behavior.PathingBehavior; import baritone.pathing.calc.AbstractNodeCostSearch; @@ -34,19 +32,18 @@ import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.movements.*; import baritone.utils.BlockStateInterface; -import baritone.utils.Helper; import net.minecraft.block.BlockLiquid; -import net.minecraft.init.Blocks; import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; import java.util.*; import static baritone.api.pathing.movement.MovementStatus.*; /** - * Behavior to execute a precomputed path. Does not (yet) deal with path segmentation or stitching - * or cutting (jumping onto the next path if it starts with a backtrack of this path's ending) + * Behavior to execute a precomputed path * * @author leijurv */ @@ -101,41 +98,32 @@ public class PathExecutor implements IPathExecutor, Helper { if (pathPosition >= path.length()) { return true; // stop bugging me, I'm done } - BetterBlockPos whereShouldIBe = path.positions().get(pathPosition); + Movement movement = (Movement) path.movements().get(pathPosition); BetterBlockPos whereAmI = ctx.playerFeet(); - if (!whereShouldIBe.equals(whereAmI)) { - - if (pathPosition == 0 && whereAmI.equals(whereShouldIBe.up()) && Math.abs(ctx.player().motionY) < 0.1 && !(path.movements().get(0) instanceof MovementAscend) && !(path.movements().get(0) instanceof MovementPillar)) { - // avoid the Wrong Y coordinate bug - // TODO add a timer here - new MovementDownward(behavior.baritone, whereAmI, whereShouldIBe).update(); - return false; - } - - if (!Blocks.AIR.equals(BlockStateInterface.getBlock(ctx, whereAmI.down()))) {//do not skip if standing on air, because our position isn't stable to skip - for (int i = 0; i < pathPosition - 1 && i < path.length(); i++) {//this happens for example when you lag out and get teleported back a couple blocks - if (whereAmI.equals(path.positions().get(i))) { - logDebug("Skipping back " + (pathPosition - i) + " steps, to " + i); - int previousPos = pathPosition; - pathPosition = Math.max(i - 1, 0); // previous step might not actually be done - for (int j = pathPosition; j <= previousPos; j++) { - path.movements().get(j).reset(); - } - onChangeInPathPosition(); - return false; + if (!movement.getValidPositions().contains(whereAmI)) { + for (int i = 0; i < pathPosition && i < path.length(); i++) {//this happens for example when you lag out and get teleported back a couple blocks + if (((Movement) path.movements().get(i)).getValidPositions().contains(whereAmI)) { + int previousPos = pathPosition; + pathPosition = i; + for (int j = pathPosition; j <= previousPos; j++) { + path.movements().get(j).reset(); } + onChangeInPathPosition(); + onTick(); + return false; } - for (int i = pathPosition + 3; i < path.length(); i++) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing) - // also don't check pathPosition+2 because reasons - if (whereAmI.equals(path.positions().get(i))) { - if (i - pathPosition > 2) { - logDebug("Skipping forward " + (i - pathPosition) + " steps, to " + i); - } - //System.out.println("Double skip sundae"); - pathPosition = i - 1; - onChangeInPathPosition(); - return false; + } + for (int i = pathPosition + 3; i < path.length() - 1; i++) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing) + // also don't check pathPosition+2 because reasons + if (((Movement) path.movements().get(i)).getValidPositions().contains(whereAmI)) { + if (i - pathPosition > 2) { + logDebug("Skipping forward " + (i - pathPosition) + " steps, to " + i); } + //System.out.println("Double skip sundae"); + pathPosition = i - 1; + onChangeInPathPosition(); + onTick(); + return false; } } } @@ -156,38 +144,6 @@ public class PathExecutor implements IPathExecutor, Helper { cancel(); return false; } - //this commented block is literally cursed. - /*Out.log(actions.get(pathPosition)); - if (pathPosition < actions.size() - 1) {//if there are two ActionBridges in a row and they are at right angles, walk diagonally. This makes it so you walk at 45 degrees along a zigzag path instead of doing inefficient zigging and zagging - if ((actions.get(pathPosition) instanceof ActionBridge) && (actions.get(pathPosition + 1) instanceof ActionBridge)) { - ActionBridge curr = (ActionBridge) actions.get(pathPosition); - ActionBridge next = (ActionBridge) actions.get(pathPosition + 1); - if (curr.dx() != next.dx() || curr.dz() != next.dz()) {//two movement are not parallel, so this is a right angle - if (curr.amIGood() && next.amIGood()) {//nothing in the way - BlockPos cornerToCut1 = new BlockPos(next.to.getX() - next.from.getX() + curr.from.getX(), next.to.getY(), next.to.getZ() - next.from.getZ() + curr.from.getZ()); - BlockPos cornerToCut2 = cornerToCut1.up(); - //Block corner1 = Baritone.get(cornerToCut1).getBlock(); - //Block corner2 = Baritone.get(cornerToCut2).getBlock(); - //Out.gui("Cutting conner " + cornerToCut1 + " " + corner1, Out.Mode.Debug); - if (!Action.avoidWalkingInto(cornerToCut1) && !Action.avoidWalkingInto(cornerToCut2)) { - double x = (next.from.getX() + next.to.getX() + 1.0D) * 0.5D; - double z = (next.from.getZ() + next.to.getZ() + 1.0D) * 0.5D; - MovementManager.clearMovement(); - if (!MovementManager.forward && curr.oneInTen != null && curr.oneInTen) { - MovementManager.clearMovement(); - MovementManager.forward = LookManager.lookAtCoords(x, 0, z, false); - } else { - MovementManager.moveTowardsCoords(x, 0, z); - } - if (MovementManager.forward && !MovementManager.backward) { - thePlayer.setSprinting(true); - } - return false; - } - } - } - } - }*/ //long start = System.nanoTime() / 1000000L; BlockStateInterface bsi = new BlockStateInterface(ctx); for (int i = pathPosition - 10; i < pathPosition + 10; i++) { @@ -195,17 +151,17 @@ public class PathExecutor implements IPathExecutor, Helper { continue; } Movement m = (Movement) path.movements().get(i); - HashSet prevBreak = new HashSet<>(m.toBreak(bsi)); - HashSet prevPlace = new HashSet<>(m.toPlace(bsi)); - HashSet prevWalkInto = new HashSet<>(m.toWalkInto(bsi)); + List prevBreak = m.toBreak(bsi); + List prevPlace = m.toPlace(bsi); + List prevWalkInto = m.toWalkInto(bsi); m.resetBlockCache(); - if (!prevBreak.equals(new HashSet<>(m.toBreak(bsi)))) { + if (!prevBreak.equals(m.toBreak(bsi))) { recalcBP = true; } - if (!prevPlace.equals(new HashSet<>(m.toPlace(bsi)))) { + if (!prevPlace.equals(m.toPlace(bsi))) { recalcBP = true; } - if (!prevWalkInto.equals(new HashSet<>(m.toWalkInto(bsi)))) { + if (!prevWalkInto.equals(m.toWalkInto(bsi))) { recalcBP = true; } } @@ -214,10 +170,10 @@ public class PathExecutor implements IPathExecutor, Helper { HashSet newPlace = new HashSet<>(); HashSet newWalkInto = new HashSet<>(); for (int i = pathPosition; i < path.movements().size(); i++) { - Movement movement = (Movement) path.movements().get(i); - newBreak.addAll(movement.toBreak(bsi)); - newPlace.addAll(movement.toPlace(bsi)); - newWalkInto.addAll(movement.toWalkInto(bsi)); + Movement m = (Movement) path.movements().get(i); + newBreak.addAll(m.toBreak(bsi)); + newPlace.addAll(m.toPlace(bsi)); + newWalkInto.addAll(m.toWalkInto(bsi)); } toBreak = newBreak; toPlace = newPlace; @@ -228,27 +184,36 @@ public class PathExecutor implements IPathExecutor, Helper { if (end - start > 0) { System.out.println("Recalculating break and place took " + (end - start) + "ms"); }*/ - IMovement movement = path.movements().get(pathPosition); + if (pathPosition < path.movements().size() - 1) { + IMovement next = path.movements().get(pathPosition + 1); + if (!behavior.baritone.bsi.worldContainsLoadedChunk(next.getDest().x, next.getDest().z)) { + logDebug("Pausing since destination is at edge of loaded chunks"); + clearKeys(); + return true; + } + } boolean canCancel = movement.safeToCancel(); if (costEstimateIndex == null || costEstimateIndex != pathPosition) { costEstimateIndex = pathPosition; // do this only once, when the movement starts, and deliberately get the cost as cached when this path was calculated, not the cost as it is right now currentMovementOriginalCostEstimate = movement.getCost(); - for (int i = 1; i < Baritone.settings().costVerificationLookahead.get() && pathPosition + i < path.length() - 1; i++) { - if (path.movements().get(pathPosition + i).calculateCostWithoutCaching() >= ActionCosts.COST_INF && canCancel) { + for (int i = 1; i < Baritone.settings().costVerificationLookahead.value && pathPosition + i < path.length() - 1; i++) { + if (((Movement) path.movements().get(pathPosition + i)).calculateCost(behavior.secretInternalGetCalculationContext()) >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and a future movement has become impossible. Cancelling."); cancel(); return true; } } } - double currentCost = movement.recalculateCost(); + double currentCost = movement.recalculateCost(behavior.secretInternalGetCalculationContext()); if (currentCost >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and this movement has become impossible. Cancelling."); cancel(); return true; } - if (!movement.calculatedWhileLoaded() && currentCost - currentMovementOriginalCostEstimate > Baritone.settings().maxCostIncrease.get() && canCancel) { + if (!movement.calculatedWhileLoaded() && currentCost - currentMovementOriginalCostEstimate > Baritone.settings().maxCostIncrease.value && canCancel) { + // don't do this if the movement was calculated while loaded + // that means that this isn't a cache error, it's just part of the path interfering with a later part logDebug("Original cost " + currentMovementOriginalCostEstimate + " current cost " + currentCost + ". Cancelling."); cancel(); return true; @@ -272,8 +237,11 @@ public class PathExecutor implements IPathExecutor, Helper { return true; } else { sprintNextTick = shouldSprintNextTick(); + if (!sprintNextTick) { + ctx.player().setSprinting(false); // letting go of control doesn't make you stop sprinting actually + } ticksOnCurrent++; - if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.get()) { + if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.value) { // only cancel if the total time has exceeded the initial estimate // as you break the blocks required, the remaining cost goes down, to the point where // ticksOnCurrent is greater than recalculateCost + 100 @@ -289,11 +257,13 @@ public class PathExecutor implements IPathExecutor, Helper { private Tuple closestPathPos(IPath path) { double best = -1; BlockPos bestPos = null; - for (BlockPos pos : path.positions()) { - double dist = VecUtils.entityDistanceToCenter(ctx.player(), pos); - if (dist < best || best == -1) { - best = dist; - bestPos = pos; + for (IMovement movement : path.movements()) { + for (BlockPos pos : ((Movement) movement).getValidPositions()) { + double dist = VecUtils.entityDistanceToCenter(ctx.player(), pos); + if (dist < best || best == -1) { + best = dist; + bestPos = pos; + } } } return new Tuple<>(best, bestPos); @@ -374,68 +344,208 @@ public class PathExecutor implements IPathExecutor, Helper { } private boolean shouldSprintNextTick() { - // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint - if (!new CalculationContext(behavior.baritone).canSprint) { - behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); - return false; - } - - // if the movement requested sprinting, then we're done - if (behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT)) { - behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); - return true; - } + boolean requested = behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT); // we'll take it from here, no need for minecraft to see we're holding down control and sprint for us behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); - // however, descend doesn't request sprinting, beceause it doesn't know the context of what movement comes after it + // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint + if (!new CalculationContext(behavior.baritone).canSprint) { + return false; + } IMovement current = path.movements().get(pathPosition); - if (current instanceof MovementDescend && pathPosition < path.length() - 2) { - if (((MovementDescend) current).safeMode()) { + // traverse requests sprinting, so we need to do this check first + if (current instanceof MovementTraverse && pathPosition < path.length() - 3) { + IMovement next = path.movements().get(pathPosition + 1); + if (next instanceof MovementAscend && sprintableAscend(ctx, (MovementTraverse) current, (MovementAscend) next, path.movements().get(pathPosition + 2))) { + if (skipNow(ctx, current)) { + logDebug("Skipping traverse to straight ascend"); + pathPosition++; + onChangeInPathPosition(); + onTick(); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + return true; + } else { + logDebug("Too far to the side to safely sprint ascend"); + } + } + } + + // if the movement requested sprinting, then we're done + if (requested) { + return true; + } + + // however, descend and ascend don't request sprinting, because they don't know the context of what movement comes after it + if (current instanceof MovementDescend) { + + if (((MovementDescend) current).safeMode() && !((MovementDescend) current).skipToAscend()) { logDebug("Sprinting would be unsafe"); return false; } - IMovement next = path.movements().get(pathPosition + 1); - if (next instanceof MovementAscend && current.getDirection().up().equals(next.getDirection().down())) { - // a descend then an ascend in the same direction - pathPosition++; - // okay to skip clearKeys and / or onChangeInPathPosition here since this isn't possible to repeat, since it's asymmetric - logDebug("Skipping descend to straight ascend"); - return true; - } - if (canSprintInto(ctx, current, next)) { - if (ctx.playerFeet().equals(current.getDest())) { + if (pathPosition < path.length() - 2) { + IMovement next = path.movements().get(pathPosition + 1); + if (next instanceof MovementAscend && current.getDirection().up().equals(next.getDirection().down())) { + // a descend then an ascend in the same direction pathPosition++; onChangeInPathPosition(); + onTick(); + // okay to skip clearKeys and / or onChangeInPathPosition here since this isn't possible to repeat, since it's asymmetric + logDebug("Skipping descend to straight ascend"); + return true; } - return true; + if (canSprintFromDescendInto(ctx, current, next)) { + if (ctx.playerFeet().equals(current.getDest())) { + pathPosition++; + onChangeInPathPosition(); + onTick(); + } + return true; + } + //logDebug("Turning off sprinting " + movement + " " + next + " " + movement.getDirection() + " " + next.getDirection().down() + " " + next.getDirection().down().equals(movement.getDirection())); } - //logDebug("Turning off sprinting " + movement + " " + next + " " + movement.getDirection() + " " + next.getDirection().down() + " " + next.getDirection().down().equals(movement.getDirection())); } if (current instanceof MovementAscend && pathPosition != 0) { IMovement prev = path.movements().get(pathPosition - 1); if (prev instanceof MovementDescend && prev.getDirection().up().equals(current.getDirection().down())) { BlockPos center = current.getSrc().up(); - if (ctx.player().posY >= center.getY()) { // playerFeet adds 0.1251 to account for soul sand + // playerFeet adds 0.1251 to account for soul sand + // farmland is 0.9375 + // 0.07 is to account for farmland + if (ctx.player().posY >= center.getY() - 0.07) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); return true; } } + if (pathPosition < path.length() - 2 && prev instanceof MovementTraverse && sprintableAscend(ctx, (MovementTraverse) prev, (MovementAscend) current, path.movements().get(pathPosition + 1))) { + return true; + } + } + if (current instanceof MovementFall) { + Tuple data = overrideFall((MovementFall) current); + if (data != null) { + BetterBlockPos fallDest = new BetterBlockPos(data.getSecond()); + if (!path.positions().contains(fallDest)) { + throw new IllegalStateException(); + } + if (ctx.playerFeet().equals(fallDest)) { + pathPosition = path.positions().indexOf(fallDest); + onChangeInPathPosition(); + onTick(); + return true; + } + clearKeys(); + behavior.baritone.getLookBehavior().updateTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), data.getFirst(), ctx.playerRotations()), false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + return true; + } } return false; } - private static boolean canSprintInto(IPlayerContext ctx, IMovement current, IMovement next) { + private Tuple overrideFall(MovementFall movement) { + Vec3i dir = movement.getDirection(); + if (dir.getY() < -3) { + return null; + } + if (!movement.toBreakCached.isEmpty()) { + return null; // it's breaking + } + Vec3i flatDir = new Vec3i(dir.getX(), 0, dir.getZ()); + int i; + outer: + for (i = pathPosition + 1; i < path.length() - 1 && i < pathPosition + 3; i++) { + IMovement next = path.movements().get(i); + if (!(next instanceof MovementTraverse)) { + break; + } + if (!flatDir.equals(next.getDirection())) { + break; + } + for (int y = next.getDest().y; y <= movement.getSrc().y + 1; y++) { + BlockPos chk = new BlockPos(next.getDest().x, y, next.getDest().z); + if (!MovementHelper.fullyPassable(ctx.world().getBlockState(chk))) { + break outer; + } + } + if (!MovementHelper.canWalkOn(ctx, next.getDest().down())) { + break; + } + } + i--; + if (i == pathPosition) { + return null; // no valid extension exists + } + double len = i - pathPosition - 0.4; + return new Tuple<>( + new Vec3d(flatDir.getX() * len + movement.getDest().x + 0.5, movement.getDest().y, flatDir.getZ() * len + movement.getDest().z + 0.5), + movement.getDest().add(flatDir.getX() * (i - pathPosition), 0, flatDir.getZ() * (i - pathPosition))); + } + + private static boolean skipNow(IPlayerContext ctx, IMovement current) { + double offTarget = Math.abs(current.getDirection().getX() * (current.getSrc().z + 0.5D - ctx.player().posZ)) + Math.abs(current.getDirection().getZ() * (current.getSrc().x + 0.5D - ctx.player().posX)); + if (offTarget > 0.1) { + return false; + } + // we are centered + BlockPos headBonk = current.getSrc().subtract(current.getDirection()).up(2); + if (MovementHelper.fullyPassable(ctx.world().getBlockState(headBonk))) { + return true; + } + // wait 0.3 + double flatDist = Math.abs(current.getDirection().getX() * (headBonk.getX() + 0.5D - ctx.player().posX)) + Math.abs(current.getDirection().getZ() * (headBonk.getZ() + 0.5 - ctx.player().posZ)); + return flatDist > 0.8; + } + + private static boolean sprintableAscend(IPlayerContext ctx, MovementTraverse current, MovementAscend next, IMovement nextnext) { + if (!Baritone.settings().sprintAscends.value) { + return false; + } + if (!current.getDirection().equals(next.getDirection().down())) { + return false; + } + if (nextnext.getDirection().getX() != next.getDirection().getX() || nextnext.getDirection().getZ() != next.getDirection().getZ()) { + return false; + } + if (!MovementHelper.canWalkOn(ctx, current.getDest().down())) { + return false; + } + if (!MovementHelper.canWalkOn(ctx, next.getDest().down())) { + return false; + } + if (!next.toBreakCached.isEmpty()) { + return false; // it's breaking + } + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 3; y++) { + BlockPos chk = current.getSrc().up(y); + if (x == 1) { + chk = chk.add(current.getDirection()); + } + if (!MovementHelper.fullyPassable(ctx.world().getBlockState(chk))) { + return false; + } + } + } + if (MovementHelper.avoidWalkingInto(ctx.world().getBlockState(current.getSrc().up(3)).getBlock())) { + return false; + } + return !MovementHelper.avoidWalkingInto(ctx.world().getBlockState(next.getDest().up(2)).getBlock()); // codacy smh my head + } + + private static boolean canSprintFromDescendInto(IPlayerContext ctx, IMovement current, IMovement next) { if (next instanceof MovementDescend && next.getDirection().equals(current.getDirection())) { return true; } - if (next instanceof MovementTraverse && next.getDirection().down().equals(current.getDirection()) && MovementHelper.canWalkOn(ctx, next.getDest().down())) { + if (!MovementHelper.canWalkOn(ctx, current.getDest().add(current.getDirection()))) { + return false; + } + if (next instanceof MovementTraverse && next.getDirection().down().equals(current.getDirection())) { return true; } - return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.get(); + return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.value; } private void onChangeInPathPosition() { @@ -455,6 +565,7 @@ public class PathExecutor implements IPathExecutor, Helper { failed = true; } + @Override public int getPosition() { return pathPosition; } @@ -473,12 +584,12 @@ public class PathExecutor implements IPathExecutor, Helper { ret.costEstimateIndex = costEstimateIndex; ret.ticksOnCurrent = ticksOnCurrent; return ret; - }).orElse(cutIfTooLong()); + }).orElseGet(this::cutIfTooLong); // dont actually call cutIfTooLong every tick if we won't actually use it, use a method reference } private PathExecutor cutIfTooLong() { - if (pathPosition > Baritone.settings().maxPathHistoryLength.get()) { - int cutoffAmt = Baritone.settings().pathHistoryCutoffAmount.get(); + if (pathPosition > Baritone.settings().maxPathHistoryLength.value) { + int cutoffAmt = Baritone.settings().pathHistoryCutoffAmount.value; CutoffPath newPath = new CutoffPath(path, cutoffAmt, path.length() - 1); if (!newPath.getDest().equals(path.getDest())) { throw new IllegalStateException(); diff --git a/src/main/java/baritone/pathing/path/SplicedPath.java b/src/main/java/baritone/pathing/path/SplicedPath.java index 686a15f0..1ba497d8 100644 --- a/src/main/java/baritone/pathing/path/SplicedPath.java +++ b/src/main/java/baritone/pathing/path/SplicedPath.java @@ -26,6 +26,7 @@ import baritone.utils.pathing.PathBase; import java.util.*; public class SplicedPath extends PathBase { + private final List path; private final List movements; diff --git a/src/main/java/baritone/process/BackfillProcess.java b/src/main/java/baritone/process/BackfillProcess.java new file mode 100644 index 00000000..f025c393 --- /dev/null +++ b/src/main/java/baritone/process/BackfillProcess.java @@ -0,0 +1,143 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.input.Input; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.MovementHelper; +import baritone.pathing.movement.MovementState; +import baritone.pathing.path.PathExecutor; +import baritone.utils.BaritoneProcessHelper; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.EmptyChunk; + +import java.util.*; +import java.util.stream.Collectors; + +public final class BackfillProcess extends BaritoneProcessHelper { + + public HashMap blocksToReplace = new HashMap<>(); + + public BackfillProcess(Baritone baritone) { + super(baritone); + } + + @Override + public boolean isActive() { + if (ctx.player() == null || ctx.world() == null) { + return false; + } + if (!Baritone.settings().backfill.value) { + return false; + } + if (Baritone.settings().allowParkour.value) { + logDirect("Backfill cannot be used with allowParkour true"); + Baritone.settings().backfill.value = false; + return false; + } + amIBreakingABlockHMMMMMMM(); + for (BlockPos pos : new ArrayList<>(blocksToReplace.keySet())) { + if (ctx.world().getChunk(pos) instanceof EmptyChunk) { + blocksToReplace.remove(pos); + } + } + baritone.getInputOverrideHandler().clearAllKeys(); + + return !toFillIn().isEmpty(); + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + if (!isSafeToCancel) { + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + baritone.getInputOverrideHandler().clearAllKeys(); + for (BlockPos toPlace : toFillIn()) { + MovementState fake = new MovementState(); + switch (MovementHelper.attemptToPlaceABlock(fake, baritone, toPlace, false)) { + case NO_OPTION: + continue; + case READY_TO_PLACE: + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + case ATTEMPTING: + // patience + baritone.getLookBehavior().updateTarget(fake.getTarget().getRotation().get(), true); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + default: + throw new IllegalStateException(); + } + } + return new PathingCommand(null, PathingCommandType.DEFER); // cede to other process + } + + private void amIBreakingABlockHMMMMMMM() { + if (!ctx.getSelectedBlock().isPresent()) { + return; + } + blocksToReplace.put(ctx.getSelectedBlock().get(), ctx.world().getBlockState(ctx.getSelectedBlock().get())); + } + + public List toFillIn() { + return blocksToReplace + .keySet() + .stream() + .filter(pos -> ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) + .filter(pos -> ctx.world().mayPlace(Blocks.DIRT, pos, false, EnumFacing.UP, null)) + .filter(pos -> !partOfCurrentMovement(pos)) + .sorted(Comparator.comparingDouble(ctx.player()::getDistanceSq).reversed()) + .collect(Collectors.toList()); + } + + private boolean partOfCurrentMovement(BlockPos pos) { + PathExecutor exec = baritone.getPathingBehavior().getCurrent(); + if (exec == null || exec.finished() || exec.failed()) { + return false; + } + Movement movement = (Movement) exec.getPath().movements().get(exec.getPosition()); + return Arrays.asList(movement.toBreakAll()).contains(pos); + } + + @Override + public void onLostControl() { + if (blocksToReplace != null && !blocksToReplace.isEmpty()) { + blocksToReplace.clear(); + } + } + + @Override + public String displayName0() { + return "Backfill"; + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public double priority() { + return 5; + } +} diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java new file mode 100644 index 00000000..cd15f283 --- /dev/null +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -0,0 +1,857 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.pathing.goals.GoalGetToBlock; +import baritone.api.process.IBuilderProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.*; +import baritone.api.utils.input.Input; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BaritoneProcessHelper; +import baritone.utils.BlockStateInterface; +import baritone.utils.PathingCommandContext; +import baritone.utils.schematic.FillSchematic; +import baritone.utils.schematic.MapArtSchematic; +import baritone.utils.schematic.Schematic; +import baritone.utils.schematic.schematica.SchematicaHelper; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.minecraft.block.BlockAir; +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.Tuple; +import net.minecraft.util.math.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.*; + +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + +public final class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess { + + private HashSet incorrectPositions; + private LongOpenHashSet observedCompleted; // positions that are completed even if they're out of render distance and we can't make sure right now + private String name; + private ISchematic realSchematic; + private ISchematic schematic; + private Vec3i origin; + private int ticks; + private boolean paused; + private int layer; + private List approxPlaceable; + + public BuilderProcess(Baritone baritone) { + super(baritone); + } + + @Override + public void build(String name, ISchematic schematic, Vec3i origin) { + this.name = name; + this.schematic = schematic; + this.realSchematic = null; + 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(); + } + + public void resume() { + paused = false; + } + + public void pause() { + paused = true; + } + + @Override + public boolean isPaused() { + return paused; + } + + @Override + public boolean build(String name, File schematic, Vec3i origin) { + NBTTagCompound tag; + try (FileInputStream fileIn = new FileInputStream(schematic)) { + tag = CompressedStreamTools.readCompressed(fileIn); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + //noinspection ConstantConditions + if (tag == null) { + return false; + } + build(name, parse(tag), origin); + return true; + } + + @Override + public void buildOpenSchematic() { + if (SchematicaHelper.isSchematicaPresent()) { + Optional> schematic = SchematicaHelper.getOpenSchematic(); + if (schematic.isPresent()) { + this.build(schematic.get().getFirst().toString(), schematic.get().getFirst(), schematic.get().getSecond()); + } else { + logDirect("No schematic currently open"); + } + } else { + logDirect("Schematica is not present"); + } + } + + public void clearArea(BlockPos corner1, BlockPos corner2) { + BlockPos origin = new BlockPos(Math.min(corner1.getX(), corner2.getX()), Math.min(corner1.getY(), corner2.getY()), Math.min(corner1.getZ(), corner2.getZ())); + 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 FillSchematic(widthX, heightY, lengthZ, Blocks.AIR.getDefaultState()), origin); + } + + @Override + public List getApproxPlaceable() { + return new ArrayList<>(approxPlaceable); + } + + private static ISchematic parse(NBTTagCompound schematic) { + return Baritone.settings().mapArtMode.value ? new MapArtSchematic(schematic) : new Schematic(schematic); + } + + @Override + public boolean isActive() { + return schematic != null; + } + + 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(), current)) { + return null; + } + IBlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current, this.approxPlaceable); + if (state.getBlock() == Blocks.AIR) { + return null; + } + return state; + } + + private Optional> toBreakNearPlayer(BuilderCalculationContext bcc) { + BetterBlockPos center = ctx.playerFeet(); + BetterBlockPos pathStart = baritone.getPathingBehavior().pathStart(); + for (int dx = -5; dx <= 5; dx++) { + for (int dy = Baritone.settings().breakFromAbove.value ? -1 : 0; dy <= 5; dy++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + 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, bcc.bsi.get0(x, y, z)); + if (desired == null) { + continue; // irrelevant + } + IBlockState curr = bcc.bsi.get0(x, y, z); + if (curr.getBlock() != Blocks.AIR && !(curr.getBlock() instanceof BlockLiquid) && !valid(curr, desired)) { + BetterBlockPos pos = new BetterBlockPos(x, y, z); + Optional rot = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); + if (rot.isPresent()) { + return Optional.of(new Tuple<>(pos, rot.get())); + } + } + } + } + } + return Optional.empty(); + } + + public static class Placement { + + private final int hotbarSelection; + private final BlockPos placeAgainst; + private final EnumFacing side; + private final Rotation rot; + + public Placement(int hotbarSelection, BlockPos placeAgainst, EnumFacing side, Rotation rot) { + this.hotbarSelection = hotbarSelection; + this.placeAgainst = placeAgainst; + this.side = side; + this.rot = rot; + } + } + + 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++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + 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.isReplaceable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { + if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() == Blocks.AIR) { + continue; + } + desirableOnHotbar.add(desired); + Optional opt = possibleToPlace(desired, x, y, z, bcc.bsi); + if (opt.isPresent()) { + return opt; + } + } + } + } + } + return Optional.empty(); + } + + private Optional possibleToPlace(IBlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { + for (EnumFacing against : EnumFacing.values()) { + BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).offset(against); + IBlockState placeAgainstState = bsi.get0(placeAgainstPos); + if (MovementHelper.isReplaceable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { + continue; + } + if (!ctx.world().mayPlace(toPlace.getBlock(), new BetterBlockPos(x, y, z), false, against, null)) { + continue; + } + AxisAlignedBB aabb = placeAgainstState.getBoundingBox(ctx.world(), placeAgainstPos); + for (Vec3d placementMultiplier : aabbSideMultipliers(against)) { + double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); + double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); + double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); + Rotation rot = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(placeX, placeY, placeZ), ctx.playerRotations()); + RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance()); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(placeAgainstPos) && result.sideHit == against.getOpposite()) { + OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); + if (hotbar.isPresent()) { + return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); + } + } + } + } + return Optional.empty(); + } + + private OptionalInt hasAnyItemThatWouldPlace(IBlockState desired, RayTraceResult result, Rotation rot) { + for (int i = 0; i < 9; i++) { + ItemStack stack = ctx.player().inventory.mainInventory.get(i); + if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { + continue; + } + float originalYaw = ctx.player().rotationYaw; + float originalPitch = ctx.player().rotationPitch; + // the state depends on the facing of the player sometimes + ctx.player().rotationYaw = rot.getYaw(); + ctx.player().rotationPitch = rot.getPitch(); + IBlockState wouldBePlaced = ((ItemBlock) stack.getItem()).getBlock().getStateForPlacement( + ctx.world(), + result.getBlockPos().offset(result.sideHit), + result.sideHit, + (float) result.hitVec.x - result.getBlockPos().getX(), // as in PlayerControllerMP + (float) result.hitVec.y - result.getBlockPos().getY(), + (float) result.hitVec.z - result.getBlockPos().getZ(), + stack.getItem().getMetadata(stack.getMetadata()), + ctx.player() + ); + ctx.player().rotationYaw = originalYaw; + ctx.player().rotationPitch = originalPitch; + if (valid(wouldBePlaced, desired)) { + return OptionalInt.of(i); + } + } + return OptionalInt.empty(); + } + + private static Vec3d[] aabbSideMultipliers(EnumFacing side) { + switch (side) { + case UP: + return new Vec3d[]{new Vec3d(0.5, 1, 0.5), new Vec3d(0.1, 1, 0.5), new Vec3d(0.9, 1, 0.5), new Vec3d(0.5, 1, 0.1), new Vec3d(0.5, 1, 0.9)}; + case DOWN: + return new Vec3d[]{new Vec3d(0.5, 0, 0.5), new Vec3d(0.1, 0, 0.5), new Vec3d(0.9, 0, 0.5), new Vec3d(0.5, 0, 0.1), new Vec3d(0.5, 0, 0.9)}; + case NORTH: + case SOUTH: + case EAST: + case WEST: + double x = side.getXOffset() == 0 ? 0.5 : (1 + side.getXOffset()) / 2D; + double z = side.getZOffset() == 0 ? 0.5 : (1 + side.getZOffset()) / 2D; + return new Vec3d[]{new Vec3d(x, 0.25, z), new Vec3d(x, 0.75, z)}; + default: // null + throw new IllegalStateException(); + } + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + approxPlaceable = approxPlaceable(36); + if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { + ticks = 5; + } else { + ticks--; + } + baritone.getInputOverrideHandler().clearAllKeys(); + if (paused) { + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + if (Baritone.settings().buildInLayers.value) { + if (realSchematic == null) { + realSchematic = schematic; + } + ISchematic realSchematic = this.realSchematic; // wrap this properly, dont just have the inner class refer to the builderprocess.this + int minYInclusive; + int maxYInclusive; + // layer = 0 should be nothing + // layer = realSchematic.heightY() should be everything + if (Baritone.settings().layerOrder.value) { // top to bottom + maxYInclusive = realSchematic.heightY() - 1; + minYInclusive = realSchematic.heightY() - layer; + } else { + maxYInclusive = layer - 1; + minYInclusive = 0; + } + schematic = new ISchematic() { + @Override + 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, IBlockState currentState) { + return ISchematic.super.inSchematic(x, y, z, currentState) && y >= minYInclusive && y <= maxYInclusive && realSchematic.inSchematic(x, y, z, currentState); + } + + @Override + public int widthX() { + return realSchematic.widthX(); + } + + @Override + public int heightY() { + return realSchematic.heightY(); + } + + @Override + public int lengthZ() { + return realSchematic.lengthZ(); + } + }; + } + BuilderCalculationContext bcc = new BuilderCalculationContext(); + if (!recalc(bcc)) { + if (Baritone.settings().buildInLayers.value && layer < realSchematic.heightY()) { + logDirect("Starting layer " + layer); + layer++; + return onTick(calcFailed, isSafeToCancel); + } + Vec3i repeat = Baritone.settings().buildRepeat.value; + if (repeat.equals(new Vec3i(0, 0, 0))) { + logDirect("Done building"); + onLostControl(); + return null; + } + // build repeat time + layer = 0; + origin = new BlockPos(origin).add(repeat); + logDirect("Repeating build in vector " + repeat + ", new origin is " + origin); + return onTick(calcFailed, isSafeToCancel); + } + if (Baritone.settings().distanceTrim.value) { + trim(); + } + + Optional> toBreak = toBreakNearPlayer(bcc); + if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround) { + // we'd like to pause to break this block + // only change look direction if it's safe (don't want to fuck up an in progress parkour for example + Rotation rot = toBreak.get().getSecond(); + BetterBlockPos pos = toBreak.get().getFirst(); + baritone.getLookBehavior().updateTarget(rot, true); + MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); + if (ctx.player().isSneaking()) { + // really horrible bug where a block is visible for breaking while sneaking but not otherwise + // so you can't see it, it goes to place something else, sneaks, then the next tick it tries to break + // and is unable since it's unsneaked in the intermediary tick + baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); + } + if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + List desirableOnHotbar = new ArrayList<>(); + Optional toPlace = searchForPlaceables(bcc, desirableOnHotbar); + if (toPlace.isPresent() && isSafeToCancel && ctx.player().onGround && ticks <= 0) { + Rotation rot = toPlace.get().rot; + baritone.getLookBehavior().updateTarget(rot, true); + ctx.player().inventory.currentItem = toPlace.get().hotbarSelection; + baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); + if ((ctx.isLookingAt(toPlace.get().placeAgainst) && ctx.objectMouseOver().sideHit.equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + + 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(approxPlaceable.get(i), desired)) { + usefulSlots.add(i); + continue outer; + } + } + noValidHotbarOption.add(desired); + } + + outer: + for (int i = 9; i < 36; i++) { + for (IBlockState desired : noValidHotbarOption) { + if (valid(approxPlaceable.get(i), desired)) { + baritone.getInventoryBehavior().attemptToPutOnHotbar(i, usefulSlots::contains); + break outer; + } + } + } + } + + Goal goal = assemble(bcc, approxPlaceable.subList(0, 9)); + if (goal == null) { + 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; + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + return new PathingCommandContext(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH, bcc); + } + + private boolean recalc(BuilderCalculationContext bcc) { + if (incorrectPositions == null) { + incorrectPositions = new HashSet<>(); + fullRecalc(bcc); + if (incorrectPositions.isEmpty()) { + return false; + } + } + recalcNearby(bcc); + if (incorrectPositions.isEmpty()) { + fullRecalc(bcc); + } + return !incorrectPositions.isEmpty(); + } + + private void trim() { + HashSet copy = new HashSet<>(incorrectPositions); + copy.removeIf(pos -> pos.distanceSq(ctx.player().posX, ctx.player().posY, ctx.player().posZ) > 200); + if (!copy.isEmpty()) { + incorrectPositions = copy; + } + } + + private void recalcNearby(BuilderCalculationContext bcc) { + BetterBlockPos center = ctx.playerFeet(); + 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, bcc.bsi.get0(x, y, z)); + if (desired != null) { + // we care about this position + BetterBlockPos pos = new BetterBlockPos(x, y, z); + if (valid(bcc.bsi.get0(x, y, z), desired)) { + incorrectPositions.remove(pos); + observedCompleted.add(BetterBlockPos.longHash(pos)); + } else { + incorrectPositions.add(pos); + observedCompleted.remove(BetterBlockPos.longHash(pos)); + } + } + } + } + } + } + + private void fullRecalc(BuilderCalculationContext bcc) { + incorrectPositions = new HashSet<>(); + for (int y = 0; y < schematic.heightY(); y++) { + for (int z = 0; z < schematic.lengthZ(); z++) { + for (int x = 0; x < schematic.widthX(); x++) { + 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, current, this.approxPlaceable))) { + observedCompleted.add(BetterBlockPos.longHash(blockX, blockY, blockZ)); + } else { + incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); + observedCompleted.remove(BetterBlockPos.longHash(blockX, blockY, blockZ)); + if (incorrectPositions.size() > Baritone.settings().incorrectSize.value) { + return; + } + } + continue; + } + // this is not in render distance + if (!observedCompleted.contains(BetterBlockPos.longHash(blockX, blockY, blockZ))) { + // and we've never seen this position be correct + // therefore mark as incorrect + incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); + if (incorrectPositions.size() > Baritone.settings().incorrectSize.value) { + return; + } + } + } + } + } + } + + 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 (approxPlaceable.contains(bcc.getSchematic(pos.x, pos.y, pos.z, state))) { + placeable.add(pos); + } + } else { + if (state.getBlock() instanceof BlockLiquid) { + // if the block itself is JUST a liquid (i.e. not just a waterlogged block), we CANNOT break it + // TODO for 1.13 make sure that this only matches pure water, not waterlogged blocks + if (!MovementHelper.possiblyFlowing(state)) { + // if it's a source block then we want to replace it with a throwaway + sourceLiquids.add(pos); + } + } else { + breakable.add(pos); + } + } + }); + List toBreak = new ArrayList<>(); + breakable.forEach(pos -> toBreak.add(breakGoal(pos, bcc))); + List toPlace = new ArrayList<>(); + placeable.forEach(pos -> { + if (!placeable.contains(pos.down()) && !placeable.contains(pos.down(2))) { + toPlace.add(placementGoal(pos, bcc)); + } + }); + sourceLiquids.forEach(pos -> toPlace.add(new GoalBlock(pos.up()))); + + if (!toPlace.isEmpty()) { + return new JankyGoalComposite(new GoalComposite(toPlace.toArray(new Goal[0])), new GoalComposite(toBreak.toArray(new Goal[0]))); + } + if (toBreak.isEmpty()) { + return null; + } + return new GoalComposite(toBreak.toArray(new Goal[0])); + } + + public static class JankyGoalComposite implements Goal { + + private final Goal primary; + private final Goal fallback; + + public JankyGoalComposite(Goal primary, Goal fallback) { + this.primary = primary; + this.fallback = fallback; + } + + + @Override + public boolean isInGoal(int x, int y, int z) { + return primary.isInGoal(x, y, z) || fallback.isInGoal(x, y, z); + } + + @Override + public double heuristic(int x, int y, int z) { + return primary.heuristic(x, y, z); + } + + @Override + public String toString() { + return "JankyComposite Primary: " + primary + " Fallback: " + fallback; + } + } + + public static class GoalBreak extends GoalGetToBlock { + + public GoalBreak(BlockPos pos) { + super(pos); + } + + @Override + public boolean isInGoal(int x, int y, int z) { + // can't stand right on top of a block, that might not work (what if it's unsupported, can't break then) + if (y > this.y) { + return false; + } + // but any other adjacent works for breaking, including inside or below + return super.isInGoal(x, y, z); + } + } + + private Goal placementGoal(BlockPos pos, BuilderCalculationContext bcc) { + if (ctx.world().getBlockState(pos).getBlock() != Blocks.AIR) { // TODO can this even happen? + return new GoalPlace(pos); + } + boolean allowSameLevel = ctx.world().getBlockState(pos.up()).getBlock() != Blocks.AIR; + IBlockState current = ctx.world().getBlockState(pos); + for (EnumFacing facing : Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP) { + //noinspection ConstantConditions + if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && ctx.world().mayPlace(bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current).getBlock(), pos, false, facing, null)) { + return new GoalAdjacent(pos, pos.offset(facing), allowSameLevel); + } + } + return new GoalPlace(pos); + } + + private Goal breakGoal(BlockPos pos, BuilderCalculationContext bcc) { + if (Baritone.settings().goalBreakFromAbove.value && bcc.bsi.get0(pos.up()).getBlock() instanceof BlockAir && bcc.bsi.get0(pos.up(2)).getBlock() instanceof BlockAir) { // TODO maybe possible without the up(2) check? + return new JankyGoalComposite(new GoalBreak(pos), new GoalGetToBlock(pos.up()) { + @Override + public boolean isInGoal(int x, int y, int z) { + if (y > this.y || (x == this.x && y == this.y && z == this.z)) { + return false; + } + return super.isInGoal(x, y, z); + } + }); + } + return new GoalBreak(pos); + } + + public static class GoalAdjacent extends GoalGetToBlock { + + private boolean allowSameLevel; + private BlockPos no; + + public GoalAdjacent(BlockPos pos, BlockPos no, boolean allowSameLevel) { + super(pos); + this.no = no; + this.allowSameLevel = allowSameLevel; + } + + public boolean isInGoal(int x, int y, int z) { + if (x == this.x && y == this.y && z == this.z) { + return false; + } + if (x == no.getX() && y == no.getY() && z == no.getZ()) { + return false; + } + if (!allowSameLevel && y == this.y - 1) { + return false; + } + if (y < this.y - 1) { + return false; + } + return super.isInGoal(x, y, z); + } + + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); + } + } + + public static class GoalPlace extends GoalBlock { + + public GoalPlace(BlockPos placeAt) { + super(placeAt.up()); + } + + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); + } + } + + @Override + public void onLostControl() { + incorrectPositions = null; + name = null; + schematic = null; + realSchematic = null; + layer = 0; + paused = false; + observedCompleted = null; + } + + @Override + public String displayName0() { + return paused ? "Builder Paused" : "Building " + name; + } + + private List approxPlaceable(int size) { + List result = new ArrayList<>(); + for (int i = 0; i < size; i++) { + ItemStack stack = ctx.player().inventory.mainInventory.get(i); + if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { + result.add(Blocks.AIR.getDefaultState()); + continue; + } + // + result.add(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player())); + // + } + return result; + } + + private boolean valid(IBlockState current, IBlockState desired) { + // TODO more complicated comparison logic I guess + if (current.getBlock() instanceof BlockLiquid && Baritone.settings().okIfWater.value) { + return true; + } + return desired == null || current.equals(desired); + } + + public class BuilderCalculationContext extends CalculationContext { + + private final List placeable; + private final ISchematic schematic; + private final int originX; + private final int originY; + private final int originZ; + + public BuilderCalculationContext() { + super(BuilderProcess.this.baritone, true); // wew lad + this.placeable = approxPlaceable(9); + this.schematic = BuilderProcess.this.schematic; + this.originX = origin.getX(); + this.originY = origin.getY(); + this.originZ = origin.getZ(); + + this.jumpPenalty += 10; + this.backtrackCostFavoringCoefficient = 1; + } + + 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; + } + } + + @Override + public double costOfPlacingAt(int x, int y, int z) { + 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, bsi.get0(x, y, z)); + if (sch != null) { + // TODO this can return true even when allowPlace is off.... is that an issue? + if (sch.getBlock() == Blocks.AIR) { + // we want this to be air, but they're asking if they can place here + // 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 (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 + // get added to unicode when? + } + if (!hasThrowaway) { + return COST_INF; + } + // we want it to be something that we don't have + // even more of a pain to place something wrong + return placeBlockCost * 3; + } else { + if (hasThrowaway) { + return placeBlockCost; + } else { + return COST_INF; + } + } + } + + @Override + public double breakCostMultiplierAt(int x, int y, int z) { + if (!allowBreak || isPossiblyProtected(x, y, z)) { + return COST_INF; + } + IBlockState sch = getSchematic(x, y, z, bsi.get0(x, y, z)); + if (sch != null) { + if (sch.getBlock() == Blocks.AIR) { + // it should be air + // regardless of current contents, we can break it + return 1; + } + // it should be a real block + // is it already that block? + if (valid(bsi.get0(x, y, z), sch)) { + return Baritone.settings().breakCorrectBlockPenaltyMultiplier.value; + } else { + // can break if it's wrong + // would be great to return less than 1 here, but that would actually make the cost calculation messed up + // since we're breaking a block, if we underestimate the cost, then it'll fail when it really takes the correct amount of time + return 1; + + } + // TODO do blocks in render distace only? + // TODO allow breaking blocks that we have a tool to harvest and immediately place back? + } else { + return 1; // why not lol + } + } + } +} diff --git a/src/main/java/baritone/process/CustomGoalProcess.java b/src/main/java/baritone/process/CustomGoalProcess.java index 1c4e0311..b45c7d6f 100644 --- a/src/main/java/baritone/process/CustomGoalProcess.java +++ b/src/main/java/baritone/process/CustomGoalProcess.java @@ -24,14 +24,12 @@ import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.utils.BaritoneProcessHelper; -import java.util.Objects; - /** * As set by ExampleBaritoneControl or something idk * * @author leijurv */ -public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomGoalProcess { +public final class CustomGoalProcess extends BaritoneProcessHelper implements ICustomGoalProcess { /** * The current goal @@ -46,7 +44,7 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG private State state; public CustomGoalProcess(Baritone baritone) { - super(baritone, 3); + super(baritone); } @Override @@ -55,6 +53,9 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG if (this.state == State.NONE) { this.state = State.GOAL_SET; } + if (this.state == State.EXECUTING) { + this.state = State.PATH_REQUESTED; + } } @Override @@ -76,20 +77,23 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { switch (this.state) { case GOAL_SET: - if (!baritone.getPathingBehavior().isPathing() && Objects.equals(baritone.getPathingBehavior().getGoal() + "", this.goal + "")) { - this.state = State.NONE; - } return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); case PATH_REQUESTED: - PathingCommand ret = new PathingCommand(this.goal, PathingCommandType.SET_GOAL_AND_PATH); + // return FORCE_REVALIDATE_GOAL_AND_PATH just once + PathingCommand ret = new PathingCommand(this.goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); this.state = State.EXECUTING; return ret; case EXECUTING: if (calcFailed) { onLostControl(); + return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); } - if (this.goal == null || this.goal.isInGoal(ctx.playerFeet())) { + if (this.goal == null || (this.goal.isInGoal(ctx.playerFeet()) && this.goal.isInGoal(baritone.getPathingBehavior().pathStart()))) { onLostControl(); // we're there xd + if (Baritone.settings().disconnectOnArrival.value) { + ctx.world().sendQuittingDisconnectingPacket(); + } + return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); } return new PathingCommand(this.goal, PathingCommandType.SET_GOAL_AND_PATH); default: @@ -104,7 +108,7 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG } @Override - public String displayName() { + public String displayName0() { return "Custom Goal " + this.goal; } diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java new file mode 100644 index 00000000..220ae3b1 --- /dev/null +++ b/src/main/java/baritone/process/ExploreProcess.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.process; + +import baritone.Baritone; +import baritone.api.cache.ICachedWorld; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.pathing.goals.GoalYLevel; +import baritone.api.process.IExploreProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.MyChunkPos; +import baritone.cache.CachedWorld; +import baritone.utils.BaritoneProcessHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; + +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public final class ExploreProcess extends BaritoneProcessHelper implements IExploreProcess { + + private BlockPos explorationOrigin; + + private IChunkFilter filter; + + private int distanceCompleted; + + public ExploreProcess(Baritone baritone) { + super(baritone); + } + + @Override + public boolean isActive() { + return explorationOrigin != null; + } + + @Override + public void explore(int centerX, int centerZ) { + explorationOrigin = new BlockPos(centerX, 0, centerZ); + distanceCompleted = 0; + } + + @Override + public void applyJsonFilter(Path path, boolean invert) throws Exception { + filter = new JsonChunkFilter(path, invert); + } + + public IChunkFilter calcFilter() { + IChunkFilter filter; + if (this.filter != null) { + filter = new EitherChunk(this.filter, new BaritoneChunkCache()); + } else { + filter = new BaritoneChunkCache(); + } + return filter; + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + if (calcFailed) { + logDirect("Failed"); + onLostControl(); + return null; + } + IChunkFilter filter = calcFilter(); + if (!Baritone.settings().disableCompletionCheck.value && filter.countRemain() == 0) { + logDirect("Explored all chunks"); + onLostControl(); + return null; + } + Goal[] closestUncached = closestUncachedChunks(explorationOrigin, filter); + if (closestUncached == null) { + logDebug("awaiting region load from disk"); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + return new PathingCommand(new GoalComposite(closestUncached), PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); + } + + private Goal[] closestUncachedChunks(BlockPos center, IChunkFilter filter) { + int chunkX = center.getX() >> 4; + int chunkZ = center.getZ() >> 4; + int count = Math.min(filter.countRemain(), Baritone.settings().exploreChunkSetMinimumSize.value); + List centers = new ArrayList<>(); + int renderDistance = Baritone.settings().worldExploringChunkOffset.value; + for (int dist = distanceCompleted; ; dist++) { + for (int dx = -dist; dx <= dist; dx++) { + int zval = dist - Math.abs(dx); + for (int mult = 0; mult < 2; mult++) { + int dz = (mult * 2 - 1) * zval; // dz can be either -zval or zval + int trueDist = Math.abs(dx) + Math.abs(dz); + if (trueDist != dist) { + throw new IllegalStateException(); + } + switch (filter.isAlreadyExplored(chunkX + dx, chunkZ + dz)) { + case UNKNOWN: + return null; // awaiting load + case NOT_EXPLORED: + break; // note: this breaks the switch not the for + case EXPLORED: + continue; // note: this continues the for + default: + } + int centerX = ((chunkX + dx) << 4) + 8; + int centerZ = ((chunkZ + dz) << 4) + 8; + int offset = renderDistance << 4; + if (dx < 0) { + centerX -= offset; + } else { + centerX += offset; + } + if (dz < 0) { + centerZ -= offset; + } else { + centerZ += offset; + } + centers.add(new BlockPos(centerX, 0, centerZ)); + } + } + if (dist % 10 == 0) { + count = Math.min(filter.countRemain(), Baritone.settings().exploreChunkSetMinimumSize.value); + } + if (centers.size() >= count) { + return centers.stream().map(pos -> createGoal(pos.getX(), pos.getZ())).toArray(Goal[]::new); + } + if (centers.isEmpty()) { + // we have explored everything from 0 to dist inclusive + // next time we should start our check at dist+1 + distanceCompleted = dist + 1; + } + } + } + + private static Goal createGoal(int x, int z) { + if (Baritone.settings().exploreMaintainY.value == -1) { + return new GoalXZ(x, z); + } + // don't use a goalblock because we still want isInGoal to return true if X and Z are correct + // we just want to try and maintain Y on the way there, not necessarily end at that specific Y + return new GoalXZ(x, z) { + @Override + public double heuristic(int x, int y, int z) { + return super.heuristic(x, y, z) + GoalYLevel.calculate(Baritone.settings().exploreMaintainY.value, y); + } + }; + } + + private enum Status { + EXPLORED, NOT_EXPLORED, UNKNOWN; + } + + private interface IChunkFilter { + + Status isAlreadyExplored(int chunkX, int chunkZ); + + int countRemain(); + } + + private class BaritoneChunkCache implements IChunkFilter { + + private final ICachedWorld cache = baritone.getWorldProvider().getCurrentWorld().getCachedWorld(); + + @Override + public Status isAlreadyExplored(int chunkX, int chunkZ) { + int centerX = chunkX << 4; + int centerZ = chunkZ << 4; + if (cache.isCached(centerX, centerZ)) { + return Status.EXPLORED; + } + if (!((CachedWorld) cache).regionLoaded(centerX, centerZ)) { + Baritone.getExecutor().execute(() -> { + ((CachedWorld) cache).tryLoadFromDisk(centerX >> 9, centerZ >> 9); + }); + return Status.UNKNOWN; // we still need to load regions from disk in order to decide properly + } + return Status.NOT_EXPLORED; + } + + @Override + public int countRemain() { + return Integer.MAX_VALUE; + } + } + + 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; + + private JsonChunkFilter(Path path, boolean invert) throws Exception { // ioexception, json exception, etc + this.invert = invert; + Gson gson = new GsonBuilder().create(); + positions = gson.fromJson(new InputStreamReader(Files.newInputStream(path)), MyChunkPos[].class); + logDirect("Loaded " + positions.length + " positions"); + inFilter = new LongOpenHashSet(); + for (MyChunkPos mcp : positions) { + inFilter.add(ChunkPos.asLong(mcp.x, mcp.z)); + } + } + + @Override + public Status isAlreadyExplored(int chunkX, int chunkZ) { + if (inFilter.contains(ChunkPos.asLong(chunkX, chunkZ)) ^ invert) { + // either it's on the list of explored chunks, or it's not on the list of unexplored chunks + // either way, we have it + return Status.EXPLORED; + } else { + // either it's not on the list of explored chunks, or it's on the list of unexplored chunks + // either way, it depends on if baritone has cached it so defer to that + return Status.UNKNOWN; + } + } + + @Override + public int countRemain() { + if (!invert) { + // if invert is false, anything not on the list is uncached + return Integer.MAX_VALUE; + } + // but if invert is true, anything not on the list IS assumed cached + // so we are done if everything on our list is cached! + int countRemain = 0; + BaritoneChunkCache bcc = new BaritoneChunkCache(); + for (MyChunkPos pos : positions) { + if (bcc.isAlreadyExplored(pos.x, pos.z) != Status.EXPLORED) { + // either waiting for it or dont have it at all + countRemain++; + if (countRemain >= Baritone.settings().exploreChunkSetMinimumSize.value) { + return countRemain; + } + } + } + return countRemain; + } + } + + private class EitherChunk implements IChunkFilter { + + private final IChunkFilter a; + private final IChunkFilter b; + + private EitherChunk(IChunkFilter a, IChunkFilter b) { + this.a = a; + this.b = b; + } + + @Override + public Status isAlreadyExplored(int chunkX, int chunkZ) { + if (a.isAlreadyExplored(chunkX, chunkZ) == Status.EXPLORED) { + return Status.EXPLORED; + } + return b.isAlreadyExplored(chunkX, chunkZ); + } + + @Override + public int countRemain() { + return Math.min(a.countRemain(), b.countRemain()); + } + } + + @Override + public void onLostControl() { + explorationOrigin = null; + } + + @Override + public String displayName0() { + return "Exploring around " + explorationOrigin + ", distance completed " + distanceCompleted + ", currently going to " + new GoalComposite(closestUncachedChunks(explorationOrigin, calcFilter())); + } +} diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java new file mode 100644 index 00000000..00273a9f --- /dev/null +++ b/src/main/java/baritone/process/FarmProcess.java @@ -0,0 +1,304 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.process.IFarmProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.RayTraceUtils; +import baritone.api.utils.Rotation; +import baritone.api.utils.RotationUtils; +import baritone.api.utils.input.Input; +import baritone.cache.WorldScanner; +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BaritoneProcessHelper; +import net.minecraft.block.*; +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.init.Items; +import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.Item; +import net.minecraft.item.ItemDye; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public final class FarmProcess extends BaritoneProcessHelper implements IFarmProcess { + + private boolean active; + + private List locations; + private int tickCount; + + private static final List FARMLAND_PLANTABLE = Arrays.asList( + Items.BEETROOT_SEEDS, + Items.MELON_SEEDS, + Items.WHEAT_SEEDS, + Items.PUMPKIN_SEEDS, + Items.POTATO, + Items.CARROT + ); + + private static final List PICKUP_DROPPED = Arrays.asList( + Items.BEETROOT_SEEDS, + Items.BEETROOT, + Items.MELON_SEEDS, + Items.MELON, + Item.getItemFromBlock(Blocks.MELON_BLOCK), + Items.WHEAT_SEEDS, + Items.WHEAT, + Items.PUMPKIN_SEEDS, + Item.getItemFromBlock(Blocks.PUMPKIN), + Items.POTATO, + Items.CARROT, + Items.NETHER_WART, + Items.REEDS, + Item.getItemFromBlock(Blocks.CACTUS) + ); + + public FarmProcess(Baritone baritone) { + super(baritone); + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void farm() { + active = true; + locations = null; + } + + private enum Harvest { + WHEAT((BlockCrops) Blocks.WHEAT), + CARROTS((BlockCrops) Blocks.CARROTS), + POTATOES((BlockCrops) Blocks.POTATOES), + BEETROOT((BlockCrops) Blocks.BEETROOTS), + PUMPKIN(Blocks.PUMPKIN, state -> true), + MELON(Blocks.MELON_BLOCK, state -> true), + NETHERWART(Blocks.NETHER_WART, state -> state.getValue(BlockNetherWart.AGE) >= 3), + SUGARCANE(Blocks.REEDS, null) { + @Override + public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { + 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) { + if (Baritone.settings().replantCrops.value) { + return world.getBlockState(pos.down()).getBlock() instanceof BlockCactus; + } + return true; + } + }; + public final Block block; + public final Predicate readyToHarvest; + + Harvest(BlockCrops blockCrops) { + this(blockCrops, blockCrops::isMaxAge); + // max age is 7 for wheat, carrots, and potatoes, but 3 for beetroot + } + + Harvest(Block block, Predicate readyToHarvest) { + this.block = block; + this.readyToHarvest = readyToHarvest; + } + + public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { + return readyToHarvest.test(state); + } + } + + private boolean readyForHarvest(World world, BlockPos pos, IBlockState state) { + for (Harvest harvest : Harvest.values()) { + if (harvest.block == state.getBlock()) { + return harvest.readyToHarvest(world, pos, state); + } + } + return false; + } + + private boolean isPlantable(ItemStack stack) { + return FARMLAND_PLANTABLE.contains(stack.getItem()); + } + + private boolean isBoneMeal(ItemStack stack) { + return !stack.isEmpty() && stack.getItem() instanceof ItemDye && EnumDyeColor.byDyeDamage(stack.getMetadata()) == EnumDyeColor.WHITE; + } + + private boolean isNetherWart(ItemStack stack) { + return !stack.isEmpty() && stack.getItem().equals(Items.NETHER_WART); + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + ArrayList scan = new ArrayList<>(); + for (Harvest harvest : Harvest.values()) { + scan.add(harvest.block); + } + 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)); + } + if (locations == null) { + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + List toBreak = new ArrayList<>(); + List openFarmland = new ArrayList<>(); + List bonemealable = new ArrayList<>(); + List openSoulsand = new ArrayList<>(); + for (BlockPos pos : locations) { + IBlockState state = ctx.world().getBlockState(pos); + boolean airAbove = ctx.world().getBlockState(pos.up()).getBlock() instanceof BlockAir; + if (state.getBlock() == Blocks.FARMLAND) { + if (airAbove) { + openFarmland.add(pos); + } + continue; + } + if (state.getBlock() == Blocks.SOUL_SAND) { + if (airAbove) { + openSoulsand.add(pos); + } + continue; + } + if (readyForHarvest(ctx.world(), pos, state)) { + toBreak.add(pos); + continue; + } + if (state.getBlock() instanceof IGrowable) { + IGrowable ig = (IGrowable) state.getBlock(); + if (ig.canGrow(ctx.world(), pos, state, true) && ig.canUseBonemeal(ctx.world(), ctx.world().rand, pos, state)) { + bonemealable.add(pos); + } + } + } + + baritone.getInputOverrideHandler().clearAllKeys(); + for (BlockPos pos : toBreak) { + Optional rot = RotationUtils.reachable(ctx, pos); + if (rot.isPresent() && isSafeToCancel) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); + if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + ArrayList both = new ArrayList<>(openFarmland); + both.addAll(openSoulsand); + for (BlockPos pos : both) { + boolean soulsand = openSoulsand.contains(pos); + Optional rot = RotationUtils.reachableOffset(ctx.player(), pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance()); + if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, soulsand ? this::isNetherWart : this::isPlantable)) { + RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance()); + if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == EnumFacing.UP) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + } + for (BlockPos pos : bonemealable) { + Optional rot = RotationUtils.reachable(ctx, pos); + if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + + if (calcFailed) { + logDirect("Farm failed"); + onLostControl(); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + + List goalz = new ArrayList<>(); + for (BlockPos pos : toBreak) { + goalz.add(new BuilderProcess.GoalBreak(pos)); + } + if (baritone.getInventoryBehavior().throwaway(false, this::isPlantable)) { + for (BlockPos pos : openFarmland) { + goalz.add(new GoalBlock(pos.up())); + } + } + if (baritone.getInventoryBehavior().throwaway(false, this::isNetherWart)) { + for (BlockPos pos : openSoulsand) { + goalz.add(new GoalBlock(pos.up())); + } + } + if (baritone.getInventoryBehavior().throwaway(false, this::isBoneMeal)) { + for (BlockPos pos : bonemealable) { + goalz.add(new GoalBlock(pos)); + } + } + for (Entity entity : ctx.world().loadedEntityList) { + if (entity instanceof EntityItem && entity.onGround) { + EntityItem ei = (EntityItem) entity; + if (PICKUP_DROPPED.contains(ei.getItem().getItem())) { + // +0.1 because of farmland's 0.9375 dummy height lol + goalz.add(new GoalBlock(new BlockPos(entity.posX, entity.posY + 0.1, entity.posZ))); + } + } + } + return new PathingCommand(new GoalComposite(goalz.toArray(new Goal[0])), PathingCommandType.SET_GOAL_AND_PATH); + } + + @Override + public void onLostControl() { + active = false; + } + + @Override + public String displayName0() { + return "Farming"; + } +} diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index 3d25c076..af30b25d 100644 --- a/src/main/java/baritone/process/FollowProcess.java +++ b/src/main/java/baritone/process/FollowProcess.java @@ -29,7 +29,6 @@ import baritone.utils.BaritoneProcessHelper; import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; -import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -46,7 +45,7 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo private List cache; public FollowProcess(Baritone baritone) { - super(baritone, 1); + super(baritone); } @Override @@ -58,13 +57,13 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo private Goal towards(Entity following) { BlockPos pos; - if (Baritone.settings().followOffsetDistance.get() == 0) { + if (Baritone.settings().followOffsetDistance.value == 0) { pos = new BlockPos(following); } else { - GoalXZ g = GoalXZ.fromDirection(following.getPositionVector(), Baritone.settings().followOffsetDirection.get(), Baritone.settings().followOffsetDistance.get()); + GoalXZ g = GoalXZ.fromDirection(following.getPositionVector(), Baritone.settings().followOffsetDirection.value, Baritone.settings().followOffsetDistance.value); pos = new BlockPos(g.getX(), following.posY, g.getZ()); } - return new GoalNear(pos, Baritone.settings().followRadius.get()); + return new GoalNear(pos, Baritone.settings().followRadius.value); } @@ -82,7 +81,12 @@ 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)); + cache = Stream.of(ctx.world().loadedEntityList, ctx.world().playerEntities) + .flatMap(List::stream) + .filter(this::followable) + .filter(this.filter) + .distinct() + .collect(Collectors.toList()); } @Override @@ -101,8 +105,8 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo } @Override - public String displayName() { - return "Follow " + cache; + public String displayName0() { + return "Following " + cache; } @Override diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index f3e245da..5c1d7783 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -18,13 +18,11 @@ package baritone.process; import baritone.Baritone; -import baritone.api.pathing.goals.Goal; -import baritone.api.pathing.goals.GoalComposite; -import baritone.api.pathing.goals.GoalGetToBlock; -import baritone.api.pathing.goals.GoalTwoBlocks; +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; @@ -36,25 +34,31 @@ import net.minecraft.inventory.ContainerPlayer; import net.minecraft.util.math.BlockPos; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Optional; -public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { +public final class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { private Block gettingTo; private List knownLocations; + private List blacklist; // locations we failed to calc to + private BlockPos start; private int tickCount = 0; + private int arrivalTickCount = 0; public GetToBlockProcess(Baritone baritone) { - super(baritone, 2); + super(baritone); } @Override public void getToBlock(Block block) { onLostControl(); gettingTo = block; + start = ctx.playerFeet(); + blacklist = new ArrayList<>(); + arrivalTickCount = 0; rescan(new ArrayList<>(), new CalculationContext(baritone)); } @@ -64,62 +68,126 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl } @Override - public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + public synchronized PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (knownLocations == null) { rescan(new ArrayList<>(), new CalculationContext(baritone)); } if (knownLocations.isEmpty()) { + if (Baritone.settings().exploreForBlocks.value && !calcFailed) { + return new PathingCommand(new GoalRunAway(1, start) { + @Override + public boolean isInGoal(int x, int y, int z) { + return false; + } + }, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); + } logDirect("No known locations of " + gettingTo + ", canceling GetToBlock"); if (isSafeToCancel) { onLostControl(); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } + Goal goal = new GoalComposite(knownLocations.stream().map(this::createGoal).toArray(Goal[]::new)); if (calcFailed) { - logDirect("Unable to find any path to " + gettingTo + ", canceling GetToBlock"); - if (isSafeToCancel) { - onLostControl(); + if (Baritone.settings().blacklistClosestOnFailure.value) { + logDirect("Unable to find any path to " + gettingTo + ", blacklisting presumably unreachable closest instances..."); + blacklistClosest(); + return onTick(false, isSafeToCancel); // gamer moment + } else { + logDirect("Unable to find any path to " + gettingTo + ", canceling GetToBlock"); + if (isSafeToCancel) { + onLostControl(); + } + return new PathingCommand(goal, PathingCommandType.CANCEL_AND_SET_GOAL); } - return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } - int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.get(); + int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain List current = new ArrayList<>(knownLocations); CalculationContext context = new CalculationContext(baritone, true); Baritone.getExecutor().execute(() -> rescan(current, context)); } - Goal goal = new GoalComposite(knownLocations.stream().map(this::createGoal).toArray(Goal[]::new)); - if (goal.isInGoal(ctx.playerFeet()) && isSafeToCancel) { + if (goal.isInGoal(ctx.playerFeet()) && goal.isInGoal(baritone.getPathingBehavior().pathStart()) && isSafeToCancel) { // we're there if (rightClickOnArrival(gettingTo)) { if (rightClick()) { onLostControl(); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } } else { onLostControl(); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } } return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); } + // blacklist the closest block and its adjacent blocks + public synchronized boolean blacklistClosest() { + List newBlacklist = new ArrayList<>(); + knownLocations.stream().min(Comparator.comparingDouble(ctx.player()::getDistanceSq)).ifPresent(newBlacklist::add); + outer: + while (true) { + for (BlockPos known : knownLocations) { + for (BlockPos blacklist : newBlacklist) { + if (areAdjacent(known, blacklist)) { // directly adjacent + newBlacklist.add(known); + knownLocations.remove(known); + continue outer; + } + } + } + // i can't do break; (codacy gets mad), and i can't do if(true){break}; (codacy gets mad) + // so i will do this + switch (newBlacklist.size()) { + default: + break outer; + } + } + logDebug("Blacklisting unreachable locations " + newBlacklist); + blacklist.addAll(newBlacklist); + return !newBlacklist.isEmpty(); + } + + // safer than direct double comparison from distanceSq + private boolean areAdjacent(BlockPos posA, BlockPos posB) { + int diffX = Math.abs(posA.getX() - posB.getX()); + int diffY = Math.abs(posA.getY() - posB.getY()); + int diffZ = Math.abs(posA.getZ() - posB.getZ()); + return (diffX + diffY + diffZ) == 1; + } + @Override - public void onLostControl() { + public synchronized void onLostControl() { gettingTo = null; knownLocations = null; + start = null; + blacklist = null; baritone.getInputOverrideHandler().clearAllKeys(); } @Override - public String displayName() { - return "Get To Block " + gettingTo; + public String displayName0() { + if (knownLocations.isEmpty()) { + return "Exploring randomly to find " + gettingTo + ", no known locations"; + } + return "Get To " + gettingTo + ", " + knownLocations.size() + " known locations"; } - private void rescan(List known, CalculationContext context) { - knownLocations = MineProcess.searchWorld(context, Collections.singletonList(gettingTo), 64, known); + private synchronized void rescan(List known, CalculationContext context) { + List positions = MineProcess.searchWorld(context, new BlockOptionalMetaLookup(gettingTo), 64, known, blacklist); + positions.removeIf(blacklist::contains); + knownLocations = positions; } private Goal createGoal(BlockPos pos) { - return walkIntoInsteadOfAdjacent(gettingTo) ? new GoalTwoBlocks(pos) : new GoalGetToBlock(pos); + if (walkIntoInsteadOfAdjacent(gettingTo)) { + return new GoalTwoBlocks(pos); + } + if (blockOnTopMustBeRemoved(gettingTo) && baritone.bsi.get0(pos.up()).isBlockNormalCube()) { + return new GoalBlock(pos.up()); + } + return new GoalGetToBlock(pos); } private boolean rightClick() { @@ -134,6 +202,10 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl return true; } } + if (arrivalTickCount++ > 20) { + logDirect("Right click timed out"); + return true; + } return false; // trying to right click, will do it next tick or so } } @@ -142,16 +214,24 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl } private boolean walkIntoInsteadOfAdjacent(Block block) { - if (!Baritone.settings().enterPortal.get()) { + if (!Baritone.settings().enterPortal.value) { return false; } return block == Blocks.PORTAL; } private boolean rightClickOnArrival(Block block) { - if (!Baritone.settings().rightClickContainerOnArrival.get()) { + if (!Baritone.settings().rightClickContainerOnArrival.value) { return false; } return block == Blocks.CRAFTING_TABLE || block == Blocks.FURNACE || block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST; } + + private boolean blockOnTopMustBeRemoved(Block block) { + if (!rightClickOnArrival(block)) { // only if we plan to actually open it on arrival + return false; + } + // only these chests; you can open a crafting table or furnace even with a block on top + return block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST; + } } diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 0312e35c..4043e627 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -22,20 +22,21 @@ import baritone.api.pathing.goals.*; import baritone.api.process.IMineProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; -import baritone.api.utils.IPlayerContext; -import baritone.api.utils.RotationUtils; +import baritone.api.utils.*; +import baritone.api.utils.input.Input; import baritone.cache.CachedChunk; -import baritone.cache.ChunkPacker; import baritone.cache.WorldScanner; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import net.minecraft.block.Block; +import net.minecraft.block.BlockAir; +import net.minecraft.block.BlockFalling; +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; @@ -43,6 +44,8 @@ import net.minecraft.world.World; import java.util.*; import java.util.stream.Collectors; +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + /** * Mine blocks of a certain type * @@ -52,83 +55,118 @@ 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; private GoalRunAway branchPointRunaway; private int desiredQuantity; private int tickCount; public MineProcess(Baritone baritone) { - super(baritone, 0); + super(baritone); } @Override public boolean isActive() { - return mining != null; + return filter != null; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (desiredQuantity > 0) { - Item item = mining.get(0).getItemDropped(mining.get(0).getDefaultState(), new Random(), 0); - int curr = ctx.player().inventory.mainInventory.stream().filter(stack -> item.equals(stack.getItem())).mapToInt(ItemStack::getCount).sum(); - System.out.println("Currently have " + curr + " " + item); + int curr = ctx.player().inventory.mainInventory.stream() + .filter(stack -> filter.has(stack)) + .mapToInt(ItemStack::getCount).sum(); + System.out.println("Currently have " + curr + " valid items"); if (curr >= desiredQuantity) { - logDirect("Have " + curr + " " + item.getItemStackDisplayName(new ItemStack(item, 1))); + logDirect("Have " + curr + " valid items"); cancel(); return null; } } if (calcFailed) { - logDirect("Unable to find any path to " + mining + ", canceling Mine"); + if (!knownOreLocations.isEmpty() && Baritone.settings().blacklistClosestOnFailure.value) { + 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 " + filter + ", canceling mine"); + cancel(); + return null; + } + } + if (!Baritone.settings().allowBreak.value) { + logDirect("Unable to mine when allowBreak is false!"); cancel(); return null; } - int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.get(); + int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; + List curr = new ArrayList<>(knownOreLocations); if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain - List curr = new ArrayList<>(knownOreLocations); CalculationContext context = new CalculationContext(baritone, true); Baritone.getExecutor().execute(() -> rescan(curr, context)); } - if (Baritone.settings().legitMine.get()) { + if (Baritone.settings().legitMine.value) { addNearby(); } - Goal goal = updateGoal(); - if (goal == null) { + Optional shaft = curr.stream() + .filter(pos -> pos.getX() == ctx.playerFeet().getX() && pos.getZ() == ctx.playerFeet().getZ()) + .filter(pos -> pos.getY() >= ctx.playerFeet().getY()) + .filter(pos -> !(BlockStateInterface.get(ctx, pos).getBlock() instanceof BlockAir)) // after breaking a block, it takes mineGoalUpdateInterval ticks for it to actually update this list =( + .min(Comparator.comparingDouble(ctx.player()::getDistanceSq)); + baritone.getInputOverrideHandler().clearAllKeys(); + if (shaft.isPresent() && ctx.player().onGround) { + BlockPos pos = shaft.get(); + IBlockState state = baritone.bsi.get0(pos); + if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { + Optional rot = RotationUtils.reachable(ctx, pos); + if (rot.isPresent() && isSafeToCancel) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); + if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot.get())) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + } + PathingCommand command = updateGoal(); + if (command == null) { // none in range // maybe say something in chat? (ahem impact) cancel(); return null; } - return new PathingCommand(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); + return command; } @Override public void onLostControl() { - mine(0, (Block[]) null); + mine(0, (BlockOptionalMetaLookup) null); } @Override - public String displayName() { - return "Mine " + mining; + public String displayName0() { + return "Mine " + filter; } - private Goal updateGoal() { + 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); + 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(ctx, loc, locs2)).toArray(Goal[]::new)); + Goal goal = new GoalComposite(locs2.stream().map(loc -> coalesce(loc, locs2)).toArray(Goal[]::new)); knownOreLocations = locs2; - return goal; + return new PathingCommand(goal, legit ? PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH : PathingCommandType.REVALIDATE_GOAL_AND_PATH); } // we don't know any ore locations at the moment - if (!Baritone.settings().legitMine.get()) { + if (!legit) { return null; } // only in non-Xray mode (aka legit mode) do we do this - int y = Baritone.settings().legitMineYLevel.get(); + int y = Baritone.settings().legitMineYLevel.value; if (branchPoint == null) { /*if (!baritone.getPathingBehavior().isPathing() && playerFeet().y == y) { // cool, path is over and we are at desired y @@ -149,53 +187,109 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } }; } - return branchPointRunaway; + return new PathingCommand(branchPointRunaway, PathingCommandType.REVALIDATE_GOAL_AND_PATH); } private void rescan(List already, CalculationContext context) { - if (mining == null) { + if (filter == null) { return; } - if (Baritone.settings().legitMine.get()) { + if (Baritone.settings().legitMine.value) { return; } - List locs = searchWorld(context, mining, ORE_LOCATIONS_COUNT, already); - 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()) { - logDebug("No locations for " + mining + " known, cancelling"); + logDirect("No locations for " + filter + " known, cancelling"); cancel(); return; } knownOreLocations = locs; } - private static Goal coalesce(IPlayerContext ctx, BlockPos loc, List locs) { - if (!Baritone.settings().forceInternalMining.get()) { - return new GoalTwoBlocks(loc); - } - + private boolean internalMiningGoal(BlockPos pos, IPlayerContext ctx, List locs) { // Here, BlockStateInterface is used because the position may be in a cached chunk (the targeted block is one that is kept track of) - boolean upwardGoal = locs.contains(loc.up()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(ctx, loc.up()) == Blocks.AIR); - boolean downwardGoal = locs.contains(loc.down()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(ctx, loc.down()) == Blocks.AIR); - return upwardGoal == downwardGoal ? new GoalTwoBlocks(loc) : upwardGoal ? new GoalBlock(loc) : new GoalBlock(loc.down()); + if (locs.contains(pos)) { + return true; + } + IBlockState state = BlockStateInterface.get(ctx, pos); + if (Baritone.settings().internalMiningAirException.value && state.getBlock() instanceof BlockAir) { + return true; + } + return filter.has(state); } - public static List droppedItemsScan(List mining, World world) { - if (!Baritone.settings().mineScanDroppedItems.get()) { - return new ArrayList<>(); + private Goal coalesce(BlockPos loc, List locs) { + boolean assumeVerticalShaftMine = !(baritone.bsi.get0(loc.up()).getBlock() instanceof BlockFalling); + if (!Baritone.settings().forceInternalMining.value) { + if (assumeVerticalShaftMine) { + // we can get directly below the block + return new GoalThreeBlocks(loc); + } else { + // we need to get feet or head into the block + return new GoalTwoBlocks(loc); + } } - Set searchingFor = new HashSet<>(); - for (Block block : mining) { - Item drop = block.getItemDropped(block.getDefaultState(), new Random(), 0); - Item ore = Item.getItemFromBlock(block); - searchingFor.add(drop); - searchingFor.add(ore); + boolean upwardGoal = internalMiningGoal(loc.up(), ctx, locs); + boolean downwardGoal = internalMiningGoal(loc.down(), ctx, locs); + boolean doubleDownwardGoal = internalMiningGoal(loc.down(2), ctx, locs); + if (upwardGoal == downwardGoal) { // symmetric + if (doubleDownwardGoal && assumeVerticalShaftMine) { + // we have a checkerboard like pattern + // this one, and the one two below it + // therefore it's fine to path to immediately below this one, since your feet will be in the doubleDownwardGoal + // but only if assumeVerticalShaftMine + return new GoalThreeBlocks(loc); + } else { + // this block has nothing interesting two below, but is symmetric vertically so we can get either feet or head into it + return new GoalTwoBlocks(loc); + } + } + if (upwardGoal) { + // downwardGoal known to be false + // ignore the gap then potential doubleDownward, because we want to path feet into this one and head into upwardGoal + return new GoalBlock(loc); + } + // upwardGoal known to be false, downwardGoal known to be true + if (doubleDownwardGoal && assumeVerticalShaftMine) { + // this block and two below it are goals + // path into the center of the one below, because that includes directly below this one + return new GoalTwoBlocks(loc.down()); + } + // upwardGoal false, downwardGoal true, doubleDownwardGoal false + // just this block and the one immediately below, no others + return new GoalBlock(loc.down()); + } + + private static class GoalThreeBlocks extends GoalTwoBlocks { + + public GoalThreeBlocks(BlockPos pos) { + super(pos); + } + + @Override + public boolean isInGoal(int x, int y, int z) { + return x == this.x && (y == this.y || y == this.y - 1 || y == this.y - 2) && z == this.z; + } + + @Override + public double heuristic(int x, int y, int z) { + int xDiff = x - this.x; + int yDiff = y - this.y; + int zDiff = z - this.z; + return GoalBlock.calculate(xDiff, yDiff < -1 ? yDiff + 2 : yDiff == -1 ? 0 : yDiff, zDiff); + } + } + + public static List droppedItemsScan(BlockOptionalMetaLookup filter, World world) { + if (!Baritone.settings().mineScanDroppedItems.value) { + return Collections.emptyList(); } 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)); } } @@ -203,32 +297,46 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro return ret; } - public static List searchWorld(CalculationContext ctx, List mining, int max, List alreadyKnown) { + public static List searchWorld(CalculationContext ctx, BlockOptionalMetaLookup filter, int max, List alreadyKnown, List blacklist) { List locs = new ArrayList<>(); - List uninteresting = new ArrayList<>(); - //long b = System.currentTimeMillis(); - for (Block m : mining) { - if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(m)) { - locs.addAll(ctx.worldData.getCachedWorld().getLocationsOf(ChunkPacker.blockToString(m), 1, ctx.getBaritone().getPlayerContext().playerFeet().getX(), ctx.getBaritone().getPlayerContext().playerFeet().getZ(), 2)); + 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(block), + Baritone.settings().maxCachedWorldScanCount.value, + pf.x, + pf.z, + 2 + )); } else { - uninteresting.add(m); + untracked.add(block); } } - //System.out.println("Scan of cached chunks took " + (System.currentTimeMillis() - b) + "ms"); - if (locs.isEmpty()) { - uninteresting = mining; - } - if (!uninteresting.isEmpty()) { - //long before = System.currentTimeMillis(); - locs.addAll(WorldScanner.INSTANCE.scanChunkRadius(ctx.getBaritone().getPlayerContext(), uninteresting, max, 10, 26)); - //System.out.println("Scan of loaded chunks took " + (System.currentTimeMillis() - before) + "ms"); + + 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); + + return prune(ctx, locs, filter, max, blacklist); } - public void addNearby() { - knownOreLocations.addAll(droppedItemsScan(mining, ctx.world())); + private void addNearby() { + knownOreLocations.addAll(droppedItemsScan(filter, ctx.world())); BlockPos playerFeet = ctx.playerFeet(); BlockStateInterface bsi = new BlockStateInterface(ctx); int searchDist = 10; @@ -238,28 +346,41 @@ 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()) && RotationUtils.reachable(ctx.player(), new BlockPos(x, y, z), fakedBlockReachDistance).isPresent()) { - knownOreLocations.add(new BlockPos(x, y, z)); + 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); + } } } } } - knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, mining, ORE_LOCATIONS_COUNT); + knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, filter, ORE_LOCATIONS_COUNT, blacklist); } - public static List prune(CalculationContext ctx, List locs2, List mining, int max) { - 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 && 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; + } + } + return false; + }); List locs = locs2 .stream() .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.bsi, pos)) + .filter(pos -> MineProcess.plausibleToBreak(ctx, pos)) - .sorted(Comparator.comparingDouble(ctx.getBaritone().getPlayerContext().playerFeet()::distanceSq)) + .filter(pos -> !blacklist.contains(pos)) + + .sorted(Comparator.comparingDouble(ctx.getBaritone().getPlayerContext().player()::getDistanceSq)) .collect(Collectors.toList()); if (locs.size() > max) { @@ -268,28 +389,34 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro return locs; } - public static boolean plausibleToBreak(BlockStateInterface bsi, BlockPos pos) { - if (MovementHelper.avoidBreaking(bsi, pos.getX(), pos.getY(), pos.getZ(), bsi.get0(pos))) { + public static boolean plausibleToBreak(CalculationContext ctx, BlockPos pos) { + if (MovementHelper.getMiningDurationTicks(ctx, pos.getX(), pos.getY(), pos.getZ(), ctx.bsi.get0(pos), true) >= COST_INF) { return false; } // bedrock above and below makes it implausible, otherwise we're good - return !(bsi.get0(pos.up()).getBlock() == Blocks.BEDROCK && bsi.get0(pos.down()).getBlock() == Blocks.BEDROCK); + return !(ctx.bsi.get0(pos.up()).getBlock() == Blocks.BEDROCK && ctx.bsi.get0(pos.down()).getBlock() == Blocks.BEDROCK); } @Override public void mineByName(int quantity, String... blocks) { - mine(quantity, blocks == null || blocks.length == 0 ? null : Arrays.stream(blocks).map(ChunkPacker::stringToBlock).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); + 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!"); + this.mine(quantity, (BlockOptionalMetaLookup) null); + return; + } this.desiredQuantity = quantity; this.knownOreLocations = new ArrayList<>(); + this.blacklist = new ArrayList<>(); this.branchPoint = null; this.branchPointRunaway = null; - if (mining != null) { + if (filter != null) { rescan(new ArrayList<>(), new CalculationContext(baritone)); } } diff --git a/src/main/java/baritone/selection/Selection.java b/src/main/java/baritone/selection/Selection.java new file mode 100644 index 00000000..58e069de --- /dev/null +++ b/src/main/java/baritone/selection/Selection.java @@ -0,0 +1,130 @@ +package baritone.selection; + +import baritone.api.selection.ISelection; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3i; + +public class Selection implements ISelection { + + private final BetterBlockPos pos1; + private final BetterBlockPos pos2; + private final BetterBlockPos min; + private final BetterBlockPos max; + private final Vec3i size; + private final AxisAlignedBB aabb; + + public Selection(BetterBlockPos pos1, BetterBlockPos pos2) { + this.pos1 = pos1; + this.pos2 = pos2; + + this.min = new BetterBlockPos( + Math.min(pos1.x, pos2.x), + Math.min(pos1.y, pos2.y), + Math.min(pos1.z, pos2.z) + ); + + this.max = new BetterBlockPos( + Math.max(pos1.x, pos2.x), + Math.max(pos1.y, pos2.y), + Math.max(pos1.z, pos2.z) + ); + + this.size = new Vec3i( + max.x - min.x + 1, + max.y - min.y + 1, + max.z - min.z + 1 + ); + + this.aabb = new AxisAlignedBB(this.min, this.max.add(1, 1, 1)); + } + + @Override + public BetterBlockPos pos1() { + return pos1; + } + + @Override + public BetterBlockPos pos2() { + return pos2; + } + + @Override + public BetterBlockPos min() { + return min; + } + + @Override + public BetterBlockPos max() { + return max; + } + + @Override + public Vec3i size() { + return size; + } + + @Override + public AxisAlignedBB aabb() { + return aabb; + } + + @Override + public int hashCode() { + return pos1.hashCode() ^ pos2.hashCode(); + } + + @Override + public String toString() { + return String.format("Selection{pos1=%s,pos2=%s}", pos1, pos2); + } + + /** + * Since it might not be immediately obvious what this does, let me explain. + *

+ * Let's say you specify EnumFacing.UP, this functions returns if pos2 is the highest BlockPos. + * If you specify EnumFacing.DOWN, it returns if pos2 is the lowest BlockPos. + * + * @param facing The direction to check. + * @return {@code true} if pos2 is further in that direction than pos1, {@code false} if it isn't, and something + * else if they're both at the same position on that axis (it really doesn't matter) + */ + private boolean isPos2(EnumFacing facing) { + boolean negative = facing.getAxisDirection().getOffset() < 0; + + switch (facing.getAxis()) { + case X: + return (pos2.x > pos1.x) ^ negative; + case Y: + return (pos2.y > pos1.y) ^ negative; + case Z: + return (pos2.z > pos1.z) ^ negative; + default: + throw new IllegalStateException("Bad EnumFacing.Axis"); + } + } + + @Override + public ISelection expand(EnumFacing direction, int blocks) { + if (isPos2(direction)) { + return new Selection(pos1, pos2.offset(direction, blocks)); + } else { + return new Selection(pos1.offset(direction, blocks), pos2); + } + } + + @Override + public ISelection contract(EnumFacing direction, int blocks) { + if (isPos2(direction)) { + return new Selection(pos1.offset(direction, blocks), pos2); + } else { + return new Selection(pos1, pos2.offset(direction, blocks)); + } + } + + @Override + public ISelection shift(EnumFacing direction, int blocks) { + return new Selection(pos1.offset(direction, blocks), pos2.offset(direction, blocks)); + } +} diff --git a/src/main/java/baritone/selection/SelectionManager.java b/src/main/java/baritone/selection/SelectionManager.java new file mode 100644 index 00000000..c7f21300 --- /dev/null +++ b/src/main/java/baritone/selection/SelectionManager.java @@ -0,0 +1,118 @@ +package baritone.selection; + +import baritone.Baritone; +import baritone.api.selection.ISelection; +import baritone.api.selection.ISelectionManager; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.EnumFacing; + +import java.util.LinkedList; +import java.util.ListIterator; + +public class SelectionManager implements ISelectionManager { + + private final LinkedList selections = new LinkedList<>(); + private ISelection[] selectionsArr = new ISelection[0]; + + public SelectionManager(Baritone baritone) { + new SelectionRenderer(baritone, this); + } + + private void resetSelectionsArr() { + selectionsArr = selections.toArray(new ISelection[0]); + } + + @Override + public synchronized ISelection addSelection(ISelection selection) { + selections.add(selection); + resetSelectionsArr(); + return selection; + } + + @Override + public ISelection addSelection(BetterBlockPos pos1, BetterBlockPos pos2) { + return addSelection(new Selection(pos1, pos2)); + } + + @Override + public synchronized ISelection removeSelection(ISelection selection) { + selections.remove(selection); + resetSelectionsArr(); + return selection; + } + + @Override + public synchronized ISelection[] removeAllSelections() { + ISelection[] selectionsArr = getSelections(); + selections.clear(); + resetSelectionsArr(); + return selectionsArr; + } + + @Override + public ISelection[] getSelections() { + return selectionsArr; + } + + @Override + public synchronized ISelection getOnlySelection() { + if (selections.size() == 1) { + return selections.peekFirst(); + } + + return null; + } + + @Override + public ISelection getLastSelection() { + return selections.peekLast(); + } + + @Override + public synchronized ISelection expand(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.expand(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } + + @Override + public synchronized ISelection contract(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.contract(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } + + @Override + public synchronized ISelection shift(ISelection selection, EnumFacing direction, int blocks) { + for (ListIterator it = selections.listIterator(); it.hasNext(); ) { + ISelection current = it.next(); + + if (current == selection) { + it.remove(); + it.add(current.shift(direction, blocks)); + resetSelectionsArr(); + return it.previous(); + } + } + + return null; + } +} diff --git a/src/main/java/baritone/selection/SelectionRenderer.java b/src/main/java/baritone/selection/SelectionRenderer.java new file mode 100644 index 00000000..89104d03 --- /dev/null +++ b/src/main/java/baritone/selection/SelectionRenderer.java @@ -0,0 +1,57 @@ +package baritone.selection; + +import baritone.Baritone; +import baritone.api.event.events.RenderEvent; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.selection.ISelection; +import baritone.utils.IRenderer; +import net.minecraft.util.math.AxisAlignedBB; + +public class SelectionRenderer implements IRenderer, AbstractGameEventListener { + + public static final double SELECTION_BOX_EXPANSION = .005D; + + private final SelectionManager manager; + + SelectionRenderer(Baritone baritone, SelectionManager manager) { + this.manager = manager; + baritone.getGameEventHandler().registerEventListener(this); + } + + public static void renderSelections(ISelection[] selections) { + float opacity = settings.selectionOpacity.value; + boolean ignoreDepth = settings.renderSelectionIgnoreDepth.value; + float lineWidth = settings.selectionLineWidth.value; + + if (!settings.renderSelection.value) { + return; + } + + IRenderer.startLines(settings.colorSelection.value, opacity, lineWidth, ignoreDepth); + + for (ISelection selection : selections) { + IRenderer.drawAABB(selection.aabb(), SELECTION_BOX_EXPANSION); + } + + if (settings.renderSelectionCorners.value) { + IRenderer.glColor(settings.colorSelectionPos1.value, opacity); + + for (ISelection selection : selections) { + IRenderer.drawAABB(new AxisAlignedBB(selection.pos1(), selection.pos1().add(1, 1, 1))); + } + + IRenderer.glColor(settings.colorSelectionPos2.value, opacity); + + for (ISelection selection : selections) { + IRenderer.drawAABB(new AxisAlignedBB(selection.pos2(), selection.pos2().add(1, 1, 1))); + } + } + + IRenderer.endLines(ignoreDepth); + } + + @Override + public void onRenderPass(RenderEvent event) { + renderSelections(manager.getSelections()); + } +} diff --git a/src/main/java/baritone/utils/BaritoneAutoTest.java b/src/main/java/baritone/utils/BaritoneAutoTest.java index b7ae5fbb..a723ebc5 100644 --- a/src/main/java/baritone/utils/BaritoneAutoTest.java +++ b/src/main/java/baritone/utils/BaritoneAutoTest.java @@ -22,6 +22,7 @@ import baritone.api.event.events.TickEvent; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiMainMenu; @@ -115,7 +116,9 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { } // Setup Baritone's pathing goal and (if needed) begin pathing - BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(GOAL); + if (!BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().isActive()) { + BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(GOAL); + } // If we have reached our goal, print a message and safely close the game if (GOAL.isInGoal(ctx.playerFeet())) { diff --git a/src/main/java/baritone/utils/BaritoneProcessHelper.java b/src/main/java/baritone/utils/BaritoneProcessHelper.java index 1a66eaa5..6abec949 100644 --- a/src/main/java/baritone/utils/BaritoneProcessHelper.java +++ b/src/main/java/baritone/utils/BaritoneProcessHelper.java @@ -19,18 +19,17 @@ package baritone.utils; import baritone.Baritone; import baritone.api.process.IBaritoneProcess; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; public abstract class BaritoneProcessHelper implements IBaritoneProcess, Helper { protected final Baritone baritone; protected final IPlayerContext ctx; - private final double priority; - public BaritoneProcessHelper(Baritone baritone, double priority) { + public BaritoneProcessHelper(Baritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); - this.priority = priority; baritone.getPathingControlManager().registerProcess(this); } @@ -38,9 +37,4 @@ public abstract class BaritoneProcessHelper implements IBaritoneProcess, Helper public boolean isTemporary() { return false; } - - @Override - public double priority() { - return priority; - } } diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index b410e6b3..26e82cd7 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -17,10 +17,9 @@ 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; /** @@ -29,34 +28,43 @@ import net.minecraft.util.math.RayTraceResult; */ public final class BlockBreakHelper implements Helper { + private final IPlayerContext ctx; private boolean didBreakLastTick; - private IPlayerContext playerContext; - - public BlockBreakHelper(IPlayerContext playerContext) { - this.playerContext = playerContext; - } - - public 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.typeOfHit == 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 new file mode 100644 index 00000000..440bfb93 --- /dev/null +++ b/src/main/java/baritone/utils/BlockPlaceHelper.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.utils; + +import baritone.Baritone; +import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.RayTraceResult; + +public class BlockPlaceHelper implements Helper { + + private final IPlayerContext ctx; + private int rightClickTimer; + + BlockPlaceHelper(IPlayerContext playerContext) { + this.ctx = playerContext; + } + + public void tick(boolean rightClickRequested) { + if (rightClickTimer > 0) { + rightClickTimer--; + return; + } + RayTraceResult mouseOver = ctx.objectMouseOver(); + if (!rightClickRequested || ctx.player().isRowingBoat() || mouseOver == null || mouseOver.getBlockPos() == null || mouseOver.typeOfHit != RayTraceResult.Type.BLOCK) { + return; + } + rightClickTimer = Baritone.settings().rightClickSpeed.value; + for (EnumHand hand : EnumHand.values()) { + if (ctx.playerController().processRightClickBlock(ctx.player(), ctx.world(), mouseOver.getBlockPos(), mouseOver.sideHit, mouseOver.hitVec, hand) == EnumActionResult.SUCCESS) { + ctx.player().swingArm(hand); + return; + } + if (!ctx.player().getHeldItem(hand).isEmpty() && ctx.playerController().processRightClick(ctx.player(), ctx.world(), hand) == EnumActionResult.SUCCESS) { + return; + } + } + } +} diff --git a/src/main/java/baritone/utils/BlockStateInterface.java b/src/main/java/baritone/utils/BlockStateInterface.java index 5f8cac6a..84bdce5f 100644 --- a/src/main/java/baritone/utils/BlockStateInterface.java +++ b/src/main/java/baritone/utils/BlockStateInterface.java @@ -66,7 +66,7 @@ public class BlockStateInterface { } else { this.loadedChunks = worldLoaded; // this will only be used on the main thread } - this.useTheRealWorld = !Baritone.settings().pathThroughCachedOnly.get(); + this.useTheRealWorld = !Baritone.settings().pathThroughCachedOnly.value; if (!Minecraft.getMinecraft().isCallingFromMinecraftThread()) { throw new IllegalStateException(); } diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java deleted file mode 100644 index 502e3d4b..00000000 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ /dev/null @@ -1,562 +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.utils; - -import baritone.Baritone; -import baritone.api.Settings; -import baritone.api.bot.connect.IConnectionResult; -import baritone.api.cache.IRememberedInventory; -import baritone.api.cache.IWaypoint; -import baritone.api.event.events.ChatEvent; -import baritone.api.pathing.goals.*; -import baritone.api.pathing.movement.ActionCosts; -import baritone.api.utils.SettingsUtil; -import baritone.behavior.Behavior; -import baritone.behavior.PathingBehavior; -import baritone.bot.UserManager; -import baritone.bot.connect.ConnectionResult; -import baritone.cache.ChunkPacker; -import baritone.cache.Waypoint; -import baritone.pathing.movement.CalculationContext; -import baritone.pathing.movement.Movement; -import baritone.pathing.movement.Moves; -import baritone.process.CustomGoalProcess; -import baritone.utils.pathing.SegmentedCalculator; -import cabaletta.comms.SocketConnection; -import net.minecraft.block.Block; -import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.Session; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.Chunk; - -import java.io.IOException; -import java.net.Socket; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ExampleBaritoneControl extends Behavior implements Helper { - - private static final String HELP_MSG = - "baritone - Output settings into chat\n" + - "settings - Same as baritone\n" + - "goal - Create a goal (one number is '', two is ' ', three is ' , 'clear' to clear)\n" + - "path - Go towards goal\n" + - "repack - (debug) Repacks chunk cache\n" + - "rescan - (debug) Same as repack\n" + - "axis - Paths towards the closest axis or diagonal axis, at y=120\n" + - "cancel - Cancels current path\n" + - "forcecancel - sudo cancel (only use if very glitched, try toggling 'pause' first)\n" + - "gc - Calls System.gc();\n" + - "invert - Runs away from the goal instead of towards it\n" + - "follow - Follows a player 'follow username'\n" + - "reloadall - (debug) Reloads chunk cache\n" + - "saveall - (debug) Saves chunk cache\n" + - "find - (debug) outputs how many blocks of a certain type are within the cache\n" + - "mine - Paths to and mines specified blocks 'mine x_ore y_ore ...'\n" + - "thisway - Creates a goal X blocks where you're facing\n" + - "list - Lists waypoints under a category\n" + - "get - Same as list\n" + - "show - Same as list\n" + - "save - Saves a waypoint (works but don't try to make sense of it)\n" + - "goto - Paths towards specified block or waypoint\n" + - "spawn - Paths towards world spawn or your most recent bed right-click\n" + - "sethome - Sets \"home\"\n" + - "home - Paths towards \"home\" \n" + - "costs - (debug) all movement costs from current location\n" + - "damn - Daniel "; - - public ExampleBaritoneControl(Baritone baritone) { - super(baritone); - } - - @Override - public void onSendChatMessage(ChatEvent event) { - if (!Baritone.settings().chatControl.get() && !Baritone.settings().removePrefix.get()) { - return; - } - String msg = event.getMessage(); - if (Baritone.settings().prefix.get()) { - if (!msg.startsWith("#")) { - return; - } - msg = msg.substring(1); - } - if (runCommand(msg)) { - event.cancel(); - } - } - - public boolean runCommand(String msg0) { - String msg = msg0.toLowerCase(Locale.US).trim(); // don't reassign the argument LOL - PathingBehavior pathingBehavior = baritone.getPathingBehavior(); - CustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); - List> toggleable = Baritone.settings().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(Baritone.settings()); - return true; - } - } - if (msg.equals("baritone") || msg.equals("settings")) { - for (Settings.Setting setting : Baritone.settings().allSettings) { - logDirect(setting.toString()); - } - return true; - } - if (msg.equals("") || msg.equals("help") || msg.equals("?")) { - for (String line : HELP_MSG.split("\n")) { - logDirect(line); - } - return false; - } - if (msg.contains(" ")) { - String[] data = msg.split(" "); - if (data.length == 2) { - Settings.Setting setting = Baritone.settings().byLowerName.get(data[0]); - if (setting != null) { - try { - if (setting.value.getClass() == Long.class) { - setting.value = Long.parseLong(data[1]); - } - if (setting.value.getClass() == Integer.class) { - setting.value = Integer.parseInt(data[1]); - } - if (setting.value.getClass() == Double.class) { - setting.value = Double.parseDouble(data[1]); - } - if (setting.value.getClass() == Float.class) { - setting.value = Float.parseFloat(data[1]); - } - } catch (NumberFormatException e) { - logDirect("Unable to parse " + data[1]); - return true; - } - SettingsUtil.save(Baritone.settings()); - logDirect(setting.toString()); - return true; - } - } - } - if (Baritone.settings().byLowerName.containsKey(msg)) { - Settings.Setting setting = Baritone.settings().byLowerName.get(msg); - logDirect(setting.toString()); - return true; - } - - if (msg.startsWith("goal")) { - String[] params = msg.substring(4).trim().split(" "); - if (params[0].equals("")) { - params = new String[]{}; - } - Goal goal; - try { - switch (params.length) { - case 0: - goal = new GoalBlock(ctx.playerFeet()); - break; - case 1: - if (params[0].equals("clear") || params[0].equals("none")) { - goal = null; - } else { - goal = new GoalYLevel(Integer.parseInt(params[0])); - } - break; - case 2: - goal = new GoalXZ(Integer.parseInt(params[0]), Integer.parseInt(params[1])); - break; - case 3: - goal = new GoalBlock(new BlockPos(Integer.parseInt(params[0]), Integer.parseInt(params[1]), Integer.parseInt(params[2]))); - break; - default: - logDirect("unable to understand lol"); - return true; - } - } catch (NumberFormatException ex) { - logDirect("unable to parse integer " + ex); - return true; - } - customGoalProcess.setGoal(goal); - logDirect("Goal: " + goal); - 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("repack") || msg.equals("rescan")) { - ChunkProviderClient cli = (ChunkProviderClient) ctx.world().getChunkProvider(); - int playerChunkX = ctx.playerFeet().getX() >> 4; - int playerChunkZ = ctx.playerFeet().getZ() >> 4; - int count = 0; - for (int x = playerChunkX - 40; x <= playerChunkX + 40; x++) { - for (int z = playerChunkZ - 40; z <= playerChunkZ + 40; z++) { - Chunk chunk = cli.getLoadedChunk(x, z); - if (chunk != null) { - count++; - baritone.getWorldProvider().getCurrentWorld().getCachedWorld().queueForPacking(chunk); - } - } - } - logDirect("Queued " + count + " chunks for repacking"); - return true; - } - if (msg.equals("axis")) { - 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.equals("reset")) { - Baritone.settings().reset(); - logDirect("Baritone settings reset"); - return true; - } - if (msg.equals("echest")) { - Optional> contents = baritone.getMemoryBehavior().echest(); - if (contents.isPresent()) { - logDirect("echest contents:"); - log(contents.get()); - } else { - logDirect("echest contents unknown"); - } - return true; - } - if (msg.equals("chests")) { - System.out.println(baritone.getWorldProvider()); - System.out.println(baritone.getWorldProvider().getCurrentWorld()); - - System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory()); - - System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories()); - - System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet()); - - System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet()); - for (Map.Entry entry : baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet()) { - logDirect(entry.getKey() + ""); - log(entry.getValue().getContents()); - } - 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("follow")) { - String name = msg.substring(6).trim(); - Optional toFollow = Optional.empty(); - if (name.length() == 0) { - toFollow = ctx.getSelectedEntity(); - } else { - for (EntityPlayer pl : ctx.world().playerEntities) { - String theirName = pl.getName().trim().toLowerCase(); - if (!theirName.equals(ctx.player().getName().trim().toLowerCase()) && (theirName.contains(name) || name.contains(theirName))) { // don't follow ourselves lol - toFollow = Optional.of(pl); - } - } - } - if (!toFollow.isPresent()) { - logDirect("Not found"); - return true; - } - Entity effectivelyFinal = toFollow.get(); - baritone.getFollowProcess().follow(x -> effectivelyFinal.equals(x)); - logDirect("Following " + toFollow.get()); - 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("find")) { - String blockType = msg.substring(4).trim(); - LinkedList 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 = BlockStateInterface.get(ctx, pos).getBlock(); - if (!ChunkPacker.blockToString(actually).equalsIgnoreCase(blockType)) { - System.out.println("Was looking for " + blockType + " but actually found " + actually + " " + ChunkPacker.blockToString(actually)); - } - } - return true; - } - if (msg.startsWith("mine")) { - String[] blockTypes = msg.substring(4).trim().split(" "); - try { - int quantity = Integer.parseInt(blockTypes[1]); - Block block = ChunkPacker.stringToBlock(blockTypes[0]); - Objects.requireNonNull(block); - baritone.getMineProcess().mine(quantity, block); - logDirect("Will mine " + quantity + " " + blockTypes[0]); - return true; - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) {} - for (String s : blockTypes) { - if (ChunkPacker.stringToBlock(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.startsWith("thisway")) { - 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); - } - Waypoint.Tag tag = Waypoint.Tag.fromString(waypointType); - if (tag == null) { - logDirect("Not a valid tag. Tags are: " + Arrays.asList(Waypoint.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, Waypoint.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("goto")) { - String waypointType = msg.substring(4).trim(); - if (waypointType.endsWith("s") && Waypoint.Tag.fromString(waypointType.substring(0, waypointType.length() - 1)) != null) { - // for example, "show deaths" - waypointType = waypointType.substring(0, waypointType.length() - 1); - } - Waypoint.Tag tag = Waypoint.Tag.fromString(waypointType); - IWaypoint waypoint; - if (tag == null) { - String mining = waypointType; - Block block = ChunkPacker.stringToBlock(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) { - logDirect("No locations for " + mining + " known, cancelling"); - 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 = new GoalBlock(waypoint.getLocation()); - customGoalProcess.setGoalAndPath(goal); - return true; - } - if (msg.equals("spawn") || msg.equals("bed")) { - IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(Waypoint.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("", Waypoint.Tag.HOME, ctx.playerFeet())); - logDirect("Saved. Say home to set goal."); - return true; - } - if (msg.equals("home")) { - IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(Waypoint.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("costs")) { - List moves = Stream.of(Moves.values()).map(x -> x.apply0(new CalculationContext(baritone), ctx.playerFeet())).collect(Collectors.toCollection(ArrayList::new)); - while (moves.contains(null)) { - moves.remove(null); - } - moves.sort(Comparator.comparingDouble(Movement::getCost)); - for (Movement move : moves) { - String[] parts = move.getClass().toString().split("\\."); - double cost = move.getCost(); - String strCost = cost + ""; - if (cost >= ActionCosts.COST_INF) { - strCost = "IMPOSSIBLE"; - } - logDirect(parts[parts.length - 1] + " " + move.getDest().getX() + "," + move.getDest().getY() + "," + move.getDest().getZ() + " " + strCost); - } - return true; - } - if (msg.startsWith("connect")) { - String dest = msg.substring(7).trim(); - String[] parts = dest.split(" "); - if (parts.length != 2) { - logDirect("Unable to parse"); - return true; - } - try { - Socket s = new Socket(parts[0], Integer.parseInt(parts[1])); - SocketConnection conn = new SocketConnection(s); - baritone.getControllerBehavior().connectTo(conn); - logDirect("Created and attached socket connection"); - } catch (IOException | NumberFormatException e) { - logDirect("Unable to connect " + e); - } - return true; - } - if (msg.equals("damn")) { - logDirect("daniel"); - } - // TODO: Temporary command to test bots offline - if (msg.equals("bot")) { - System.out.println("DOING A BOT"); - IConnectionResult result = UserManager.INSTANCE.connect(new Session("Bot" + System.currentTimeMillis() % 1000, UUID.randomUUID().toString(), "", "")); - System.out.println(result); - return true; - } - return false; - } - - private void log(List stacks) { - for (ItemStack stack : stacks) { - if (!stack.isEmpty()) { - logDirect(stack.getCount() + "x " + stack.getDisplayName() + "@" + stack.getItemDamage()); - } - } - } -} diff --git a/src/main/java/baritone/utils/GuiClick.java b/src/main/java/baritone/utils/GuiClick.java new file mode 100644 index 00000000..fc97605f --- /dev/null +++ b/src/main/java/baritone/utils/GuiClick.java @@ -0,0 +1,145 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.Baritone; +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.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import org.lwjgl.BufferUtils; +import org.lwjgl.input.Mouse; +import org.lwjgl.util.glu.GLU; + +import java.awt.*; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Collections; + +import static org.lwjgl.opengl.GL11.*; +import static baritone.api.utils.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class GuiClick extends GuiScreen { + + // My name is Brady and I grant leijurv permission to use this pasted code + private final FloatBuffer MODELVIEW = BufferUtils.createFloatBuffer(16); + private final FloatBuffer PROJECTION = BufferUtils.createFloatBuffer(16); + private final IntBuffer VIEWPORT = BufferUtils.createIntBuffer(16); + private final FloatBuffer TO_WORLD_BUFFER = BufferUtils.createFloatBuffer(3); + + private BlockPos clickStart; + private BlockPos currentMouseOver; + + @Override + public boolean doesGuiPauseGame() { + return false; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + int mx = Mouse.getX(); + int my = Mouse.getY(); + Vec3d near = toWorld(mx, my, 0); + Vec3d far = toWorld(mx, my, 1); // "Use 0.945 that's what stack overflow says" - leijurv + if (near != null && far != null) { + Vec3d viewerPos = new Vec3d(mc.getRenderManager().viewerPosX, mc.getRenderManager().viewerPosY, mc.getRenderManager().viewerPosZ); + RayTraceResult result = mc.world.rayTraceBlocks(near.add(viewerPos), far.add(viewerPos), false, false, true); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { + currentMouseOver = result.getBlockPos(); + } + } + } + + @Override + protected void mouseReleased(int mouseX, int mouseY, int mouseButton) { + if (mouseButton == 0) { + if (clickStart != null && !clickStart.equals(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)); + } + } else if (mouseButton == 1) { + BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(currentMouseOver.up())); + } + clickStart = null; + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { + clickStart = currentMouseOver; + } + + public void onRender() { + GlStateManager.getFloat(GL_MODELVIEW_MATRIX, (FloatBuffer) MODELVIEW.clear()); + GlStateManager.getFloat(GL_PROJECTION_MATRIX, (FloatBuffer) PROJECTION.clear()); + GlStateManager.glGetInteger(GL_VIEWPORT, (IntBuffer) VIEWPORT.clear()); + + if (currentMouseOver != null) { + Entity e = mc.getRenderViewEntity(); + // drawSingleSelectionBox WHEN? + PathRenderer.drawManySelectionBoxes(e, Collections.singletonList(currentMouseOver), Color.CYAN); + if (clickStart != null && !clickStart.equals(currentMouseOver)) { + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + GlStateManager.color(Color.RED.getColorComponents(null)[0], Color.RED.getColorComponents(null)[1], Color.RED.getColorComponents(null)[2], 0.4F); + GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.value); + GlStateManager.disableTexture2D(); + GlStateManager.depthMask(false); + GlStateManager.disableDepth(); + BetterBlockPos a = new BetterBlockPos(currentMouseOver); + BetterBlockPos b = new BetterBlockPos(clickStart); + IRenderer.drawAABB(new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); + GlStateManager.enableDepth(); + + GlStateManager.depthMask(true); + GlStateManager.enableTexture2D(); + GlStateManager.disableBlend(); + } + } + } + + private Vec3d toWorld(double x, double y, double z) { + boolean result = GLU.gluUnProject((float) x, (float) y, (float) z, MODELVIEW, PROJECTION, VIEWPORT, (FloatBuffer) TO_WORLD_BUFFER.clear()); + if (result) { + return new Vec3d(TO_WORLD_BUFFER.get(0), TO_WORLD_BUFFER.get(1), TO_WORLD_BUFFER.get(2)); + } + return null; + } +} diff --git a/src/main/java/baritone/utils/Helper.java b/src/main/java/baritone/utils/Helper.java deleted file mode 100755 index 9ee6ed59..00000000 --- a/src/main/java/baritone/utils/Helper.java +++ /dev/null @@ -1,72 +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.utils; - -import baritone.Baritone; -import net.minecraft.client.Minecraft; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; - -/** - * @author Brady - * @since 8/1/2018 - */ -public interface Helper { - - /** - * Instance of {@link Helper}. Used for static-context reference. - */ - 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 - )); - - Minecraft mc = Minecraft.getMinecraft(); - - /** - * Send a message to chat only if chatDebug is on - * - * @param message The message to display in chat - */ - default void logDebug(String message) { - if (!Baritone.settings().chatDebug.get()) { - //System.out.println("Suppressed debug message:"); - //System.out.println(message); - return; - } - logDirect(message); - } - - /** - * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a direct response to a chat command) - * - * @param message The message to display in chat - */ - default void logDirect(String message) { - ITextComponent component = MESSAGE_PREFIX.createCopy(); - component.getStyle().setColor(TextFormatting.GRAY); - component.appendSibling(new TextComponentString(" " + message)); - Minecraft.getMinecraft().addScheduledTask(() -> Baritone.settings().logger.get().accept(component)); - } -} diff --git a/src/main/java/baritone/utils/IRenderer.java b/src/main/java/baritone/utils/IRenderer.java new file mode 100644 index 00000000..0078f227 --- /dev/null +++ b/src/main/java/baritone/utils/IRenderer.java @@ -0,0 +1,112 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils; + +import baritone.api.BaritoneAPI; +import baritone.api.Settings; +import baritone.api.utils.Helper; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.math.AxisAlignedBB; + +import java.awt.*; + +import static org.lwjgl.opengl.GL11.*; + +public interface IRenderer { + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + RenderManager renderManager = Helper.mc.getRenderManager(); + Settings settings = BaritoneAPI.getSettings(); + + static void glColor(Color color, float alpha) { + float[] colorComponents = color.getColorComponents(null); + GlStateManager.color(colorComponents[0], colorComponents[1], colorComponents[2], alpha); + } + + static void startLines(Color color, float alpha, float lineWidth, boolean ignoreDepth) { + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + glColor(color, alpha); + GlStateManager.glLineWidth(lineWidth); + GlStateManager.disableTexture2D(); + GlStateManager.depthMask(false); + + if (ignoreDepth) { + GlStateManager.disableDepth(); + } + } + + static void startLines(Color color, float lineWidth, boolean ignoreDepth) { + startLines(color, .4f, lineWidth, ignoreDepth); + } + + static void endLines(boolean ignoredDepth) { + if (ignoredDepth) { + GlStateManager.enableDepth(); + } + + GlStateManager.depthMask(true); + GlStateManager.enableTexture2D(); + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + } + + static void drawAABB(AxisAlignedBB aabb) { + AxisAlignedBB toDraw = aabb.offset(-renderManager.viewerPosX, -renderManager.viewerPosY, -renderManager.viewerPosZ); + + buffer.begin(GL_LINES, DefaultVertexFormats.POSITION); + // bottom + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + // top + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + // corners + buffer.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + buffer.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + tessellator.draw(); + } + + static void drawAABB(AxisAlignedBB aabb, double expand) { + drawAABB(aabb.grow(expand, expand, expand)); + } +} diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 93fbe875..47a06854 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -1,131 +1,118 @@ -/* - * This file is part of Baritone. - * - * Baritone is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Baritone is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License 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.Baritone; -import baritone.api.BaritoneAPI; -import baritone.api.event.events.TickEvent; -import baritone.api.utils.IInputOverrideHandler; -import baritone.api.utils.input.Input; -import baritone.behavior.Behavior; -import net.minecraft.client.Minecraft; -import net.minecraft.client.settings.KeyBinding; -import net.minecraft.util.MovementInput; -import net.minecraft.util.MovementInputFromOptions; - -import java.util.HashMap; -import java.util.Map; - -/** - * An interface with the game's control system allowing the ability to - * force down certain controls, having the same effect as if we were actually - * physically forcing down the assigned key. - * - * @author Brady - * @since 7/31/2018 - */ -public final class InputOverrideHandler extends Behavior implements IInputOverrideHandler { - - /** - * Maps inputs to whether or not we are forcing their state down. - */ - private final Map inputForceStateMap = new HashMap<>(); - - private final BlockBreakHelper blockBreakHelper; - - public InputOverrideHandler(Baritone baritone) { - super(baritone); - this.blockBreakHelper = new BlockBreakHelper(baritone.getPlayerContext()); - } - - /** - * Returns whether or not we are forcing down the specified {@link KeyBinding}. - * - * @param key The KeyBinding object - * @return Whether or not it is being forced down - */ - @Override - public final Boolean isInputForcedDown(KeyBinding key) { - Input input = Input.getInputForBind(key); - if (input == null || !inControl()) { - return null; - } - if (input == Input.CLICK_LEFT) { - return false; - } - if (input == Input.CLICK_RIGHT) { - return isInputForcedDown(Input.CLICK_RIGHT); - } - return null; - } - - /** - * Returns whether or not we are forcing down the specified {@link Input}. - * - * @param input The input - * @return Whether or not it is being forced down - */ - @Override - public final boolean isInputForcedDown(Input input) { - return input == null ? false : this.inputForceStateMap.getOrDefault(input, false); - } - - /** - * Sets whether or not the specified {@link Input} is being forced down. - * - * @param input The {@link Input} - * @param forced Whether or not the state is being forced - */ - @Override - public final void setInputForceState(Input input, boolean forced) { - this.inputForceStateMap.put(input, forced); - } - - /** - * Clears the override state for all keys - */ - @Override - public final void clearAllKeys() { - this.inputForceStateMap.clear(); - } - - @Override - public final void onTick(TickEvent event) { - if (event.getType() == TickEvent.Type.OUT) { - return; - } - blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT)); - - MovementInput desired = inControl() - ? new PlayerMovementInput(this) - : new MovementInputFromOptions(Minecraft.getMinecraft().gameSettings); - - if (ctx.player().movementInput.getClass() != desired.getClass()) { - ctx.player().movementInput = desired; // only set it if it was previously incorrect - // gotta do it this way, or else it constantly thinks you're beginning a double tap W sprint lol - } - } - - private boolean inControl() { - return baritone.getPathingBehavior().isPathing() || baritone != BaritoneAPI.getProvider().getPrimaryBaritone(); - } - - public BlockBreakHelper getBlockBreakHelper() { - return blockBreakHelper; - } -} +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.Baritone; +import baritone.api.BaritoneAPI; +import baritone.api.event.events.TickEvent; +import baritone.api.utils.IInputOverrideHandler; +import baritone.api.utils.input.Input; +import baritone.behavior.Behavior; +import net.minecraft.client.Minecraft; +import net.minecraft.util.MovementInputFromOptions; + +import java.util.HashMap; +import java.util.Map; + +/** + * An interface with the game's control system allowing the ability to + * force down certain controls, having the same effect as if we were actually + * physically forcing down the assigned key. + * + * @author Brady + * @since 7/31/2018 + */ +public final class InputOverrideHandler extends Behavior implements IInputOverrideHandler { + + /** + * Maps inputs to whether or not we are forcing their state down. + */ + private final Map inputForceStateMap = new HashMap<>(); + + private final BlockBreakHelper blockBreakHelper; + private final BlockPlaceHelper blockPlaceHelper; + + public InputOverrideHandler(Baritone baritone) { + super(baritone); + this.blockBreakHelper = new BlockBreakHelper(baritone.getPlayerContext()); + this.blockPlaceHelper = new BlockPlaceHelper(baritone.getPlayerContext()); + } + + /** + * Returns whether or not we are forcing down the specified {@link Input}. + * + * @param input The input + * @return Whether or not it is being forced down + */ + @Override + public final boolean isInputForcedDown(Input input) { + return input == null ? false : this.inputForceStateMap.getOrDefault(input, false); + } + + /** + * Sets whether or not the specified {@link Input} is being forced down. + * + * @param input The {@link Input} + * @param forced Whether or not the state is being forced + */ + @Override + public final void setInputForceState(Input input, boolean forced) { + this.inputForceStateMap.put(input, forced); + } + + /** + * Clears the override state for all keys + */ + @Override + public final void clearAllKeys() { + this.inputForceStateMap.clear(); + } + + @Override + public final void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.OUT) { + return; + } + if (isInputForcedDown(Input.CLICK_LEFT)) { + setInputForceState(Input.CLICK_RIGHT, false); + } + blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT)); + blockPlaceHelper.tick(isInputForcedDown(Input.CLICK_RIGHT)); + + if (inControl()) { + if (ctx.player().movementInput.getClass() != PlayerMovementInput.class) { + ctx.player().movementInput = new PlayerMovementInput(this); + } + } else { + if (ctx.player().movementInput.getClass() == PlayerMovementInput.class) { // allow other movement inputs that aren't this one, e.g. for a freecam + ctx.player().movementInput = new MovementInputFromOptions(Minecraft.getMinecraft().gameSettings); + } + } + // only set it if it was previously incorrect + // gotta do it this way, or else it constantly thinks you're beginning a double tap W sprint lol + } + + private boolean inControl() { + // if we are not primary (a bot) we should set the movementinput even when idle (not pathing) + return baritone.getPathingBehavior().isPathing() || baritone != BaritoneAPI.getProvider().getPrimaryBaritone(); + } + + public BlockBreakHelper getBlockBreakHelper() { + return blockBreakHelper; + } +} diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index 256c1ae4..29d455d2 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -17,19 +17,17 @@ package baritone.utils; -import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.event.events.RenderEvent; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.*; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; 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; @@ -49,21 +47,21 @@ import static org.lwjgl.opengl.GL11.*; * @author Brady * @since 8/9/2018 */ -public final class PathRenderer implements Helper { - - private static final Tessellator TESSELLATOR = Tessellator.getInstance(); - private static final BufferBuilder BUFFER = TESSELLATOR.getBuffer(); +public final class PathRenderer implements IRenderer { private PathRenderer() {} public static void render(RenderEvent event, PathingBehavior behavior) { - // System.out.println("Render passing"); - // System.out.println(event.getPartialTicks()); float partialTicks = event.getPartialTicks(); Goal goal = behavior.getGoal(); if (behavior.baritone.getPlayerContext().world() == null) { return; } + + if (Helper.mc.currentScreen instanceof GuiClick) { + ((GuiClick) Helper.mc.currentScreen).onRender(); + } + int thisPlayerDimension = behavior.baritone.getPlayerContext().world().provider.getDimensionType().getId(); int currentRenderViewDimension = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world().provider.getDimensionType().getId(); @@ -72,7 +70,7 @@ public final class PathRenderer implements Helper { return; } - Entity renderView = mc.getRenderViewEntity(); + Entity renderView = Helper.mc.getRenderViewEntity(); if (renderView.world != BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world()) { System.out.println("I have no idea what's going on"); @@ -81,91 +79,72 @@ public final class PathRenderer implements Helper { return; } - if (goal != null && Baritone.settings().renderGoal.value) { - drawLitDankGoalBox(renderView, goal, partialTicks, Baritone.settings().colorGoalBox.get()); + if (goal != null && settings.renderGoal.value) { + drawDankLitGoalBox(renderView, goal, partialTicks, settings.colorGoalBox.value); } - if (!Baritone.settings().renderPath.get()) { + + if (!settings.renderPath.value) { return; } - //drawManySelectionBoxes(player, Collections.singletonList(behavior.pathStart()), partialTicks, Color.WHITE); - //long start = System.nanoTime(); - - 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 && 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); // 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.get(), Baritone.settings().fadePath.get(), 10, 20); - } - if (next != null && next.getPath() != null) { - drawPath(next.getPath(), 0, renderView, partialTicks, Baritone.settings().colorNextPath.get(), Baritone.settings().fadePath.get(), 10, 20); + drawPath(current.getPath(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20); } - //long split = System.nanoTime(); - if (current != null) { - drawManySelectionBoxes(renderView, current.toBreak(), partialTicks, Baritone.settings().colorBlocksToBreak.get()); - drawManySelectionBoxes(renderView, current.toPlace(), partialTicks, Baritone.settings().colorBlocksToPlace.get()); - drawManySelectionBoxes(renderView, current.toWalkInto(), partialTicks, Baritone.settings().colorBlocksToWalkInto.get()); + if (next != null && next.getPath() != null) { + 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.get(), Baritone.settings().fadePath.get(), 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.get(), Baritone.settings().fadePath.get(), 10, 20); - drawManySelectionBoxes(renderView, Collections.singletonList(mr.getDest()), partialTicks, Baritone.settings().colorMostRecentConsidered.get()); + currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { + drawPath(mr, 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20); + drawManySelectionBoxes(renderView, Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value); }); }); - //long end = System.nanoTime(); - //System.out.println((end - split) + " " + (split - start)); - // if (end - start > 0) { - // System.out.println("Frame took " + (split - start) + " " + (end - split)); - //} } - public static void drawPath(IPath path, int startIndex, Entity player, float partialTicks, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); - GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.get()); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderPathIgnoreDepth.get()) { - GlStateManager.disableDepth(); - } - List positions = path.positions(); - int next; - Tessellator tessellator = Tessellator.getInstance(); + public static void drawPath(IPath path, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { + IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); + int fadeStart = fadeStart0 + startIndex; int fadeEnd = fadeEnd0 + startIndex; - for (int i = startIndex; i < positions.size() - 1; i = next) { - BetterBlockPos start = positions.get(i); - next = i + 1; - BetterBlockPos end = positions.get(next); + List positions = path.positions(); + for (int i = startIndex, next; i < positions.size() - 1; i = next) { + BetterBlockPos start = positions.get(i); + BetterBlockPos end = positions.get(next = i + 1); int dirX = end.x - start.x; int dirY = end.y - start.y; int dirZ = end.z - start.z; - while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && (dirX == positions.get(next + 1).x - end.x && dirY == positions.get(next + 1).y - end.y && dirZ == positions.get(next + 1).z - end.z)) { - next++; - end = positions.get(next); - } - double x1 = start.x; - double y1 = start.y; - double z1 = start.z; - double x2 = end.x; - double y2 = end.y; - double z2 = end.z; - if (fadeOut) { + while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && + (dirX == positions.get(next + 1).x - end.x && + dirY == positions.get(next + 1).y - end.y && + dirZ == positions.get(next + 1).z - end.z)) { + end = positions.get(++next); + } + + if (fadeOut) { float alpha; + if (i <= fadeStart) { alpha = 0.4F; } else { @@ -174,114 +153,72 @@ public final class PathRenderer implements Helper { } alpha = 0.4F * (1.0F - (float) (i - fadeStart) / (float) (fadeEnd - fadeStart)); } - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], alpha); + + IRenderer.glColor(color, alpha); } - drawLine(player, x1, y1, z1, x2, y2, z2, partialTicks); + + drawLine(start.x, start.y, start.z, end.x, end.y, end.z); + tessellator.draw(); } - if (Baritone.settings().renderPathIgnoreDepth.get()) { - GlStateManager.enableDepth(); - } - //GlStateManager.color(0.0f, 0.0f, 0.0f, 0.4f); - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); + + IRenderer.endLines(settings.renderPathIgnoreDepth.value); } - public static void drawLine(Entity player, double bp1x, double bp1y, double bp1z, double bp2x, double bp2y, double bp2z, float partialTicks) { - double d0 = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double) partialTicks; - double d1 = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double) partialTicks; - double d2 = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double) partialTicks; - 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, float partialTicks, Color color) { - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); - GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.get()); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); + public static void drawManySelectionBoxes(Entity player, Collection positions, Color color) { + IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderSelectionBoxesIgnoreDepth.value); - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.get()) { - GlStateManager.disableDepth(); - } - - float expand = 0.002F; //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? - double renderPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double) partialTicks; - double renderPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double) partialTicks; - double renderPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double) partialTicks; positions.forEach(pos -> { - IBlockState state = BlockStateInterface.get(BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext(), pos); + IBlockState state = bsi.get0(pos); AxisAlignedBB toDraw; + if (state.getBlock().equals(Blocks.AIR)) { toDraw = Blocks.DIRT.getDefaultState().getSelectedBoundingBox(player.world, pos); } else { toDraw = state.getSelectedBoundingBox(player.world, pos); } - toDraw = toDraw.expand(expand, expand, expand).offset(-renderPosX, -renderPosY, -renderPosZ); - 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.drawAABB(toDraw, .002D); }); - if (Baritone.settings().renderSelectionBoxesIgnoreDepth.get()) { - GlStateManager.enableDepth(); - } - - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); + IRenderer.endLines(settings.renderSelectionBoxesIgnoreDepth.value); } - public static void drawLitDankGoalBox(Entity player, Goal goal, float partialTicks, Color color) { - double renderPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double) partialTicks; - double renderPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double) partialTicks; - double renderPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double) partialTicks; - - double minX; - double maxX; - double minZ; - double maxZ; - double minY; - double maxY; - double y1; - double y2; + public static void drawDankLitGoalBox(Entity player, Goal goal, float partialTicks, Color color) { + 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(); minX = goalPos.getX() + 0.002 - renderPosX; maxX = goalPos.getX() + 1 - 0.002 - renderPosX; minZ = goalPos.getZ() + 0.002 - renderPosZ; maxZ = goalPos.getZ() + 1 - 0.002 - renderPosZ; - double y = MathHelper.cos((float) (((float) ((System.nanoTime() / 100000L) % 20000L)) / 20000F * Math.PI * 2)); if (goal instanceof GoalGetToBlock || goal instanceof GoalTwoBlocks) { y /= 2; } @@ -297,10 +234,12 @@ public final class PathRenderer implements Helper { } else if (goal instanceof GoalXZ) { GoalXZ goalPos = (GoalXZ) goal; - if (Baritone.settings().renderGoalXZBeacon.get()) { - mc.getTextureManager().bindTexture(TileEntityBeaconRenderer.TEXTURE_BEACON_BEAM); + if (settings.renderGoalXZBeacon.value) { + glPushAttrib(GL_LIGHTING_BIT); - if (Baritone.settings().renderGoalIgnoreDepth.get()) { + Helper.mc.getTextureManager().bindTexture(TileEntityBeaconRenderer.TEXTURE_BEACON_BEAM); + + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.disableDepth(); } @@ -316,9 +255,11 @@ public final class PathRenderer implements Helper { color.getColorComponents(null) ); - if (Baritone.settings().renderGoalIgnoreDepth.get()) { + if (settings.renderGoalIgnoreDepth.value) { GlStateManager.enableDepth(); } + + glPopAttrib(); return; } @@ -333,53 +274,53 @@ public final class PathRenderer implements Helper { maxY = 256 - renderPosY; } else if (goal instanceof GoalComposite) { for (Goal g : ((GoalComposite) goal).goals()) { - drawLitDankGoalBox(player, g, partialTicks, color); + 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 - 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; + y2 = 1 - y + goalpos.level - renderPosY; } else { return; } - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); - GlStateManager.color(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.6F); - GlStateManager.glLineWidth(Baritone.settings().goalRenderLineWidthPixels.get()); - GlStateManager.disableTexture2D(); - GlStateManager.depthMask(false); - if (Baritone.settings().renderGoalIgnoreDepth.get()) { - GlStateManager.disableDepth(); - } + IRenderer.startLines(color, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); renderHorizontalQuad(minX, maxX, minZ, maxZ, y1); renderHorizontalQuad(minX, maxX, minZ, maxZ, y2); - BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, minY, minZ).endVertex(); - BUFFER.pos(minX, maxY, minZ).endVertex(); - BUFFER.pos(maxX, minY, minZ).endVertex(); - BUFFER.pos(maxX, maxY, minZ).endVertex(); - BUFFER.pos(maxX, minY, maxZ).endVertex(); - BUFFER.pos(maxX, maxY, maxZ).endVertex(); - BUFFER.pos(minX, minY, maxZ).endVertex(); - BUFFER.pos(minX, maxY, maxZ).endVertex(); - TESSELLATOR.draw(); + buffer.begin(GL_LINES, DefaultVertexFormats.POSITION); + buffer.pos(minX, minY, minZ).endVertex(); + buffer.pos(minX, maxY, minZ).endVertex(); + buffer.pos(maxX, minY, minZ).endVertex(); + buffer.pos(maxX, maxY, minZ).endVertex(); + buffer.pos(maxX, minY, maxZ).endVertex(); + buffer.pos(maxX, maxY, maxZ).endVertex(); + buffer.pos(minX, minY, maxZ).endVertex(); + buffer.pos(minX, maxY, maxZ).endVertex(); + tessellator.draw(); - if (Baritone.settings().renderGoalIgnoreDepth.get()) { - GlStateManager.enableDepth(); - } - GlStateManager.depthMask(true); - GlStateManager.enableTexture2D(); - GlStateManager.disableBlend(); + IRenderer.endLines(settings.renderGoalIgnoreDepth.value); } private static void renderHorizontalQuad(double minX, double maxX, double minZ, double maxZ, double y) { if (y != 0) { - BUFFER.begin(GL_LINE_LOOP, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, y, minZ).endVertex(); - BUFFER.pos(maxX, y, minZ).endVertex(); - BUFFER.pos(maxX, y, maxZ).endVertex(); - BUFFER.pos(minX, y, maxZ).endVertex(); - TESSELLATOR.draw(); + buffer.begin(GL_LINE_LOOP, DefaultVertexFormats.POSITION); + buffer.pos(minX, y, minZ).endVertex(); + buffer.pos(maxX, y, minZ).endVertex(); + buffer.pos(maxX, y, maxZ).endVertex(); + buffer.pos(minX, y, maxZ).endVertex(); + tessellator.draw(); } } } diff --git a/src/main/java/baritone/utils/PathingCommandContext.java b/src/main/java/baritone/utils/PathingCommandContext.java new file mode 100644 index 00000000..c43166e9 --- /dev/null +++ b/src/main/java/baritone/utils/PathingCommandContext.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.utils; + +import baritone.api.pathing.goals.Goal; +import baritone.api.process.PathingCommand; +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) { + super(goal, commandType); + this.desiredCalcContext = context; + } +} diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index e748042e..a83e53a1 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -24,16 +24,18 @@ import baritone.api.pathing.calc.IPathingControlManager; import baritone.api.pathing.goals.Goal; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; import baritone.behavior.PathingBehavior; import baritone.pathing.path.PathExecutor; import net.minecraft.util.math.BlockPos; import java.util.*; -import java.util.stream.Stream; public class PathingControlManager implements IPathingControlManager { + private final Baritone baritone; private final HashSet processes; // unGh + private final List active; private IBaritoneProcess inControlLastTick; private IBaritoneProcess inControlThisTick; private PathingCommand command; @@ -41,13 +43,13 @@ public class PathingControlManager implements IPathingControlManager { public PathingControlManager(Baritone baritone) { this.baritone = baritone; this.processes = new HashSet<>(); + this.active = new ArrayList<>(); baritone.getGameEventHandler().registerEventListener(new AbstractGameEventListener() { // needs to be after all behavior ticks @Override public void onTick(TickEvent event) { - if (event.getType() == TickEvent.Type.OUT) { - return; + if (event.getType() == TickEvent.Type.IN) { + postTick(); } - postTick(); } }); } @@ -62,6 +64,7 @@ public class PathingControlManager implements IPathingControlManager { inControlLastTick = null; inControlThisTick = null; command = null; + active.clear(); for (IBaritoneProcess proc : processes) { proc.onLostControl(); if (proc.isActive() && !proc.isTemporary()) { // it's okay only for a temporary thing (like combat pause) to maintain control even if you say to cancel @@ -82,12 +85,19 @@ public class PathingControlManager implements IPathingControlManager { public void preTick() { inControlLastTick = inControlThisTick; + inControlThisTick = null; PathingBehavior p = baritone.getPathingBehavior(); command = executeProcesses(); if (command == null) { p.cancelSegmentIfSafe(); + p.secretInternalSetGoal(null); return; } + if (!Objects.equals(inControlThisTick, inControlLastTick) && command.commandType != PathingCommandType.REQUEST_PAUSE && inControlLastTick != null && !inControlLastTick.isTemporary()) { + // if control has changed from a real process to another real process, and the new process wants to do something + p.cancelSegmentIfSafe(); + // get rid of the in progress stuff from the last process + } switch (command.commandType) { case REQUEST_PAUSE: p.requestPause(); @@ -98,18 +108,18 @@ public class PathingControlManager implements IPathingControlManager { break; case FORCE_REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); } break; case REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); } break; case SET_GOAL_AND_PATH: // now this i can do if (command.goal != null) { - baritone.getPathingBehavior().secretInternalSetGoalAndPath(command.goal); + baritone.getPathingBehavior().secretInternalSetGoalAndPath(command); } break; default: @@ -117,7 +127,7 @@ public class PathingControlManager implements IPathingControlManager { } } - public void postTick() { + private void postTick() { // if we did this in pretick, it would suck // we use the time between ticks as calculation time // therefore, we only cancel and recalculate after the tick for the current path has executed @@ -132,13 +142,13 @@ public class PathingControlManager implements IPathingControlManager { // pwnage p.softCancelIfSafe(); } - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); break; case REVALIDATE_GOAL_AND_PATH: - if (Baritone.settings().cancelOnGoalInvalidation.get() && (command.goal == null || revalidateGoal(command.goal))) { + if (Baritone.settings().cancelOnGoalInvalidation.value && (command.goal == null || revalidateGoal(command.goal))) { p.softCancelIfSafe(); } - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); break; default: } @@ -171,22 +181,30 @@ public class PathingControlManager implements IPathingControlManager { public PathingCommand executeProcesses() { - Stream inContention = processes.stream() - .filter(IBaritoneProcess::isActive) - .sorted(Comparator.comparingDouble(IBaritoneProcess::priority).reversed()); + for (IBaritoneProcess process : processes) { + if (process.isActive()) { + if (!active.contains(process)) { + // put a newly active process at the very front of the queue + active.add(0, process); + } + } else { + active.remove(process); + } + } + // ties are broken by which was added to the beginning of the list first + active.sort(Comparator.comparingDouble(IBaritoneProcess::priority).reversed()); - - Iterator iterator = inContention.iterator(); + Iterator iterator = active.iterator(); while (iterator.hasNext()) { IBaritoneProcess proc = iterator.next(); PathingCommand exec = proc.onTick(Objects.equals(proc, inControlLastTick) && baritone.getPathingBehavior().calcFailedLastTick(), baritone.getPathingBehavior().isSafeToCancel()); if (exec == null) { if (proc.isActive()) { - throw new IllegalStateException(proc.displayName() + " returned null PathingCommand"); + throw new IllegalStateException(proc.displayName() + " actively returned null PathingCommand"); } - proc.onLostControl(); - } else { + // no need to call onLostControl; they are reporting inactive. + } else if (exec.commandType != PathingCommandType.DEFER) { inControlThisTick = proc; if (!proc.isTemporary()) { iterator.forEachRemaining(IBaritoneProcess::onLostControl); diff --git a/src/main/java/baritone/utils/PlayerMovementInput.java b/src/main/java/baritone/utils/PlayerMovementInput.java index b2111a54..22e2063a 100644 --- a/src/main/java/baritone/utils/PlayerMovementInput.java +++ b/src/main/java/baritone/utils/PlayerMovementInput.java @@ -17,15 +17,14 @@ package baritone.utils; -import baritone.api.utils.IInputOverrideHandler; import baritone.api.utils.input.Input; import net.minecraft.util.MovementInput; public class PlayerMovementInput extends MovementInput { - private final IInputOverrideHandler handler; + private final InputOverrideHandler handler; - public PlayerMovementInput(IInputOverrideHandler handler) { + PlayerMovementInput(InputOverrideHandler handler) { this.handler = handler; } @@ -33,7 +32,7 @@ public class PlayerMovementInput extends MovementInput { this.moveStrafe = 0.0F; this.moveForward = 0.0F; - jump = handler.isInputForcedDown(Input.JUMP); // oppa + jump = handler.isInputForcedDown(Input.JUMP); // oppa gangnam style if (this.forwardKeyDown = handler.isInputForcedDown(Input.MOVE_FORWARD)) { this.moveForward++; diff --git a/src/main/java/baritone/utils/ToolSet.java b/src/main/java/baritone/utils/ToolSet.java index fa71a6b0..90f06bb0 100644 --- a/src/main/java/baritone/utils/ToolSet.java +++ b/src/main/java/baritone/utils/ToolSet.java @@ -38,6 +38,7 @@ import java.util.function.Function; * @author Avery, Brady, leijurv */ public class ToolSet { + /** * A cache mapping a {@link Block} to how long it will take to break * with this toolset, given the optimum tool is used. @@ -55,7 +56,7 @@ public class ToolSet { breakStrengthCache = new HashMap<>(); this.player = player; - if (Baritone.settings().considerPotionEffects.get()) { + if (Baritone.settings().considerPotionEffects.value) { double amplifier = potionAmplifier(); Function amplify = x -> amplifier * x; backendCalculation = amplify.compose(this::getBestDestructionTime); @@ -65,10 +66,10 @@ public class ToolSet { } /** - * Using the best tool on the hotbar, how long would it take to mine this block + * Using the best tool on the hotbar, how fast we can mine this block * * @param state the blockstate to be mined - * @return how long it would take in ticks + * @return the speed of how fast we'll mine it. 1/(time in ticks) */ public double getStrVsBlock(IBlockState state) { return breakStrengthCache.computeIfAbsent(state.getBlock(), backendCalculation); @@ -90,30 +91,39 @@ public class ToolSet { } } + 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; } } } @@ -127,14 +137,19 @@ 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)); - return calculateSpeedVsBlock(stack, b.getDefaultState()); + ItemStack stack = player.inventory.getStackInSlot(getBestSlot(b, false)); + return calculateSpeedVsBlock(stack, b.getDefaultState()) * avoidanceMultiplier(b); + } + + private double avoidanceMultiplier(Block b) { + return Baritone.settings().blocksToAvoidBreaking.value.contains(b) ? 0.1 : 1; } /** * Calculates how long would it take to mine the specified block given the best tool * in this toolset is used. A negative value is returned if the specified block is unbreakable. * + * @param item the item to mine it with * @param state the blockstate to be mined * @return how long it would take in ticks */ diff --git a/src/main/java/baritone/utils/accessor/IBitArray.java b/src/main/java/baritone/utils/accessor/IBitArray.java new file mode 100644 index 00000000..81501036 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IBitArray.java @@ -0,0 +1,8 @@ +package baritone.utils.accessor; + +public interface IBitArray { + + int getAtFast(int index); + + int[] toArray(); +} diff --git a/src/main/java/baritone/utils/accessor/IBlockStateContainer.java b/src/main/java/baritone/utils/accessor/IBlockStateContainer.java new file mode 100644 index 00000000..7c47a9b8 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IBlockStateContainer.java @@ -0,0 +1,18 @@ +package baritone.utils.accessor; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BitArray; +import net.minecraft.world.chunk.IBlockStatePalette; + +public interface IBlockStateContainer { + + IBlockStatePalette getPalette(); + + BitArray getStorage(); + + IBlockState getFast(int index); + + IBlockState getAtPalette(int index); + + int[] storageArray(); +} diff --git a/src/main/java/baritone/utils/accessor/IChunkProviderClient.java b/src/main/java/baritone/utils/accessor/IChunkProviderClient.java index 19f14685..e5fde40b 100644 --- a/src/main/java/baritone/utils/accessor/IChunkProviderClient.java +++ b/src/main/java/baritone/utils/accessor/IChunkProviderClient.java @@ -21,5 +21,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import net.minecraft.world.chunk.Chunk; public interface IChunkProviderClient { + Long2ObjectMap loadedChunks(); } diff --git a/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java b/src/main/java/baritone/utils/accessor/IPlayerControllerMP.java new file mode 100644 index 00000000..354b9eef --- /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 00000000..e187c3e6 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/ITabCompleter.java @@ -0,0 +1,10 @@ +package baritone.utils.accessor; + +public interface ITabCompleter { + + String getPrefix(); + + void setPrefix(String prefix); + + boolean onGuiChatSetCompletions(String[] newCompl); +} diff --git a/src/main/java/baritone/utils/command/BaritoneChatControl.java b/src/main/java/baritone/utils/command/BaritoneChatControl.java new file mode 100644 index 00000000..70e2370e --- /dev/null +++ b/src/main/java/baritone/utils/command/BaritoneChatControl.java @@ -0,0 +1,199 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.ICommandArgument; +import baritone.api.utils.command.exception.CommandNotEnoughArgumentsException; +import baritone.api.utils.command.exception.CommandNotFoundException; +import baritone.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.manager.ICommandManager; +import baritone.utils.command.argument.CommandArguments; +import baritone.utils.command.manager.CommandManager; +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(CommandManager.expand(commandStr).getFirst()).handle(null, null); + } + } else if ((settings.chatControl.value || settings.chatControlAnyway.value) && runCommand(msg)) { + event.cancel(); + } + } + + private void logRanCommand(String command, String rest) { + if (settings.echoCommands.value) { + String msg = command + rest; + String toDisplay = settings.censorRanCommands.value ? command + " ..." : msg; + ITextComponent component = new TextComponentString(String.format("> %s", toDisplay)); + component.getStyle() + .setColor(TextFormatting.WHITE) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to rerun command") + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + msg + )); + logDirect(component); + } + } + + public boolean runCommand(String msg) { + if (msg.trim().equalsIgnoreCase("damn")) { + logDirect("daniel"); + return false; + } else if (msg.trim().equalsIgnoreCase("orderpizza")) { + try { + ((IGuiScreen) mc.currentScreen).openLink(new URI("https://www.dominos.com/en/pages/order/")); + } catch (NullPointerException | URISyntaxException ignored) {} + return false; + } + if (msg.isEmpty()) { + return this.runCommand("help"); + } + Tuple> pair = CommandManager.expand(msg); + String command = pair.getFirst(); + String rest = msg.substring(pair.getFirst().length()); + ArgConsumer argc = new ArgConsumer(this.manager, pair.getSecond()); + if (!argc.hasAny()) { + Settings.Setting setting = settings.byLowerName.get(command.toLowerCase(Locale.US)); + if (setting != null) { + logRanCommand(command, rest); + if (setting.getValueClass() == Boolean.class) { + this.manager.execute(String.format("set toggle %s", setting.getName())); + } else { + this.manager.execute(String.format("set %s", setting.getName())); + } + return true; + } + } else if (argc.hasExactlyOne()) { + for (Settings.Setting setting : settings.allSettings) { + if (setting.getName().equals("logger")) { + continue; + } + if (setting.getName().equalsIgnoreCase(pair.getFirst())) { + logRanCommand(command, rest); + try { + this.manager.execute(String.format("set %s %s", setting.getName(), argc.getString())); + } catch (CommandNotEnoughArgumentsException ignored) {} // The operation is safe + return true; + } + } + } + + // If the command exists, then handle echoing the input + if (this.manager.getCommand(pair.getFirst()) != null) { + logRanCommand(command, rest); + } + + return this.manager.execute(pair); + } + + @Override + public void onPreTabComplete(TabCompleteEvent.Pre event) { + if (!settings.prefixControl.value) { + return; + } + String prefix = event.prefix.get(); + String commandPrefix = settings.prefix.value; + if (!prefix.startsWith(commandPrefix)) { + return; + } + String msg = prefix.substring(commandPrefix.length()); + List args = CommandArguments.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 = CommandArguments.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/argument/CommandArgument.java b/src/main/java/baritone/utils/command/argument/CommandArgument.java new file mode 100644 index 00000000..9c374248 --- /dev/null +++ b/src/main/java/baritone/utils/command/argument/CommandArgument.java @@ -0,0 +1,97 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.argument; + +import baritone.api.utils.command.argparser.ArgParserManager; +import baritone.api.utils.command.argument.ICommandArgument; +import baritone.api.utils.command.exception.CommandInvalidArgumentException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; + +import java.util.stream.Stream; + +/** + * The default implementation of {@link ICommandArgument} + * + * @author LoganDark + */ +class CommandArgument implements ICommandArgument { + + private final int index; + private final String value; + private final String rawRest; + + CommandArgument(int index, String value, String rawRest) { + this.index = index; + this.value = value; + this.rawRest = rawRest; + } + + @Override + public int getIndex() { + return this.index; + } + + @Override + public String getValue() { + return this.value; + } + + @Override + public String getRawRest() { + return this.rawRest; + } + + @Override + 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())); + } + + @Override + public T getAs(Class type) throws CommandInvalidTypeException { + return ArgParserManager.parseStateless(type, this); + } + + @Override + public boolean is(Class type) { + try { + getAs(type); + return true; + } catch (Throwable t) { + return false; + } + } + + @SuppressWarnings("UnusedReturnValue") + @Override + public T getAs(Class type, Class stateType, S state) throws CommandInvalidTypeException { + return ArgParserManager.parseStated(type, stateType, this, state); + } + + @Override + public boolean is(Class type, Class stateType, S state) { + try { + getAs(type, stateType, state); + return true; + } catch (Throwable t) { + return false; + } + } +} diff --git a/src/main/java/baritone/utils/command/argument/CommandArguments.java b/src/main/java/baritone/utils/command/argument/CommandArguments.java new file mode 100644 index 00000000..22f89559 --- /dev/null +++ b/src/main/java/baritone/utils/command/argument/CommandArguments.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.utils.command.argument; + +import baritone.api.utils.command.argument.ICommandArgument; +import baritone.api.utils.command.exception.CommandInvalidArgumentException; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author LoganDark + */ +public final class CommandArguments { + + private CommandArguments() {} + + private static final Pattern ARG_PATTERN = Pattern.compile("\\S+"); + + /** + * Turn a string into a list of {@link ICommandArgument}s. This is needed because of {@link ICommandArgument#getRawRest()} + * + * @param string The string to convert + * @param preserveEmptyLast If the string ends with whitespace, add an empty {@link ICommandArgument} to the end This + * is useful for tab completion + * @return A list of {@link ICommandArgument}s + */ + public static List from(String string, boolean preserveEmptyLast) { + List args = new ArrayList<>(); + Matcher argMatcher = ARG_PATTERN.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/main/java/baritone/utils/command/defaults/AxisCommand.java b/src/main/java/baritone/utils/command/defaults/AxisCommand.java new file mode 100644 index 00000000..f67c82e5 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + Goal goal = new GoalAxis(); + baritone.getCustomGoalProcess().setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Set a goal to the axes"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The axis command sets a goal that tells Baritone to head towards the nearest axis. That is, X=0 or Z=0.", + "", + "Usage:", + "> axis" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/BlacklistCommand.java b/src/main/java/baritone/utils/command/defaults/BlacklistCommand.java new file mode 100644 index 00000000..accda72b --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Blacklist closest block"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "While going to a block this command blacklists the closest block so that Baritone won't attempt to get to it.", + "", + "Usage:", + "> blacklist" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/BuildCommand.java b/src/main/java/baritone/utils/command/defaults/BuildCommand.java new file mode 100644 index 00000000..b852d0be --- /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.IArgConsumer; +import net.minecraft.client.Minecraft; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +public class BuildCommand extends Command { + + private static final File schematicsDir = new File(Minecraft.getMinecraft().gameDir, "schematics"); + + public BuildCommand(IBaritone baritone) { + super(baritone, "build"); + } + + @Override + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return RelativeFile.tabComplete(args, schematicsDir); + } else if (args.has(2)) { + args.get(); + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Build a schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Build a schematic from a file.", + "", + "Usage:", + "> build - Loads and builds '.schematic'", + "> build - Custom position" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/CancelCommand.java b/src/main/java/baritone/utils/command/defaults/CancelCommand.java new file mode 100644 index 00000000..868da5e0 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getPathingBehavior().cancelEverything(); + logDirect("ok canceled"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Cancel what Baritone is currently doing"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The cancel command tells Baritone to stop whatever it's currently doing.", + "", + "Usage:", + "> cancel" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ChestsCommand.java b/src/main/java/baritone/utils/command/defaults/ChestsCommand.java new file mode 100644 index 00000000..130d5909 --- /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.IArgConsumer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +public class ChestsCommand extends Command { + + public ChestsCommand(IBaritone baritone) { + super(baritone, "chests"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + Set> 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 + public Stream tabComplete(String label, IArgConsumer 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 00000000..ba8e917c --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.openClick(); + logDirect("aight dude"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Open click"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Opens click dude", + "", + "Usage:", + "> click" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ComeCommand.java b/src/main/java/baritone/utils/command/defaults/ComeCommand.java new file mode 100644 index 00000000..cc4f4071 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Start heading towards your camera"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The come command tells Baritone to head towards your camera.", + "", + "This can be useful in hacked clients where freecam doesn't move your player position.", + "", + "Usage:", + "> come" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/CommandAlias.java b/src/main/java/baritone/utils/command/defaults/CommandAlias.java new file mode 100644 index 00000000..6aa32af1 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) { + this.baritone.getCommandManager().execute(String.format("%s %s", target, args.rawRest())); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return this.baritone.getCommandManager().tabComplete(String.format("%s %s", target, args.rawRest())); + } + + @Override + public String getShortDesc() { + return shortDesc; + } + + @Override + public List getLongDesc() { + return Collections.singletonList(String.format("This command is an alias, for: %s ...", target)); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/DefaultCommands.java b/src/main/java/baritone/utils/command/defaults/DefaultCommands.java new file mode 100644 index 00000000..3f8e3908 --- /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 00000000..15a8e241 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + if (args.hasAtMost(2)) { + return args.tabCompleteDatatype(RelativeGoalXZ.INSTANCE); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Explore things"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Tell Baritone to explore randomly. If you used explorefilter before this, it will be applied.", + "", + "Usage:", + "> explore - Explore from your current position.", + "> explore - Explore from the specified X and Z position." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ExploreFilterCommand.java b/src/main/java/baritone/utils/command/defaults/ExploreFilterCommand.java new file mode 100644 index 00000000..6f60a55d --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return RelativeFile.tabComplete(args, RelativeFile.gameDir()); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Explore chunks from a json"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Apply an explore filter before using explore, which tells the explore process which chunks have been explored/not explored.", + "", + "The JSON file will follow this format: [{\"x\":0,\"z\":0},...]", + "", + "If 'invert' is specified, the chunks listed will be considered NOT explored, rather than explored.", + "", + "Usage:", + "> explorefilter [invert] - Load the JSON file referenced by the specified path. If invert is specified, it must be the literal word 'invert'." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/FarmCommand.java b/src/main/java/baritone/utils/command/defaults/FarmCommand.java new file mode 100644 index 00000000..0ece390d --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getFarmProcess().farm(); + logDirect("Farming"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Farm nearby crops"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The farm command starts farming nearby plants. It harvests mature crops and plants new ones.", + "", + "Usage:", + "> farm" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/FindCommand.java b/src/main/java/baritone/utils/command/defaults/FindCommand.java new file mode 100644 index 00000000..62a49e02 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FindCommand.java @@ -0,0 +1,80 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.BlockById; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.IArgConsumer; +import net.minecraft.block.Block; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class FindCommand extends Command { + + public FindCommand(IBaritone baritone) { + super(baritone, "find"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + List toFind = new ArrayList<>(); + while (args.hasAny()) { + toFind.add(args.getDatatypeFor(BlockById.INSTANCE)); + } + BetterBlockPos origin = ctx.playerFeet(); + toFind.stream() + .flatMap(block -> + ctx.worldData().getCachedWorld().getLocationsOf( + Block.REGISTRY.getNameForObject(block).getPath(), + Integer.MAX_VALUE, + origin.x, + origin.y, + 4 + ).stream() + ) + .map(BetterBlockPos::new) + .map(BetterBlockPos::toString) + .forEach(this::logDirect); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return args.tabCompleteDatatype(BlockById.INSTANCE); + } + + @Override + public String getShortDesc() { + return "Find positions of a certain block"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "", + "", + "Usage:", + "> " + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/FollowCommand.java b/src/main/java/baritone/utils/command/defaults/FollowCommand.java new file mode 100644 index 00000000..b1cb5a80 --- /dev/null +++ b/src/main/java/baritone/utils/command/defaults/FollowCommand.java @@ -0,0 +1,155 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.EntityClassById; +import baritone.api.utils.command.datatypes.IDatatypeFor; +import baritone.api.utils.command.datatypes.NearbyPlayer; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.helpers.arguments.IArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FollowCommand extends Command { + + public FollowCommand(IBaritone baritone) { + super(baritone, "follow"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMin(1); + FollowGroup group; + FollowList list; + List entities = new ArrayList<>(); + List> classes = new ArrayList<>(); + if (args.hasExactlyOne()) { + baritone.getFollowProcess().follow((group = args.getEnum(FollowGroup.class)).filter); + } else { + args.requireMin(2); + group = null; + list = args.getEnum(FollowList.class); + while (args.hasAny()) { + Object gotten = args.getDatatypeFor(list.datatype); + if (gotten instanceof Class) { + //noinspection unchecked + classes.add((Class) gotten); + } else { + entities.add((Entity) gotten); + } + } + baritone.getFollowProcess().follow( + classes.isEmpty() + ? entities::contains + : e -> classes.stream().anyMatch(c -> c.isInstance(e)) + ); + } + if (group != null) { + logDirect(String.format("Following all %s", group.name().toLowerCase(Locale.US))); + } else { + logDirect("Following these types of entities:"); + if (classes.isEmpty()) { + entities.stream() + .map(Entity::toString) + .forEach(this::logDirect); + } else { + classes.stream() + .map(EntityList::getKey) + .map(Objects::requireNonNull) + .map(ResourceLocation::toString) + .forEach(this::logDirect); + } + } + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(FollowGroup.class) + .append(FollowList.class) + .filterPrefix(args.getString()) + .stream(); + } else { + IDatatypeFor followType; + try { + followType = args.getEnum(FollowList.class).datatype; + } catch (NullPointerException e) { + return Stream.empty(); + } + while (args.has(2)) { + if (args.peekDatatypeOrNull(followType) == null) { + return Stream.empty(); + } + args.get(); + } + return args.tabCompleteDatatype(followType); + } + } + + @Override + public String getShortDesc() { + return "Follow entity things"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The follow command tells Baritone to follow certain kinds of entities.", + "", + "Usage:", + "> follow entities - Follows all entities.", + "> follow entity <...> - Follow certain entities (for example 'skeleton', 'horse' etc.)", + "> follow players - Follow players", + "> follow player <...> - Follow certain players" + ); + } + + private enum FollowGroup { + ENTITIES(EntityLiving.class::isInstance), + PLAYERS(EntityPlayer.class::isInstance); /* , + FRIENDLY(entity -> entity.getAttackTarget() != HELPER.mc.player), + HOSTILE(FRIENDLY.filter.negate()); */ + final Predicate filter; + + FollowGroup(Predicate filter) { + this.filter = filter; + } + } + + private enum FollowList { + ENTITY(EntityClassById.INSTANCE), + PLAYER(NearbyPlayer.INSTANCE); + + final IDatatypeFor datatype; + + FollowList(IDatatypeFor datatype) { + this.datatype = datatype; + } + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ForceCancelCommand.java b/src/main/java/baritone/utils/command/defaults/ForceCancelCommand.java new file mode 100644 index 00000000..7a020b14 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); + pathingBehavior.cancelEverything(); + pathingBehavior.forceCancel(); + logDirect("ok force canceled"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Force cancel"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Like cancel, but more forceful.", + "", + "Usage:", + "> forcecancel" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/GcCommand.java b/src/main/java/baritone/utils/command/defaults/GcCommand.java new file mode 100644 index 00000000..20b2e334 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + System.gc(); + logDirect("ok called System.gc()"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Call System.gc()"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Calls System.gc().", + "", + "Usage:", + "> gc" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/GoalCommand.java b/src/main/java/baritone/utils/command/defaults/GoalCommand.java new file mode 100644 index 00000000..f2692fb2 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + TabCompleteHelper helper = new TabCompleteHelper(); + if (args.hasExactlyOne()) { + helper.append("reset", "clear", "none", "~"); + } else { + if (args.hasAtMost(3)) { + while (args.has(2)) { + if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { + break; + } + args.get(); + if (!args.has(2)) { + helper.append("~"); + } + } + } + } + return helper.filterPrefix(args.getString()).stream(); + } + + @Override + public String getShortDesc() { + return "Set or clear the goal"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The goal command allows you to set or clear Baritone's goal.", + "", + "Wherever a coordinate is expected, you can use ~ just like in regular Minecraft commands. Or, you can just use regular numbers.", + "", + "Usage:", + "> goal - Set the goal to your current position", + "> goal - Erase the goal", + "> goal - Set the goal to a Y level", + "> goal - Set the goal to an X,Z position", + "> goal - Set the goal to an X,Y,Z position" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/HelpCommand.java b/src/main/java/baritone/utils/command/defaults/HelpCommand.java new file mode 100644 index 00000000..480207f1 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .addCommands(this.baritone.getCommandManager()) + .filterPrefix(args.getString()) + .stream(); + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View all commands or help on specific ones"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Using this command, you can view detailed help information on how to use certain commands of Baritone.", + "", + "Usage:", + "> help - Lists all commands and their short descriptions.", + "> help - Displays help information on a specific command." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/InvertCommand.java b/src/main/java/baritone/utils/command/defaults/InvertCommand.java new file mode 100644 index 00000000..9b5d0a16 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Run away from the current goal"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The invert command tells Baritone to head away from the current goal rather than towards it.", + "", + "Usage:", + "> invert - Invert the current goal." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/MineCommand.java b/src/main/java/baritone/utils/command/defaults/MineCommand.java new file mode 100644 index 00000000..2a91c6e6 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return args.tabCompleteDatatype(BlockById.INSTANCE); + } + + @Override + public String getShortDesc() { + return "Mine some blocks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The mine command allows you to tell Baritone to search for and mine individual blocks.", + "", + "The specified blocks can be ores (which are commonly cached), or any other block.", + "", + "Also see the legitMine settings (see #set l legitMine).", + "", + "Usage:", + "> mine diamond_ore - Mines all diamonds it can find.", + "> mine redstone_ore lit_redstone_ore - Mines redstone ore.", + "> mine log:0 - Mines only oak logs." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/PathCommand.java b/src/main/java/baritone/utils/command/defaults/PathCommand.java new file mode 100644 index 00000000..e52a93a4 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasAny() && !args.has(4)) { + while (args.has(2)) { + if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) { + break; + } + args.get(); + if (!args.has(2)) { + return new TabCompleteHelper() + .append("~") + .filterPrefix(args.getString()) + .stream(); + } + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Start heading towards a goal"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The path command tells Baritone to head towards the current goal.", + "", + "Usage:", + "> path - Start the pathing.", + "> path ", + "> path ", + "> path - Define the goal here" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/PauseResumeCommands.java b/src/main/java/baritone/utils/command/defaults/PauseResumeCommands.java new file mode 100644 index 00000000..08a0ae66 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + if (paused[0]) { + throw new CommandInvalidStateException("Already paused"); + } + paused[0] = true; + logDirect("Paused"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer 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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + if (!paused[0]) { + throw new CommandInvalidStateException("Not paused"); + } + paused[0] = false; + logDirect("Resumed"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer 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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + logDirect(String.format("Baritone is %spaused", paused[0] ? "" : "not ")); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Tells you if Baritone is paused"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The paused command tells you if Baritone is currently paused by use of the pause command.", + "", + "Usage:", + "> paused" + ); + } + }; + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ProcCommand.java b/src/main/java/baritone/utils/command/defaults/ProcCommand.java new file mode 100644 index 00000000..233c18a3 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View process state information"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The proc command provides miscellaneous information about the process currently controlling Baritone.", + "", + "You are not expected to understand this if you aren't familiar with how Baritone works.", + "", + "Usage:", + "> proc - View process information, if present" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ReloadAllCommand.java b/src/main/java/baritone/utils/command/defaults/ReloadAllCommand.java new file mode 100644 index 00000000..6128094c --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + ctx.worldData().getCachedWorld().reloadAllFromDisk(); + logDirect("Reloaded"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Reloads Baritone's cache for this world"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The reloadall command reloads Baritone's world cache.", + "", + "Usage:", + "> reloadall" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/RenderCommand.java b/src/main/java/baritone/utils/command/defaults/RenderCommand.java new file mode 100644 index 00000000..15473afa --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + BetterBlockPos origin = ctx.playerFeet(); + int renderDistance = (mc.gameSettings.renderDistanceChunks + 1) * 16; + mc.renderGlobal.markBlockRangeForRenderUpdate( + origin.x - renderDistance, + 0, + origin.z - renderDistance, + origin.x + renderDistance, + 255, + origin.z + renderDistance + ); + logDirect("Done"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Fix glitched chunks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The render command fixes glitched chunk rendering without having to reload all of them.", + "", + "Usage:", + "> render" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/RepackCommand.java b/src/main/java/baritone/utils/command/defaults/RepackCommand.java new file mode 100644 index 00000000..1f11802a --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + logDirect(String.format("Queued %d chunks for repacking", WorldScanner.INSTANCE.repack(ctx))); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Re-cache chunks"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Repack chunks around you. This basically re-caches them.", + "", + "Usage:", + "> repack - Repack chunks." + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SaveAllCommand.java b/src/main/java/baritone/utils/command/defaults/SaveAllCommand.java new file mode 100644 index 00000000..9c507f69 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + ctx.worldData().getCachedWorld().save(); + logDirect("Saved"); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Saves Baritone's cache for this world"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The saveall command saves Baritone's world cache.", + "", + "Usage:", + "> saveall" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SchematicaCommand.java b/src/main/java/baritone/utils/command/defaults/SchematicaCommand.java new file mode 100644 index 00000000..c531a48b --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer args) throws CommandException { + args.requireMax(0); + baritone.getBuilderProcess().buildOpenSchematic(); + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Builds the loaded schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Builds the schematica currently open in Schematica.", + "", + "Usage:", + "> schematica" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SelCommand.java b/src/main/java/baritone/utils/command/defaults/SelCommand.java new file mode 100644 index 00000000..83fc77e3 --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(Action.getAllNames()) + .filterPrefix(args.getString()) + .sortAlphabetically() + .stream(); + } else { + Action action = Action.getByName(args.getString()); + if (action != null) { + if (action == Action.POS1 || action == Action.POS2) { + if (args.hasAtMost(3)) { + return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); + } + } else if (action == Action.SET || action == Action.WALLS || action == Action.CLEARAREA || action == Action.REPLACE) { + if (args.hasExactlyOne() || action == Action.REPLACE) { + while (args.has(2)) { + args.get(); + } + return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); + } + } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(TransformTarget.getAllNames()) + .filterPrefix(args.getString()) + .sortAlphabetically() + .stream(); + } else { + TransformTarget target = TransformTarget.getByName(args.getString()); + if (target != null && args.hasExactlyOne()) { + return args.tabCompleteDatatype(ForEnumFacing.INSTANCE); + } + } + } + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "WorldEdit-like commands"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The sel command allows you to manipulate Baritone's selections, similarly to WorldEdit.", + "", + "Using these selections, you can clear areas, fill them with blocks, or something else.", + "", + "The expand/contract/shift commands use a kind of selector to choose which selections to target. Supported ones are a/all, n/newest, and o/oldest.", + "", + "Usage:", + "> sel pos1/p1/1 - Set position 1 to your current position.", + "> sel pos1/p1/1 - Set position 1 to a relative position.", + "> sel pos2/p2/2 - Set position 2 to your current position.", + "> sel pos2/p2/2 - Set position 2 to a relative position.", + "", + "> sel clear/c - Clear the selection.", + "> sel undo/u - Undo the last action (setting positions, creating selections, etc.)", + "> sel set/fill/s/f [block] - Completely fill all selections with a block.", + "> sel walls/w [block] - Fill in the walls of the selection with a specified block.", + "> sel shell/shl [block] - The same as walls, but fills in a ceiling and floor too.", + "> sel cleararea/ca - Basically 'set air'.", + "> sel replace/r - Replaces blocks with another block.", + "", + "> sel expand - Expand the targets.", + "> sel contract - Contract the targets.", + "> sel shift - Shift the targets (does not resize)." + ); + } + + enum Action { + POS1("pos1", "p1", "1"), + POS2("pos2", "p2", "2"), + CLEAR("clear", "c"), + UNDO("undo", "u"), + SET("set", "fill", "s", "f"), + WALLS("walls", "w"), + SHELL("shell", "shl"), + CLEARAREA("cleararea", "ca"), + REPLACE("replace", "r"), + EXPAND("expand", "ex"), + CONTRACT("contract", "ct"), + SHIFT("shift", "sh"); + private final String[] names; + + Action(String... names) { + this.names = names; + } + + public static Action getByName(String name) { + for (Action action : Action.values()) { + for (String alias : action.names) { + if (alias.equalsIgnoreCase(name)) { + return action; + } + } + } + return null; + } + + public static String[] getAllNames() { + Set names = new HashSet<>(); + for (Action action : Action.values()) { + names.addAll(Arrays.asList(action.names)); + } + return names.toArray(new String[0]); + } + } + + enum TransformTarget { + ALL(sels -> sels, "all", "a"), + NEWEST(sels -> new ISelection[]{sels[sels.length - 1]}, "newest", "n"), + OLDEST(sels -> new ISelection[]{sels[0]}, "oldest", "o"); + private final Function transform; + private final String[] names; + + TransformTarget(Function transform, String... names) { + this.transform = transform; + this.names = names; + } + + public ISelection[] transform(ISelection[] selections) { + return transform.apply(selections); + } + + public static TransformTarget getByName(String name) { + for (TransformTarget target : TransformTarget.values()) { + for (String alias : target.names) { + if (alias.equalsIgnoreCase(name)) { + return target; + } + } + } + return null; + } + + public static String[] getAllNames() { + Set names = new HashSet<>(); + for (TransformTarget target : TransformTarget.values()) { + names.addAll(Arrays.asList(target.names)); + } + return names.toArray(new String[0]); + } + } +} diff --git a/src/main/java/baritone/utils/command/defaults/SetCommand.java b/src/main/java/baritone/utils/command/defaults/SetCommand.java new file mode 100644 index 00000000..be07a7ae --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + if (args.hasAny()) { + String arg = args.getString(); + if (args.hasExactlyOne() && !Arrays.asList("s", "save").contains(args.peekString().toLowerCase(Locale.US))) { + if (arg.equalsIgnoreCase("reset")) { + return new TabCompleteHelper() + .addModifiedSettings() + .prepend("all") + .filterPrefix(args.getString()) + .stream(); + } else if (arg.equalsIgnoreCase("toggle")) { + return new TabCompleteHelper() + .addToggleableSettings() + .filterPrefix(args.getString()) + .stream(); + } + Settings.Setting setting = Baritone.settings().byLowerName.get(arg.toLowerCase(Locale.US)); + if (setting != null) { + if (setting.getType() == Boolean.class) { + TabCompleteHelper helper = new TabCompleteHelper(); + if ((Boolean) setting.value) { + helper.append("true", "false"); + } else { + helper.append("false", "true"); + } + return helper.filterPrefix(args.getString()).stream(); + } else { + return Stream.of(settingValueToString(setting)); + } + } + } else if (!args.hasAny()) { + return new TabCompleteHelper() + .addSettings() + .sortAlphabetically() + .prepend("list", "modified", "reset", "toggle", "save") + .filterPrefix(arg) + .stream(); + } + } + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View or change settings"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Using the set command, you can manage all of Baritone's settings. Almost every aspect is controlled by these settings - go wild!", + "", + "Usage:", + "> set - Same as `set list`", + "> set list [page] - View all settings", + "> set modified [page] - View modified settings", + "> set - View the current value of a setting", + "> set - Set the value of a setting", + "> set reset all - Reset ALL SETTINGS to their defaults", + "> set reset - Reset a setting to its default", + "> set toggle - Toggle a boolean setting", + "> set save - Save all settings (this is automatic tho)" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/ThisWayCommand.java b/src/main/java/baritone/utils/command/defaults/ThisWayCommand.java new file mode 100644 index 00000000..bef3a513 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Travel in your current direction"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Creates a GoalXZ some amount of blocks in the direction you're currently looking", + "", + "Usage:", + "> thisway - makes a GoalXZ distance blocks in front of you" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/TunnelCommand.java b/src/main/java/baritone/utils/command/defaults/TunnelCommand.java new file mode 100644 index 00000000..1b9066f9 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Set a goal to tunnel in your current direction"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The tunnel command sets a goal that tells Baritone to mine completely straight in the direction that you're facing.", + "", + "Usage:", + "> tunnel" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/VersionCommand.java b/src/main/java/baritone/utils/command/defaults/VersionCommand.java new file mode 100644 index 00000000..4c9aa850 --- /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.IArgConsumer; + +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "View the Baritone version"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The version command prints the version of Baritone you're currently running.", + "", + "Usage:", + "> version - View version information, if present" + ); + } +} diff --git a/src/main/java/baritone/utils/command/defaults/WaypointsCommand.java b/src/main/java/baritone/utils/command/defaults/WaypointsCommand.java new file mode 100644 index 00000000..bda39e2a --- /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.IArgConsumer; +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 + public void execute(String label, IArgConsumer 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 + public Stream tabComplete(String label, IArgConsumer 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/helpers/arguments/ArgConsumer.java b/src/main/java/baritone/utils/command/helpers/arguments/ArgConsumer.java new file mode 100644 index 00000000..06aaa6eb --- /dev/null +++ b/src/main/java/baritone/utils/command/helpers/arguments/ArgConsumer.java @@ -0,0 +1,444 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.helpers.arguments; + +import baritone.api.IBaritone; +import baritone.api.utils.command.argument.ICommandArgument; +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.helpers.arguments.IArgConsumer; +import baritone.api.utils.command.manager.ICommandManager; +import baritone.utils.command.argument.CommandArguments; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +public class ArgConsumer implements IArgConsumer { + + /** + * The parent {@link ICommandManager} for this {@link IArgConsumer}}. Used by {@link #context}. + */ + private final ICommandManager manager; + + /** + * The {@link IDatatypeContext} instance for this {@link IArgConsumer}}, 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 + */ + private final LinkedList args; + + /** + * The list of consumed arguments for this ArgConsumer. The most recently consumed argument is the last one + */ + private 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<>()); + } + + @Override + public LinkedList getArgs() { + return this.args; + } + + @Override + public Deque getConsumed() { + return this.consumed; + } + + @Override + public boolean has(int num) { + return args.size() >= num; + } + + @Override + public boolean hasAny() { + return has(1); + } + + @Override + public boolean hasAtMost(int num) { + return args.size() <= num; + } + + @Override + public boolean hasAtMostOne() { + return hasAtMost(1); + } + + @Override + public boolean hasExactly(int num) { + return args.size() == num; + } + + @Override + public boolean hasExactlyOne() { + return hasExactly(1); + } + + @Override + public ICommandArgument peek(int index) throws CommandNotEnoughArgumentsException { + requireMin(index + 1); + return args.get(index); + } + + @Override + public ICommandArgument peek() throws CommandNotEnoughArgumentsException { + return peek(0); + } + + @Override + public boolean is(Class type, int index) throws CommandNotEnoughArgumentsException { + return peek(index).is(type); + } + + @Override + public boolean is(Class type) throws CommandNotEnoughArgumentsException { + return is(type, 0); + } + + @Override + public String peekString(int index) throws CommandNotEnoughArgumentsException { + return peek(index).getValue(); + } + + @Override + public String peekString() throws CommandNotEnoughArgumentsException { + return peekString(0); + } + + @Override + public > E peekEnum(Class enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peek(index).getEnum(enumClass); + } + + @Override + public > E peekEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peekEnum(enumClass, 0); + } + + @Override + public > E peekEnumOrNull(Class enumClass, int index) throws CommandNotEnoughArgumentsException { + try { + return peekEnum(enumClass, index); + } catch (CommandInvalidTypeException e) { + return null; + } + } + + @Override + public > E peekEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException { + return peekEnumOrNull(enumClass, 0); + } + + @Override + public T peekAs(Class type, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peek(index).getAs(type); + } + + @Override + public T peekAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return peekAs(type, 0); + } + + @Override + public T peekAsOrDefault(Class type, T def, int index) throws CommandNotEnoughArgumentsException { + try { + return peekAs(type, index); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + @Override + public T peekAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException { + return peekAsOrDefault(type, def, 0); + } + + @Override + public T peekAsOrNull(Class type, int index) throws CommandNotEnoughArgumentsException { + return peekAsOrDefault(type, null, index); + } + + @Override + public T peekAsOrNull(Class type) throws CommandNotEnoughArgumentsException { + return peekAsOrNull(type, 0); + } + + @Override + public T peekDatatype(IDatatypeFor datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypeFor(datatype); + } + + @Override + public T peekDatatype(IDatatypePost datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return this.peekDatatype(datatype, null); + } + + @Override + public T peekDatatype(IDatatypePost datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypePost(datatype, original); + } + + @Override + public T peekDatatypeOrNull(IDatatypeFor datatype) { + return copy().getDatatypeForOrNull(datatype); + } + + @Override + public T peekDatatypeOrNull(IDatatypePost datatype) { + return copy().getDatatypePostOrNull(datatype, null); + } + + @Override + public > T peekDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return copy().getDatatypePost(datatype, original); + } + + @Override + public > T peekDatatypePostOrDefault(D datatype, O original, T def) { + return copy().getDatatypePostOrDefault(datatype, original, def); + } + + @Override + public > T peekDatatypePostOrNull(D datatype, O original) { + return peekDatatypePostOrDefault(datatype, original, null); + } + + @Override + public > T peekDatatypeFor(Class datatype) { + return copy().peekDatatypeFor(datatype); + } + + @Override + public > T peekDatatypeForOrDefault(Class datatype, T def) { + return copy().peekDatatypeForOrDefault(datatype, def); + } + + @Override + public > T peekDatatypeForOrNull(Class datatype) { + return peekDatatypeForOrDefault(datatype, null); + } + + @Override + public ICommandArgument get() throws CommandNotEnoughArgumentsException { + requireMin(1); + ICommandArgument arg = args.removeFirst(); + consumed.add(arg); + return arg; + } + + @Override + public String getString() throws CommandNotEnoughArgumentsException { + return get().getValue(); + } + + @Override + public > E getEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return get().getEnum(enumClass); + } + + @Override + public > E getEnumOrDefault(Class enumClass, E def) throws CommandNotEnoughArgumentsException { + try { + peekEnum(enumClass); + return getEnum(enumClass); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + @Override + public > E getEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException { + return getEnumOrDefault(enumClass, null); + } + + @Override + public T getAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException { + return get().getAs(type); + } + + @Override + public T getAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException { + try { + T val = peek().getAs(type); + get(); + return val; + } catch (CommandInvalidTypeException e) { + return def; + } + } + + @Override + public T getAsOrNull(Class type) throws CommandNotEnoughArgumentsException { + return getAsOrDefault(type, null); + } + + @Override + 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()); + } + } + + @Override + 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; + } + } + + @Override + public > T getDatatypePostOrNull(D datatype, O original) { + return this.getDatatypePostOrDefault(datatype, original, null); + } + + @Override + 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()); + } + } + + @Override + 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; + } + } + + @Override + public > T getDatatypeForOrNull(D datatype) { + return this.getDatatypeForOrDefault(datatype, null); + } + + @Override + public Stream tabCompleteDatatype(T datatype) { + try { + return datatype.tabComplete(this.context); + } catch (Exception e) { + e.printStackTrace(); + } + return Stream.empty(); + } + + @Override + public String rawRest() { + return args.size() > 0 ? args.getFirst().getRawRest() : ""; + } + + @Override + public void requireMin(int min) throws CommandNotEnoughArgumentsException { + if (args.size() < min) { + throw new CommandNotEnoughArgumentsException(min + consumed.size()); + } + } + + @Override + public void requireMax(int max) throws CommandTooManyArgumentsException { + if (args.size() > max) { + throw new CommandTooManyArgumentsException(max + consumed.size()); + } + } + + @Override + public void requireExactly(int args) throws CommandException { + requireMin(args); + requireMax(args); + } + + @Override + public boolean hasConsumed() { + return !consumed.isEmpty(); + } + + @Override + public ICommandArgument consumed() { + return consumed.size() > 0 ? consumed.getLast() : CommandArguments.unknown(); + } + + @Override + public String consumedString() { + return consumed().getValue(); + } + + @Override + public ArgConsumer copy() { + return new ArgConsumer(manager, args, consumed); + } + + /** + * Implementation of {@link IDatatypeContext} which adapts to the parent {@link IArgConsumer}} + */ + 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/main/java/baritone/utils/command/manager/CommandManager.java b/src/main/java/baritone/utils/command/manager/CommandManager.java new file mode 100644 index 00000000..290b3852 --- /dev/null +++ b/src/main/java/baritone/utils/command/manager/CommandManager.java @@ -0,0 +1,159 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.ICommandArgument; +import baritone.api.utils.command.exception.CommandUnhandledException; +import baritone.api.utils.command.exception.ICommandException; +import baritone.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.argument.CommandArguments; +import baritone.utils.command.defaults.DefaultCommands; +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(expand(string)); + } + + @Override + public boolean execute(Tuple> expanded) { + ExecutionWrapper execution = this.from(expanded); + if (execution != null) { + execution.execute(); + } + return execution != null; + } + + @Override + public Stream tabComplete(Tuple> expanded) { + ExecutionWrapper execution = this.from(expanded); + return execution == null ? Stream.empty() : execution.tabComplete(); + } + + @Override + public Stream tabComplete(String prefix) { + Tuple> pair = expand(prefix, true); + String label = pair.getFirst(); + List args = pair.getSecond(); + if (args.isEmpty()) { + return new TabCompleteHelper() + .addCommands(this.baritone.getCommandManager()) + .filterPrefix(label) + .stream(); + } else { + return tabComplete(pair); + } + } + + private ExecutionWrapper from(Tuple> expanded) { + String label = expanded.getFirst(); + ArgConsumer args = new ArgConsumer(this, expanded.getSecond()); + + Command command = this.getCommand(label); + return command == null ? null : new ExecutionWrapper(command, label, args); + } + + private static Tuple> expand(String string, boolean preserveEmptyLast) { + String label = string.split("\\s", 2)[0]; + List args = CommandArguments.from(string.substring(label.length()), preserveEmptyLast); + return new Tuple<>(label, args); + } + + public static Tuple> expand(String string) { + return expand(string, false); + } + + private static final class ExecutionWrapper { + private Command command; + private String label; + private ArgConsumer args; + + private ExecutionWrapper(Command command, String label, ArgConsumer args) { + this.command = command; + this.label = label; + this.args = args; + } + + private void execute() { + try { + this.command.execute(this.label, this.args); + } catch (Throwable t) { + // Create a handleable exception, wrap if needed + ICommandException exception = t instanceof ICommandException + ? (ICommandException) t + : new CommandUnhandledException(t); + + exception.handle(command, args.getArgs()); + } + } + + private Stream tabComplete() { + try { + return this.command.tabComplete(this.label, this.args); + } catch (Throwable t) { + return Stream.empty(); + } + } + } +} diff --git a/src/main/java/baritone/utils/pathing/Avoidance.java b/src/main/java/baritone/utils/pathing/Avoidance.java index 1f61dc57..ca377fb0 100644 --- a/src/main/java/baritone/utils/pathing/Avoidance.java +++ b/src/main/java/baritone/utils/pathing/Avoidance.java @@ -21,7 +21,10 @@ import baritone.Baritone; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.IPlayerContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; +import net.minecraft.entity.monster.EntityEnderman; import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.EntityPigZombie; +import net.minecraft.entity.monster.EntitySpider; import net.minecraft.util.math.BlockPos; import java.util.ArrayList; @@ -29,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; @@ -57,17 +61,23 @@ public class Avoidance { } public static List create(IPlayerContext ctx) { - if (!Baritone.settings().avoidance.get()) { + if (!Baritone.settings().avoidance.value) { return Collections.emptyList(); } List res = new ArrayList<>(); - double mobSpawnerCoeff = Baritone.settings().mobSpawnerAvoidanceCoefficient.get(); - double mobCoeff = Baritone.settings().mobAvoidanceCoefficient.get(); + double mobSpawnerCoeff = Baritone.settings().mobSpawnerAvoidanceCoefficient.value; + double mobCoeff = Baritone.settings().mobAvoidanceCoefficient.value; if (mobSpawnerCoeff != 1.0D) { - ctx.worldData().getCachedWorld().getLocationsOf("mob_spawner", 1, ctx.playerFeet().x, ctx.playerFeet().z, 2).forEach(mobspawner -> res.add(new Avoidance(mobspawner, mobSpawnerCoeff, Baritone.settings().mobSpawnerAvoidanceRadius.get()))); + ctx.worldData().getCachedWorld().getLocationsOf("mob_spawner", 1, ctx.playerFeet().x, ctx.playerFeet().z, 2) + .forEach(mobspawner -> res.add(new Avoidance(mobspawner, mobSpawnerCoeff, Baritone.settings().mobSpawnerAvoidanceRadius.value))); } if (mobCoeff != 1.0D) { - ctx.world().loadedEntityList.stream().filter(entity -> entity instanceof EntityMob).forEach(entity -> res.add(new Avoidance(new BlockPos(entity), mobCoeff, Baritone.settings().mobAvoidanceRadius.get()))); + ctx.world().loadedEntityList.stream() + .filter(entity -> entity instanceof EntityMob) + .filter(entity -> (!(entity instanceof EntitySpider)) || ctx.player().getBrightness() < 0.5) + .filter(entity -> !(entity instanceof EntityPigZombie) || ((EntityPigZombie) entity).isAngry()) + .filter(entity -> !(entity instanceof EntityEnderman) || ((EntityEnderman) entity).isScreaming()) + .forEach(entity -> res.add(new Avoidance(new BlockPos(entity), mobCoeff, Baritone.settings().mobAvoidanceRadius.value))); } return res; } diff --git a/src/main/java/baritone/utils/pathing/Favoring.java b/src/main/java/baritone/utils/pathing/Favoring.java index 7ffe49ff..bbc23c7f 100644 --- a/src/main/java/baritone/utils/pathing/Favoring.java +++ b/src/main/java/baritone/utils/pathing/Favoring.java @@ -17,27 +17,29 @@ package baritone.utils.pathing; -import baritone.Baritone; import baritone.api.pathing.calc.IPath; import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; +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) { - this(previous); + public Favoring(IPlayerContext ctx, IPath previous, CalculationContext context) { + this(previous, context); for (Avoidance avoid : Avoidance.create(ctx)) { avoid.applySpherical(favorings); } - System.out.println("Favoring size: " + favorings.size()); + Helper.HELPER.logDebug("Favoring size: " + favorings.size()); } - public Favoring(IPath previous) { // create one just from previous path, no mob avoidances + public Favoring(IPath previous, CalculationContext context) { // create one just from previous path, no mob avoidances favorings = new Long2DoubleOpenHashMap(); favorings.defaultReturnValue(1.0D); - double coeff = Baritone.settings().backtrackCostFavoringCoefficient.get(); + double coeff = context.backtrackCostFavoringCoefficient; if (coeff != 1D && previous != null) { previous.positions().forEach(pos -> favorings.put(BetterBlockPos.longHash(pos), coeff)); } diff --git a/src/main/java/baritone/utils/pathing/MutableMoveResult.java b/src/main/java/baritone/utils/pathing/MutableMoveResult.java index b6e1ba8a..17fc6ee2 100644 --- a/src/main/java/baritone/utils/pathing/MutableMoveResult.java +++ b/src/main/java/baritone/utils/pathing/MutableMoveResult.java @@ -25,6 +25,7 @@ import baritone.api.pathing.movement.ActionCosts; * @author leijurv */ public final class MutableMoveResult { + public int x; public int y; public int z; diff --git a/src/main/java/baritone/utils/pathing/PathBase.java b/src/main/java/baritone/utils/pathing/PathBase.java index 04fe9872..de10fdf3 100644 --- a/src/main/java/baritone/utils/pathing/PathBase.java +++ b/src/main/java/baritone/utils/pathing/PathBase.java @@ -17,6 +17,7 @@ package baritone.utils.pathing; +import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; @@ -25,8 +26,12 @@ 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) { + return this; + } BlockStateInterface bsi = (BlockStateInterface) bsi0; for (int i = 0; i < positions().size(); i++) { BlockPos pos = positions().get(i); @@ -39,14 +44,14 @@ public abstract class PathBase implements IPath { @Override public PathBase staticCutoff(Goal destination) { - int min = BaritoneAPI.getSettings().pathCutoffMinimumLength.get(); + int min = BaritoneAPI.getSettings().pathCutoffMinimumLength.value; if (length() < min) { return this; } if (destination == null || destination.isInGoal(getDest())) { return this; } - double factor = BaritoneAPI.getSettings().pathCutoffFactor.get(); + double factor = BaritoneAPI.getSettings().pathCutoffFactor.value; int newLength = (int) ((length() - min) * factor) + min - 1; return new CutoffPath(this, newLength); } diff --git a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java deleted file mode 100644 index e1d6dd10..00000000 --- a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java +++ /dev/null @@ -1,110 +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.utils.pathing; - -import baritone.Baritone; -import baritone.api.pathing.calc.IPath; -import baritone.api.pathing.goals.Goal; -import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.PathCalculationResult; -import baritone.cache.CachedWorld; -import baritone.pathing.calc.AStarPathFinder; -import baritone.pathing.calc.AbstractNodeCostSearch; -import baritone.pathing.movement.CalculationContext; -import baritone.pathing.path.SplicedPath; -import net.minecraft.util.EnumFacing; - -import java.util.Optional; -import java.util.function.Consumer; - -/** - * Calculate and splice many path segments to reach a goal - * - * @author leijurv - */ -public class SegmentedCalculator { - private final BetterBlockPos start; - private final Goal goal; - private final CalculationContext context; - - private SegmentedCalculator(BetterBlockPos start, Goal goal, CalculationContext context) { - this.start = start; - this.goal = goal; - this.context = context; - } - - private Optional doCalc() { - Optional soFar = Optional.empty(); - while (true) { - PathCalculationResult result = segment(soFar); - switch (result.getType()) { - case SUCCESS_SEGMENT: - case SUCCESS_TO_GOAL: - break; - case FAILURE: // if path calculation failed, we're done - case EXCEPTION: // if path calculation threw an exception, we're done - return soFar; - default: // CANCELLATION and null should not be possible, nothing else has access to this, so it can't have been canceled - throw new IllegalStateException(); - } - IPath segment = result.getPath().get(); // path calculation result type is SUCCESS_SEGMENT, so the path must be present - IPath combined = soFar.map(previous -> (IPath) SplicedPath.trySplice(previous, segment, true).get()).orElse(segment); - loadAdjacent(combined.getDest().getX(), combined.getDest().getZ()); - soFar = Optional.of(combined); - if (result.getType() == PathCalculationResult.Type.SUCCESS_TO_GOAL) { - return soFar; - } - } - } - - private void loadAdjacent(int blockX, int blockZ) { - BetterBlockPos bp = new BetterBlockPos(blockX, 64, blockZ); - CachedWorld cached = (CachedWorld) context.getBaritone().getPlayerContext().worldData().getCachedWorld(); - for (int i = 0; i < 4; i++) { - // pathing thread is not allowed to load new cached regions from disk - // it checks if every chunk is loaded before getting blocks from it - // so you see path segments ending at multiples of 512 (plus or minus one) on either x or z axis - // this loads every adjacent chunk to the segment end, so it can continue into the next cached region - BetterBlockPos toLoad = bp.offset(EnumFacing.byHorizontalIndex(i), 16); - cached.tryLoadFromDisk(toLoad.x >> 9, toLoad.z >> 9); - } - } - - private PathCalculationResult segment(Optional previous) { - BetterBlockPos segmentStart = previous.map(IPath::getDest).orElse(start); // <-- e p i c - AbstractNodeCostSearch search = new AStarPathFinder(segmentStart.x, segmentStart.y, segmentStart.z, goal, new Favoring(previous.orElse(null)), context); // this is on another thread, so cannot include mob avoidances. - return search.calculate(Baritone.settings().primaryTimeoutMS.get(), Baritone.settings().failureTimeoutMS.get()); // use normal time settings, not the plan ahead settings, so as to not overwhelm the computer - } - - public static void calculateSegmentsThreaded(BetterBlockPos start, Goal goal, CalculationContext context, Consumer onCompletion, Runnable onFailure) { - Baritone.getExecutor().execute(() -> { - Optional result; - try { - result = new SegmentedCalculator(start, goal, context).doCalc(); - } catch (Exception ex) { - ex.printStackTrace(); - result = Optional.empty(); - } - if (result.isPresent()) { - onCompletion.accept(result.get()); - } else { - onFailure.run(); - } - }); - } -} diff --git a/src/main/java/baritone/utils/player/PrimaryPlayerContext.java b/src/main/java/baritone/utils/player/PrimaryPlayerContext.java index 1eefc91f..3cb498ac 100644 --- a/src/main/java/baritone/utils/player/PrimaryPlayerContext.java +++ b/src/main/java/baritone/utils/player/PrimaryPlayerContext.java @@ -19,9 +19,10 @@ package baritone.utils.player; import baritone.api.BaritoneAPI; import baritone.api.cache.IWorldData; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import baritone.api.utils.IPlayerController; -import baritone.utils.Helper; +import baritone.api.utils.RayTraceUtils; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.World; @@ -58,6 +59,6 @@ public enum PrimaryPlayerContext implements IPlayerContext, Helper { @Override public RayTraceResult objectMouseOver() { - return mc.objectMouseOver; + return RayTraceUtils.rayTraceTowards(player(), playerRotations(), playerController().getBlockReachDistance()); } } diff --git a/src/main/java/baritone/utils/player/PrimaryPlayerController.java b/src/main/java/baritone/utils/player/PrimaryPlayerController.java index 3bb815bb..872a3658 100644 --- a/src/main/java/baritone/utils/player/PrimaryPlayerController.java +++ b/src/main/java/baritone/utils/player/PrimaryPlayerController.java @@ -17,15 +17,21 @@ package baritone.utils.player; +import baritone.api.utils.Helper; import baritone.api.utils.IPlayerController; -import baritone.utils.Helper; +import baritone.utils.accessor.IPlayerControllerMP; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ClickType; import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameType; +import net.minecraft.world.World; /** * Implementation of {@link IPlayerController} that chains to the primary player controller's methods @@ -37,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); @@ -63,7 +79,22 @@ public enum PrimaryPlayerController implements IPlayerController, Helper { } @Override - public RayTraceResult objectMouseOver() { - return mc.objectMouseOver; + public EnumActionResult processRightClickBlock(EntityPlayerSP player, World world, BlockPos pos, EnumFacing direction, Vec3d vec, EnumHand hand) { + return mc.playerController.processRightClickBlock(player, (WorldClient) world, pos, direction, vec, hand); + } + + @Override + 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/FillSchematic.java b/src/main/java/baritone/utils/schematic/FillSchematic.java new file mode 100644 index 00000000..3216f59f --- /dev/null +++ b/src/main/java/baritone/utils/schematic/FillSchematic.java @@ -0,0 +1,58 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; + +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 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, IBlockState current, List approxPlaceable) { + return state; + } + + @Override + public int widthX() { + return widthX; + } + + @Override + public int heightY() { + return heightY; + } + + @Override + public int lengthZ() { + return lengthZ; + } +} diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java new file mode 100644 index 00000000..32b3292c --- /dev/null +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.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.schematic; + +import net.minecraft.block.BlockAir; +import net.minecraft.block.state.IBlockState; +import net.minecraft.nbt.NBTTagCompound; + +import java.util.OptionalInt; +import java.util.function.Predicate; + +public class MapArtSchematic extends Schematic { + + private final int[][] heightMap; + + public MapArtSchematic(NBTTagCompound schematic) { + super(schematic); + heightMap = new int[widthX][lengthZ]; + + for (int x = 0; x < widthX; x++) { + for (int z = 0; z < lengthZ; z++) { + IBlockState[] column = states[x][z]; + + OptionalInt lowestBlockY = lastIndexMatching(column, state -> !(state.getBlock() instanceof BlockAir)); + if (lowestBlockY.isPresent()) { + heightMap[x][z] = lowestBlockY.getAsInt(); + } else { + System.out.println("Column " + x + "," + z + " has no blocks, but it's apparently map art? wtf"); + System.out.println("Letting it be whatever"); + heightMap[x][z] = 256; + } + + } + } + } + + private static OptionalInt lastIndexMatching(T[] arr, Predicate predicate) { + for (int y = arr.length - 1; y >= 0; y--) { + if (predicate.test(arr[y])) { + return OptionalInt.of(y); + } + } + return OptionalInt.empty(); + } + + @Override + 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, 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 new file mode 100644 index 00000000..47461e60 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/Schematic.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.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.nbt.NBTTagCompound; + +import java.util.List; + +public class Schematic implements ISchematic { + + public final int widthX; + public final int heightY; + public final int lengthZ; + protected final IBlockState[][][] states; + + public Schematic(NBTTagCompound schematic) { + String type = schematic.getString("Materials"); + if (!type.equals("Alpha")) { + throw new IllegalStateException("bad schematic " + type); + } + widthX = schematic.getInteger("Width"); + heightY = schematic.getInteger("Height"); + lengthZ = schematic.getInteger("Length"); + byte[] blocks = schematic.getByteArray("Blocks"); + byte[] metadata = schematic.getByteArray("Data"); + + byte[] additional = null; + if (schematic.hasKey("AddBlocks")) { + byte[] addBlocks = schematic.getByteArray("AddBlocks"); + additional = new byte[addBlocks.length * 2]; + for (int i = 0; i < addBlocks.length; i++) { + additional[i * 2 + 0] = (byte) ((addBlocks[i] >> 4) & 0xF); // lower nibble + additional[i * 2 + 1] = (byte) ((addBlocks[i] >> 0) & 0xF); // upper nibble + } + } + states = new IBlockState[widthX][lengthZ][heightY]; + for (int y = 0; y < heightY; y++) { + for (int z = 0; z < lengthZ; z++) { + for (int x = 0; x < widthX; x++) { + int blockInd = (y * lengthZ + z) * widthX + x; + + int blockID = blocks[blockInd] & 0xFF; + if (additional != null) { + // additional is 0 through 15 inclusive since it's & 0xF above + blockID |= additional[blockInd] << 8; + } + Block block = Block.REGISTRY.getObjectById(blockID); + int meta = metadata[blockInd] & 0xFF; + states[x][z][y] = block.getStateFromMeta(meta); + } + } + } + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return states[x][z][y]; + } + + @Override + public int widthX() { + return widthX; + } + + @Override + public int heightY() { + return heightY; + } + + @Override + public int lengthZ() { + return lengthZ; + } +} diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java new file mode 100644 index 00000000..38279031 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.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.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) { + this.schematic = schematicWorld; + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + return schematic.getSchematic().getBlockState(new BlockPos(x, y, z)); + } + + @Override + public int widthX() { + return schematic.getSchematic().getWidth(); + } + + @Override + public int heightY() { + return schematic.getSchematic().getHeight(); + } + + @Override + public int lengthZ() { + return schematic.getSchematic().getLength(); + } +} diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java new file mode 100644 index 00000000..bacf1e9d --- /dev/null +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.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.utils.schematic.schematica; + +import baritone.api.utils.ISchematic; +import com.github.lunatrius.schematica.Schematica; +import com.github.lunatrius.schematica.proxy.ClientProxy; +import net.minecraft.util.Tuple; +import net.minecraft.util.math.BlockPos; + +import java.util.Optional; + +public enum SchematicaHelper { + ; + + public static boolean isSchematicaPresent() { + try { + Class.forName(Schematica.class.getName()); + return true; + } catch (ClassNotFoundException | NoClassDefFoundError ex) { + return false; + } + } + + public static Optional> getOpenSchematic() { + return Optional.ofNullable(ClientProxy.schematic) + .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 new file mode 100644 index 00000000..4c9da008 --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/core/util/math/MBlockPos.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 com.github.lunatrius.core.util.math; + +import net.minecraft.util.math.BlockPos; + +public class MBlockPos extends BlockPos { + + MBlockPos() { + super(6, 6, 6); + } + + @Override + public int getX() { + throw new LinkageError("LOL"); + } + + @Override + public int getY() { + throw new LinkageError("LOL"); + } + + @Override + public int getZ() { + throw new LinkageError("LOL"); + } +} diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java b/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.java new file mode 100644 index 00000000..415399df --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/schematica/Schematica.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 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 new file mode 100644 index 00000000..bbf11428 --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/schematica/api/ISchematic.java @@ -0,0 +1,32 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package com.github.lunatrius.schematica.api; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.BlockPos; + +public interface ISchematic { + + IBlockState getBlockState(BlockPos var1); + + int getWidth(); + + int getHeight(); + + int getLength(); +} 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 new file mode 100644 index 00000000..25e5c280 --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/schematica/client/world/SchematicWorld.java @@ -0,0 +1,30 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package com.github.lunatrius.schematica.client.world; + +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 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 new file mode 100644 index 00000000..952f86a1 --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/ClientProxy.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 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/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java new file mode 100644 index 00000000..eb3c034e --- /dev/null +++ b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java @@ -0,0 +1,20 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package com.github.lunatrius.schematica.proxy; + +public abstract class CommonProxy {} diff --git a/src/test/java/baritone/pathing/calc/openset/OpenSetsTest.java b/src/test/java/baritone/pathing/calc/openset/OpenSetsTest.java index a12e0b7a..33a1f4dc 100644 --- a/src/test/java/baritone/pathing/calc/openset/OpenSetsTest.java +++ b/src/test/java/baritone/pathing/calc/openset/OpenSetsTest.java @@ -50,14 +50,14 @@ public class OpenSetsTest { return testSizes; } - private static void removeAndTest(int amount, IOpenSet[] test, Optional> mustContain) { + private static void removeAndTest(int amount, IOpenSet[] test, Collection mustContain) { double[][] results = new double[test.length][amount]; for (int i = 0; i < test.length; i++) { long before = System.nanoTime() / 1000000L; for (int j = 0; j < amount; j++) { PathNode pn = test[i].removeLowest(); - if (mustContain.isPresent() && !mustContain.get().contains(pn)) { - throw new IllegalStateException(mustContain.get() + " " + pn); + if (mustContain != null && !mustContain.contains(pn)) { + throw new IllegalStateException(mustContain + " " + pn); } results[i][j] = pn.combinedCost; } @@ -131,7 +131,7 @@ public class OpenSetsTest { System.out.println("Removal round 1"); // remove a quarter of the nodes and verify that they are indeed the size/4 lowest ones - removeAndTest(size / 4, test, Optional.of(lowestQuarter)); + removeAndTest(size / 4, test, lowestQuarter); // none of them should be empty (sanity check) for (IOpenSet set : test) { @@ -160,7 +160,7 @@ public class OpenSetsTest { System.out.println("Removal round 2"); // remove the remaining 3/4 - removeAndTest(size - size / 4, test, Optional.empty()); + removeAndTest(size - size / 4, test, null); // every set should now be empty for (IOpenSet set : test) { diff --git a/src/test/java/baritone/pathing/movement/ActionCostsTest.java b/src/test/java/baritone/pathing/movement/ActionCostsTest.java index cce61e4b..9bfdafb0 100644 --- a/src/test/java/baritone/pathing/movement/ActionCostsTest.java +++ b/src/test/java/baritone/pathing/movement/ActionCostsTest.java @@ -23,6 +23,7 @@ import static baritone.api.pathing.movement.ActionCosts.*; import static org.junit.Assert.assertEquals; public class ActionCostsTest { + @Test public void testFallNBlocksCost() { assertEquals(FALL_N_BLOCKS_COST.length, 257); // Fall 0 blocks through fall 256 blocks diff --git a/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java b/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java index c30b45d1..d05a323b 100644 --- a/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java +++ b/src/test/java/baritone/utils/pathing/PathingBlockTypeTest.java @@ -22,6 +22,7 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; public class PathingBlockTypeTest { + @Test public void testBits() { for (PathingBlockType type : PathingBlockType.values()) {