diff --git a/.gitignore b/.gitignore index 564974df..de408ec0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ **/*.swp run/ +autotest/ +dist/ # Gradle build/ diff --git a/.travis.yml b/.travis.yml index c54706cf..a8344ba0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ install: - travis_retry docker build -t cabaletta/baritone . script: -- 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; sh scripts/build.sh; DISPLAY=:99 BARITONE_AUTO_TEST=true ./gradlew runClient" +- 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 - cat dist/checksums.txt diff --git a/Dockerfile b/Dockerfile index ac36eea5..bec43d69 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,16 +13,12 @@ RUN apt install --target-release jessie-backports \ 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 --force-yes unzip wget - -ADD . /code - -RUN echo "\nrunClient {\nargs \"--width\",\"128\",\"--height\",\"128\"\n}" >> /code/build.gradle - -# this .deb is specially patched to support lwjgl -# source: https://github.com/tectonicus/tectonicus/issues/60#issuecomment-154239173 -RUN dpkg -i /code/scripts/xvfb_1.16.4-1_amd64.deb +COPY . /code WORKDIR /code +# this .deb is specially patched to support lwjgl +# 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 diff --git a/FEATURES.md b/FEATURES.md index 172b2f87..cd2a90fe 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -10,16 +10,19 @@ - **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. # Pathing method -Baritone uses a modified version of A*. +Baritone uses A*, with some modifications: -- **Incremental cost backoff** Since most of the time Baritone only knows the terrain up to the render distance, it can't calculate a full path to the goal. Therefore it needs to select a segment to execute first (assuming it will calculate the next segment at the end of this one). It uses incremental cost backoff to select the best node by varying metrics, then paths to that node. This is unchanged from MineBot and I made a write-up that still applies. In essence, it keeps track of the best node by various increasing coefficients, then picks the node with the least coefficient that goes at least 5 blocks from the starting position. +- **Segmented calculation** Traditional A* calculates until the most promising node is in the goal, however in the environment of Minecraft with a limited render distance, we don't know the environment all the way to our goal. Baritone has three possible ways for path calculation to end: finding a path all the way to the goal, running out of time, or getting to the render distance. In the latter two scenarios, the selection of which segment to actually execute falls to the next item (incremental cost backoff). Whenever the path calculation thread finds that the best / most promising node is at the edge of loaded chunks, it increments a counter. If this happens more than 50 times (configurable), path calculation exits early. This happens with very low render distances. Otherwise, calculation continues until the timeout is hit (also configurable) or we find a path all the way to the goal. +- **Incremental cost backoff** When path calculation exits early without getting all the way to the goal, Baritone it needs to select a segment to execute first (assuming it will calculate the next segment at the end of this one). It uses incremental cost backoff to select the best node by varying metrics, then paths to that node. This is unchanged from MineBot and I made a write-up that still applies. In essence, it keeps track of the best node by various increasing coefficients, then picks the node with the least coefficient that goes at least 5 blocks from the starting position. - **Minimum improvement repropagation** The pathfinder ignores alternate routes that provide minimal improvements (less than 0.01 ticks of improvement), because the calculation cost of repropagating this to all connected nodes is much higher than the half-millisecond path time improvement it would get. -- **Backtrack cost favoring** While calculating the next segment, Baritone favors backtracking its current segment slightly, as a tiebreaker. 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 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. +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. # Goals @@ -30,6 +33,7 @@ The pathing goal can be set to any of these options: - **GoalTwoBlocks** a block position that the player should stand in, either at foot or eye level - **GoalGetToBlock** a block position that the player should stand adjacent to, below, or on top of - **GoalNear** a block position that the player should get within a certain radius of, used for following entities +- **GoalAxis** a block position on an axis or diagonal axis (so x=0, z=0, or x=z), and y=120 (configurable) And finally `GoalComposite`. `GoalComposite` is a list of other goals, any one of which satisfies the goal. For example, `mine diamond_ore` creates a `GoalComposite` of `GoalTwoBlocks`s for every diamond ore location it knows of. @@ -38,7 +42,6 @@ And finally `GoalComposite`. `GoalComposite` is a list of other goals, any one o Things it doesn't have yet - Trapdoors - Sprint jumping in a 1x2 corridor -- Parkour (jumping over gaps of any length) [IN PROGRESS] See issues for more. diff --git a/IMPACT.md b/IMPACT.md index e8430ab5..cc12252e 100644 --- a/IMPACT.md +++ b/IMPACT.md @@ -1,89 +1 @@ -# Integration between Baritone and Impact -Impact 4.4 will have Baritone included on release, however, if you're impatient, you can install Baritone into Impact 4.3 right now! - -## 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 3 methods of acquiring a build of Baritone (While it is still in development) - -### 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`. - -### 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`` - -### Cutting Edge Release -If you want to trust @Plutie#9079, you can download an automatically generated build of the latest commit -from his Jenkins server, found here. - -## 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 a new object in the array, between the ``Impact`` and ``ClientAPI`` dependencies preferably. - ``` - { - "name": "cabaletta:baritone:X.Y.Z" - }, - ``` -- 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. +Impact 4.4 is out. See INSTALL.md \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..9df3d6bf --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,96 @@ +# 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/README.md b/README.md index ad6980f6..445d34bb 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,52 @@ # 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/github/license/cabaletta/baritone.svg)](LICENSE) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7150d8ccf6094057b1782aa7a8f92d7d)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade) +[![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) +[![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) -Unofficial Jenkins: [![Build Status](https://plutiejenkins.leijurv.com/job/baritone/badge/icon)](https://plutiejenkins.leijurv.com/job/baritone/lastSuccessfulBuild/) +A Minecraft pathfinder bot. -A Minecraft pathfinder bot. This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), +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. + +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). Here are some links to help to get started: - [Features](FEATURES.md) -- [Baritone + Impact](IMPACT.md) +- [Installation](INSTALL.md) There's also some useful information down below # Setup -## IntelliJ's Gradle UI -- Open the project in IntelliJ as a Gradle project -- Run the Gradle task `setupDecompWorkspace` -- Run the Gradle task `genIntellijRuns` -- Refresh the Gradle project (or just restart IntelliJ) -- Select the "Minecraft Client" launch config and run - ## Command Line On Mac OSX and Linux, use `./gradlew` instead of `gradlew`. Running Baritone: ``` -$ gradlew run +$ gradlew runClient ``` -Setting up for IntelliJ: +Building Baritone: ``` -$ gradlew setupDecompWorkspace -$ gradlew --refresh-dependencies -$ gradlew genIntellijRuns +$ gradlew build ``` +For example, 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) diff --git a/build.gradle b/build.gradle index 0a1bb804..e0b556a2 100755 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ */ group 'baritone' -version '0.0.3' +version '1.0.0-hotfix-2' buildscript { repositories { @@ -37,6 +37,10 @@ buildscript { } } + +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' @@ -54,10 +58,12 @@ sourceSets { minecraft { version = '1.12.2' - mappings = 'snapshot_20180731' + mappings = 'stable_39' tweakClass = 'baritone.launch.BaritoneTweaker' runDir = 'run' - makeObfSourceJar = false + + // The sources jar should use SRG names not MCP to ensure compatibility with all mappings + makeObfSourceJar = true } repositories { @@ -67,9 +73,15 @@ repositories { 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' @@ -88,4 +100,16 @@ mixin { jar { from 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' + versionManifest 'https://launchermeta.mojang.com/mc/game/version_manifest.json' +} + +task createDist(type: CreateDistTask, dependsOn: proguard) + +build.finalizedBy(createDist) diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore new file mode 100644 index 00000000..daf21d56 --- /dev/null +++ b/buildSrc/.gitignore @@ -0,0 +1,3 @@ +build/ +.gradle/ +out/ \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000..2ac49af0 --- /dev/null +++ b/buildSrc/build.gradle @@ -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 . + */ + +repositories { + mavenCentral() +} + +dependencies { + compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + compile group: 'commons-io', name: 'commons-io', version: '2.6' +} \ No newline at end of file diff --git a/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java b/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java new file mode 100644 index 00000000..0450509f --- /dev/null +++ b/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.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.gradle.task; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.gradle.api.DefaultTask; + +import java.io.File; +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 + * @since 10/12/2018 + */ +class BaritoneGradleTask extends DefaultTask { + + protected static final JsonParser PARSER = new JsonParser(); + + protected static final String + PROGUARD_ZIP = "proguard.zip", + PROGUARD_JAR = "proguard.jar", + PROGUARD_CONFIG_TEMPLATE = "scripts/proguard.pro", + PROGUARD_CONFIG_DEST = "template.pro", + PROGUARD_API_CONFIG = "api.pro", + 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"; + + protected String artifactName, artifactVersion; + protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, 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.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH); + + if (!Files.exists(this.artifactPath)) { + throw new IllegalStateException("Artifact not found! Run build first!"); + } + } + + protected void write(InputStream stream, Path file) throws Exception { + if (Files.exists(file)) { + Files.delete(file); + } + Files.copy(stream, file); + } + + protected String formatVersion(String string) { + return String.format(string, this.artifactName, this.artifactVersion); + } + + protected Path getRelativeFile(String file) { + return Paths.get(new File(file).getAbsolutePath()); + } + + protected Path getTemporaryFile(String file) { + return Paths.get(new File(getTemporaryDir(), file).getAbsolutePath()); + } + + 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 new file mode 100644 index 00000000..49e2e142 --- /dev/null +++ b/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java @@ -0,0 +1,82 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.gradle.task; + +import org.gradle.api.tasks.TaskAction; + +import javax.xml.bind.DatatypeConverter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +/** + * @author Brady + * @since 10/12/2018 + */ +public class CreateDistTask extends BaritoneGradleTask { + + private static MessageDigest SHA1_DIGEST; + + @TaskAction + protected void exec() throws Exception { + 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)); + + // NIO will not automatically create directories + Path dir = getRelativeFile("dist/"); + if (!Files.exists(dir)) { + Files.createDirectory(dir); + } + + // 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); + + // Calculate all checksums and format them like "shasum" + List shasum = Stream.of(api, standalone, unoptimized) + .map(path -> sha1(path) + " " + path.getFileName().toString()) + .collect(Collectors.toList()); + + shasum.forEach(System.out::println); + + // Write the checksums to a file + Files.write(getRelativeFile("dist/checksums.txt"), shasum); + } + + private static synchronized String sha1(Path path) { + try { + if (SHA1_DIGEST == null) { + SHA1_DIGEST = MessageDigest.getInstance("SHA-1"); + } + return DatatypeConverter.printHexBinary(SHA1_DIGEST.digest(Files.readAllBytes(path))).toLowerCase(); + } catch (Exception e) { + // haha no thanks + throw new IllegalStateException(e); + } + } +} diff --git a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java new file mode 100644 index 00000000..350f7446 --- /dev/null +++ b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java @@ -0,0 +1,260 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.gradle.task; + +import baritone.gradle.util.Determinizer; +import com.google.gson.*; +import org.apache.commons.io.IOUtils; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; +import org.gradle.internal.Pair; + +import java.io.*; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +/** + * @author Brady + * @since 10/11/2018 + */ +public class ProguardTask extends BaritoneGradleTask { + + private static final Pattern TEMP_LIBRARY_PATTERN = Pattern.compile("-libraryjars 'tempLibraries\\/([a-zA-Z0-9/_\\-\\.]+)\\.jar'"); + + @Input + private String url; + + @Input + private String extract; + + @Input + private String versionManifest; + + private Map versionDownloadMap; + private List requiredLibraries; + + @TaskAction + protected void exec() throws Exception { + super.verifyArtifacts(); + + // "Haha brady why don't you make separate tasks" + processArtifact(); + downloadProguard(); + extractProguard(); + generateConfigs(); + downloadVersionManifest(); + acquireDependencies(); + proguardApi(); + proguardStandalone(); + cleanup(); + } + + private void processArtifact() throws Exception { + if (Files.exists(this.artifactUnoptimizedPath)) { + Files.delete(this.artifactUnoptimizedPath); + } + + Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString()); + } + + private void downloadProguard() throws Exception { + Path proguardZip = getTemporaryFile(PROGUARD_ZIP); + if (!Files.exists(proguardZip)) { + write(new URL(this.url).openStream(), proguardZip); + } + } + + private void extractProguard() throws Exception { + Path proguardJar = getTemporaryFile(PROGUARD_JAR); + if (!Files.exists(proguardJar)) { + ZipFile zipFile = new ZipFile(getTemporaryFile(PROGUARD_ZIP).toFile()); + ZipEntry zipJarEntry = zipFile.getEntry(this.extract); + write(zipFile.getInputStream(zipJarEntry), proguardJar); + zipFile.close(); + } + } + + private void generateConfigs() throws Exception { + Files.copy(getRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), REPLACE_EXISTING); + + // Setup the template that will be used to derive the API and Standalone configs + List template = Files.readAllLines(getTemporaryFile(PROGUARD_CONFIG_DEST)); + template.add(0, "-injars " + this.artifactPath.toString()); + template.add(1, "-outjars " + this.getTemporaryFile(PROGUARD_EXPORT_PATH)); + + // Acquire the RT jar using "java -verbose". This doesn't work on Java 9+ + Process p = new ProcessBuilder("java", "-verbose").start(); + String out = IOUtils.toString(p.getInputStream(), "UTF-8").split("\n")[0].split("Opened ")[1].replace("]", ""); + template.add(2, "-libraryjars '" + out + "'"); + + // API config doesn't require any changes from the changes that we made to the template + Files.write(getTemporaryFile(PROGUARD_API_CONFIG), template); + + // For the Standalone config, don't keep the API package + List standalone = new ArrayList<>(template); + standalone.removeIf(s -> s.contains("# this is the keep api")); + Files.write(getTemporaryFile(PROGUARD_STANDALONE_CONFIG), standalone); + + // Discover all of the libraries that we will need to acquire from gradle + this.requiredLibraries = new ArrayList<>(); + template.forEach(line -> { + if (!line.startsWith("#")) { + Matcher m = TEMP_LIBRARY_PATTERN.matcher(line); + if (m.find()) { + this.requiredLibraries.add(m.group(1)); + } + } + }); + } + + private void downloadVersionManifest() throws Exception { + Path manifestJson = getTemporaryFile(VERSION_MANIFEST); + write(new URL(this.versionManifest).openStream(), manifestJson); + + // Place all the versions in the map with their download URL + this.versionDownloadMap = new HashMap<>(); + JsonObject json = readJson(Files.readAllLines(manifestJson)).getAsJsonObject(); + JsonArray versions = json.getAsJsonArray("versions"); + versions.forEach(element -> { + JsonObject object = element.getAsJsonObject(); + this.versionDownloadMap.put(object.get("id").getAsString(), object.get("url").getAsString()); + }); + } + + private void acquireDependencies() throws Exception { + + // Create a map of all of the dependencies that we are able to access in this project + // Likely a better way to do this, I just pair the dependency with the first valid configuration + Map> dependencyLookupMap = new HashMap<>(); + getProject().getConfigurations().stream().filter(Configuration::isCanBeResolved).forEach(config -> + config.getAllDependencies().forEach(dependency -> + dependencyLookupMap.putIfAbsent(dependency.getName() + "-" + dependency.getVersion(), Pair.of(config, dependency)))); + + // Create the directory if it doesn't already exist + Path tempLibraries = getTemporaryFile(TEMP_LIBRARY_DIR); + if (!Files.exists(tempLibraries)) { + Files.createDirectory(tempLibraries); + } + + // Iterate the required libraries to copy them to tempLibraries + for (String lib : this.requiredLibraries) { + // Download the version jar from the URL acquired from the version manifest + if (lib.startsWith("minecraft")) { + String version = lib.split("-")[1]; + Path versionJar = getTemporaryFile("tempLibraries/" + lib + ".jar"); + if (!Files.exists(versionJar)) { + JsonObject versionJson = PARSER.parse(new InputStreamReader(new URL(this.versionDownloadMap.get(version)).openStream())).getAsJsonObject(); + String url = versionJson.getAsJsonObject("downloads").getAsJsonObject("client").getAsJsonPrimitive("url").getAsString(); + write(new URL(url).openStream(), versionJar); + } + continue; + } + + // Find a configuration/dependency pair that matches the desired library + Pair pair = null; + for (Map.Entry> entry : dependencyLookupMap.entrySet()) { + if (entry.getKey().startsWith(lib)) { + pair = entry.getValue(); + } + } + + // The pair must be non-null + Objects.requireNonNull(pair); + + // Find the library jar file, and copy it to tempLibraries + for (File file : pair.getLeft().files(pair.getRight())) { + if (file.getName().startsWith(lib)) { + Files.copy(file.toPath(), getTemporaryFile("tempLibraries/" + lib + ".jar"), REPLACE_EXISTING); + } + } + } + } + + private void proguardApi() throws Exception { + runProguard(getTemporaryFile(PROGUARD_API_CONFIG)); + Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString()); + } + + private void proguardStandalone() throws Exception { + runProguard(getTemporaryFile(PROGUARD_STANDALONE_CONFIG)); + Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString()); + } + + private void cleanup() { + try { + Files.delete(this.proguardOut); + } catch (IOException ignored) {} + } + + public void setUrl(String url) { + this.url = url; + } + + public void setExtract(String extract) { + this.extract = extract; + } + + public void setVersionManifest(String versionManifest) { + this.versionManifest = versionManifest; + } + + private void runProguard(Path config) throws Exception { + // Delete the existing proguard output file. Proguard probably handles this already, but why not do it ourselves + if (Files.exists(this.proguardOut)) { + Files.delete(this.proguardOut); + } + + Path proguardJar = getTemporaryFile(PROGUARD_JAR); + Process p = new ProcessBuilder("java", "-jar", proguardJar.toString(), "@" + config.toString()) + .directory(getTemporaryFile("").toFile()) // Set the working directory to the temporary folder] + .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()); + + // Halt the current thread until the process is complete, if the exit code isn't 0, throw an exception + int exitCode = p.waitFor(); + if (exitCode != 0) { + throw new IllegalStateException("Proguard exited with code " + exitCode); + } + } + + private void printOutputLog(InputStream stream) { + new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } catch (final Exception e) { + e.printStackTrace(); + } + }).start(); + } +} diff --git a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java new file mode 100644 index 00000000..fc268cd3 --- /dev/null +++ b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.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.gradle.util; + +import com.google.gson.*; +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.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.stream.Collectors; + +/** + * Make a .jar file deterministic by sorting all entries by name, and setting all the last modified times to 42069. + * This makes the build 100% reproducible since the timestamp when you built it no longer affects the final file. + * + * @author leijurv + */ +public class Determinizer { + + public static void determinize(String inputPath, String outputPath) throws IOException { + System.out.println("Running Determinizer"); + System.out.println(" Input path: " + inputPath); + System.out.println(" Output path: " + outputPath); + + try ( + JarFile jarFile = new JarFile(new File(inputPath)); + JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(outputPath))) + ) { + + List entries = jarFile.stream() + .sorted(Comparator.comparing(JarEntry::getName)) + .collect(Collectors.toList()); + + for (JarEntry entry : entries) { + if (entry.getName().equals("META-INF/fml_cache_annotation.json")) { + continue; + } + if (entry.getName().equals("META-INF/fml_cache_class_versions.json")) { + continue; + } + JarEntry clone = new JarEntry(entry.getName()); + clone.setTime(42069); + jos.putNextEntry(clone); + if (entry.getName().endsWith(".refmap.json")) { + JsonObject object = new JsonParser().parse(new InputStreamReader(jarFile.getInputStream(entry))).getAsJsonObject(); + jos.write(writeSorted(object).getBytes()); + } else { + copy(jarFile.getInputStream(entry), jos); + } + } + jos.finish(); + } + } + + private static void copy(InputStream is, OutputStream os) throws IOException { + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + } + + private static String writeSorted(JsonObject in) throws IOException { + StringWriter writer = new StringWriter(); + JsonWriter jw = new JsonWriter(writer); + ORDERED_JSON_WRITER.write(jw, in); + return writer.toString() + "\n"; + } + + /** + * All credits go to GSON and its contributors. GSON is licensed under the Apache 2.0 License. + * This implementation has been modified to write {@link JsonObject} keys in order. + * + * @see GSON License + * @see Original Source + */ + private static final TypeAdapter ORDERED_JSON_WRITER = new TypeAdapter() { + + @Override + public JsonElement read(JsonReader in) { + return null; + } + + @Override + public void write(JsonWriter out, JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + out.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + out.value(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + out.value(primitive.getAsBoolean()); + } else { + out.value(primitive.getAsString()); + } + + } else if (value.isJsonArray()) { + out.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(out, e); + } + out.endArray(); + + } else if (value.isJsonObject()) { + out.beginObject(); + + List> entries = new ArrayList<>(value.getAsJsonObject().entrySet()); + entries.sort(Comparator.comparing(Map.Entry::getKey)); + for (Map.Entry e : entries) { + out.name(e.getKey()); + write(out, e.getValue()); + } + out.endObject(); + + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; +} diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100644 index a224a884..00000000 --- a/scripts/build.sh +++ /dev/null @@ -1,30 +0,0 @@ -set -e # this makes the whole script fail immediately if any one of these commands fails -./gradlew build -export VERSION=$(cat build.gradle | grep "version '" | cut -d "'" -f 2-2) - -wget -nv https://downloads.sourceforge.net/project/proguard/proguard/6.0/proguard6.0.3.zip -unzip proguard6.0.3.zip 2>&1 > /dev/null -cd build/libs -echo "-injars 'baritone-$VERSION.jar'" >> api.pro # insert current version -cat ../../scripts/proguard.pro | grep -v "this is the rt jar" | grep -v "\-injars" >> api.pro # remove default rt jar and injar lines -echo "-libraryjars '$(java -verbose 2>/dev/null | sed -ne '1 s/\[Opened \(.*\)\]/\1/p')'" >> api.pro # insert correct rt.jar location -tail api.pro # debug, print out what the previous two commands generated -cat api.pro | grep -v "\-keep class baritone.api" > standalone.pro # standalone doesn't keep baritone api - -#wget -nv https://www.dropbox.com/s/zmc2l3jnwdvzvak/tempLibraries.zip?dl=1 # i'm sorry -#mv tempLibraries.zip?dl=1 tempLibraries.zip -#unzip tempLibraries.zip - -#instead of downloading these jars from my dropbox in a zip, just assume gradle's already got them for us -mkdir -p tempLibraries -cat ../../scripts/proguard.pro | grep tempLibraries | grep .jar | cut -d "/" -f 2- | cut -d "'" -f -1 | xargs -n1 -I{} bash -c "find ~/.gradle -name {}" | tee /dev/stderr | xargs -n1 -I{} cp {} tempLibraries - -mkdir ../../dist -java -jar ../../proguard6.0.3/lib/proguard.jar @api.pro -mv Obfuscated/baritone-$VERSION.jar ../../dist/baritone-api-$VERSION.jar -java -jar ../../proguard6.0.3/lib/proguard.jar @standalone.pro -mv Obfuscated/baritone-$VERSION.jar ../../dist/baritone-standalone-$VERSION.jar -mv baritone-$VERSION.jar ../../dist/baritone-unoptimized-$VERSION.jar -cd ../../dist -shasum * | tee checksums.txt -cd .. \ No newline at end of file diff --git a/scripts/proguard.pro b/scripts/proguard.pro index 28120808..a13a0e3f 100644 --- a/scripts/proguard.pro +++ b/scripts/proguard.pro @@ -1,7 +1,3 @@ --injars baritone-0.0.3.jar --outjars Obfuscated - - -keepattributes Signature -keepattributes *Annotation* @@ -16,10 +12,14 @@ -flattenpackagehierarchy -repackageclasses 'baritone' -#-keep class baritone.behavior.** { *; } --keep class baritone.api.** { *; } -#-keep class baritone.* { *; } -#-keep class baritone.pathing.goals.** { *; } +-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 { *; } # setting names are reflected from field names, so keep field names -keepclassmembers class baritone.api.Settings { @@ -30,10 +30,11 @@ -keep class baritone.launch.** { *; } # copy all necessary libraries into tempLibraries to build --libraryjars '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/rt.jar' # this is the rt jar -libraryjars 'tempLibraries/minecraft-1.12.2.jar' +-libraryjars 'tempLibraries/SimpleTweaker-1.2.jar' + -libraryjars 'tempLibraries/authlib-1.5.25.jar' -libraryjars 'tempLibraries/codecjorbis-20101023.jar' -libraryjars 'tempLibraries/codecwav-20101023.jar' @@ -48,7 +49,6 @@ -libraryjars 'tempLibraries/httpclient-4.3.3.jar' -libraryjars 'tempLibraries/httpcore-4.3.2.jar' -libraryjars 'tempLibraries/icu4j-core-mojang-51.2.jar' --libraryjars 'tempLibraries/java-objc-bridge-1.0.0.jar' -libraryjars 'tempLibraries/jinput-2.0.5.jar' -libraryjars 'tempLibraries/jna-4.4.0.jar' -libraryjars 'tempLibraries/jopt-simple-5.0.3.jar' @@ -58,8 +58,12 @@ -libraryjars 'tempLibraries/librarylwjglopenal-20100824.jar' -libraryjars 'tempLibraries/log4j-api-2.8.1.jar' -libraryjars 'tempLibraries/log4j-core-2.8.1.jar' --libraryjars 'tempLibraries/lwjgl-2.9.4-nightly-20150209.jar' --libraryjars 'tempLibraries/lwjgl_util-2.9.4-nightly-20150209.jar' + +# startsWith is used to check the library, and mac/linux differ in which version they use +# this is FINE +-libraryjars 'tempLibraries/lwjgl-.jar' +-libraryjars 'tempLibraries/lwjgl_util-.jar' + -libraryjars 'tempLibraries/netty-all-4.1.9.Final.jar' -libraryjars 'tempLibraries/oshi-core-1.1.jar' -libraryjars 'tempLibraries/patchy-1.1.jar' diff --git a/src/api/java/baritone/api/BaritoneAPI.java b/src/api/java/baritone/api/BaritoneAPI.java index aa9491db..b9dbcd04 100644 --- a/src/api/java/baritone/api/BaritoneAPI.java +++ b/src/api/java/baritone/api/BaritoneAPI.java @@ -17,8 +17,20 @@ package baritone.api; -import baritone.api.behavior.*; +import baritone.api.behavior.ILookBehavior; +import baritone.api.behavior.IMemoryBehavior; +import baritone.api.behavior.IPathingBehavior; import baritone.api.cache.IWorldProvider; +import baritone.api.cache.IWorldScanner; +import baritone.api.event.listener.IGameEventListener; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.process.IFollowProcess; +import baritone.api.process.IGetToBlockProcess; +import baritone.api.process.IMineProcess; +import baritone.api.utils.SettingsUtil; + +import java.util.Iterator; +import java.util.ServiceLoader; /** * API exposure for various things implemented in Baritone. @@ -28,37 +40,38 @@ import baritone.api.cache.IWorldProvider; * @author Brady * @since 9/23/2018 */ -public class BaritoneAPI { +public final class BaritoneAPI { - // General - private static final Settings settings = new Settings(); - private static IWorldProvider worldProvider; + private static final IBaritone baritone; + private static final Settings settings; - // Behaviors - private static IFollowBehavior followBehavior; - private static ILookBehavior lookBehavior; - private static IMemoryBehavior memoryBehavior; - private static IMineBehavior mineBehavior; - private static IPathingBehavior pathingBehavior; + static { + ServiceLoader baritoneLoader = ServiceLoader.load(IBaritoneProvider.class); + Iterator instances = baritoneLoader.iterator(); + baritone = instances.next().getBaritoneForPlayer(null); // PWNAGE - public static IFollowBehavior getFollowBehavior() { - return followBehavior; + settings = new Settings(); + SettingsUtil.readAndApply(settings); + } + + public static IFollowProcess getFollowProcess() { + return baritone.getFollowProcess(); } public static ILookBehavior getLookBehavior() { - return lookBehavior; + return baritone.getLookBehavior(); } public static IMemoryBehavior getMemoryBehavior() { - return memoryBehavior; + return baritone.getMemoryBehavior(); } - public static IMineBehavior getMineBehavior() { - return mineBehavior; + public static IMineProcess getMineProcess() { + return baritone.getMineProcess(); } public static IPathingBehavior getPathingBehavior() { - return pathingBehavior; + return baritone.getPathingBehavior(); } public static Settings getSettings() { @@ -66,34 +79,22 @@ public class BaritoneAPI { } public static IWorldProvider getWorldProvider() { - return worldProvider; + return baritone.getWorldProvider(); } - /** - * FOR INTERNAL USE ONLY - */ - public static void registerProviders( - IWorldProvider worldProvider - ) { - BaritoneAPI.worldProvider = worldProvider; + public static IWorldScanner getWorldScanner() { + return baritone.getWorldScanner(); } - /** - * FOR INTERNAL USE ONLY - */ - // @formatter:off - public static void registerDefaultBehaviors( - IFollowBehavior followBehavior, - ILookBehavior lookBehavior, - IMemoryBehavior memoryBehavior, - IMineBehavior mineBehavior, - IPathingBehavior pathingBehavior - ) { - BaritoneAPI.followBehavior = followBehavior; - BaritoneAPI.lookBehavior = lookBehavior; - BaritoneAPI.memoryBehavior = memoryBehavior; - BaritoneAPI.mineBehavior = mineBehavior; - BaritoneAPI.pathingBehavior = pathingBehavior; + public static ICustomGoalProcess getCustomGoalProcess() { + return baritone.getCustomGoalProcess(); + } + + public static IGetToBlockProcess getGetToBlockProcess() { + return baritone.getGetToBlockProcess(); + } + + public static void registerEventListener(IGameEventListener listener) { + baritone.registerEventListener(listener); } - // @formatter:on } diff --git a/src/api/java/baritone/api/IBaritone.java b/src/api/java/baritone/api/IBaritone.java new file mode 100644 index 00000000..aee9dead --- /dev/null +++ b/src/api/java/baritone/api/IBaritone.java @@ -0,0 +1,89 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api; + +import baritone.api.behavior.ILookBehavior; +import baritone.api.behavior.IMemoryBehavior; +import baritone.api.behavior.IPathingBehavior; +import baritone.api.cache.IWorldProvider; +import baritone.api.cache.IWorldScanner; +import baritone.api.event.listener.IGameEventListener; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.process.IFollowProcess; +import baritone.api.process.IGetToBlockProcess; +import baritone.api.process.IMineProcess; + +/** + * @author Brady + * @since 9/29/2018 + */ +public interface IBaritone { + + /** + * @return The {@link IFollowProcess} instance + * @see IFollowProcess + */ + IFollowProcess getFollowProcess(); + + /** + * @return The {@link ILookBehavior} instance + * @see ILookBehavior + */ + ILookBehavior getLookBehavior(); + + /** + * @return The {@link IMemoryBehavior} instance + * @see IMemoryBehavior + */ + IMemoryBehavior getMemoryBehavior(); + + /** + * @return The {@link IMineProcess} instance + * @see IMineProcess + */ + IMineProcess getMineProcess(); + + /** + * @return The {@link IPathingBehavior} instance + * @see IPathingBehavior + */ + IPathingBehavior getPathingBehavior(); + + /** + * @return The {@link IWorldProvider} instance + * @see IWorldProvider + */ + IWorldProvider getWorldProvider(); + + /** + * @return The {@link IWorldScanner} instance + * @see IWorldScanner + */ + IWorldScanner getWorldScanner(); + + ICustomGoalProcess getCustomGoalProcess(); + + IGetToBlockProcess getGetToBlockProcess(); + + /** + * Registers a {@link IGameEventListener} with Baritone's "event bus". + * + * @param listener The listener + */ + void registerEventListener(IGameEventListener listener); +} diff --git a/src/api/java/baritone/api/IBaritoneProvider.java b/src/api/java/baritone/api/IBaritoneProvider.java new file mode 100644 index 00000000..745842ce --- /dev/null +++ b/src/api/java/baritone/api/IBaritoneProvider.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; + +import net.minecraft.client.entity.EntityPlayerSP; + +/** + * @author Leijurv + */ +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. + * + * @param player The player + * @return The {@link IBaritone} instance. + */ + IBaritone getBaritoneForPlayer(EntityPlayerSP player); +} diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index fdbaaf45..7fce0254 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -56,6 +56,13 @@ public class Settings { */ public 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); + /** * Allow Baritone to fall arbitrary distances and place a water bucket beneath it. * Reliability: questionable. @@ -149,7 +156,7 @@ public class Settings { * * @see Issue #18 */ - public Setting backtrackCostFavoringCoefficient = new Setting<>(0.9); + public Setting backtrackCostFavoringCoefficient = new Setting<>(0.5); /** * Don't repropagate cost improvements below 0.01 ticks. They're all just floating point inaccuracies, @@ -191,7 +198,7 @@ public class Settings { /** * Start planning the next path once the remaining movements tick estimates sum up to less than this value */ - public Setting planningTickLookAhead = new Setting<>(100); + public Setting planningTickLookAhead = new Setting<>(150); /** * Default size of the Long2ObjectOpenHashMap used in pathing @@ -269,6 +276,13 @@ public class Settings { */ public 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 + */ + public Setting pruneRegionsFromRAM = new Setting<>(false); + /** * Print all the debug messages to chat */ @@ -295,6 +309,21 @@ public class Settings { */ public Setting renderGoal = new Setting<>(true); + /** + * Ignore depth when rendering the goal + */ + public Setting renderGoalIgnoreDepth = new Setting<>(true); + + /** + * Ignore depth when rendering the selection boxes (to break, to place, to walk into) + */ + public Setting renderSelectionBoxesIgnoreDepth = new Setting<>(true); + + /** + * Ignore depth when rendering the path + */ + public Setting renderPathIgnoreDepth = new Setting<>(true); + /** * Line width of the path when rendered, in pixels */ @@ -348,12 +377,27 @@ public class Settings { */ public 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 + */ + public 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); + /** * Rescan for the goal once every 5 ticks. * Set to 0 to disable. */ public Setting mineGoalUpdateInterval = 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); + /** * Cancel the current path if the goal has changed, and the path originally ended in the goal but doesn't anymore. *

@@ -374,6 +418,18 @@ public class Settings { */ public 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 + */ + public Setting legitMine = new Setting<>(false); + + /** + * What Y level to go to for legit strip mining + */ + public Setting legitMineYLevel = new Setting<>(11); + /** * 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 @@ -404,6 +460,28 @@ public class Settings { */ public Setting followRadius = new Setting<>(3); + /** + * Cached chunks (regardless of if they're in RAM or saved to disk) expire and are deleted after this number of seconds + * -1 to disable + *

+ * I would highly suggest leaving this setting disabled (-1). + *

+ * The only valid reason I can think of enable this setting is if you are extremely low on disk space and you play on multiplayer, + * and can't take (average) 300kb saved for every 512x512 area. (note that more complicated terrain is less compressible and will take more space) + *

+ * However, simply discarding old chunks because they are old is inadvisable. Baritone is extremely good at correcting + * itself and its paths as it learns new information, as new chunks load. There is no scenario in which having an + * incorrect cache can cause Baritone to get stuck, take damage, or perform any action it wouldn't otherwise, everything + * is rechecked once the real chunk is in range. + *

+ * Having a robust cache greatly improves long distance pathfinding, as it's able to go around large scale obstacles + * before they're in render distance. In fact, when the chunkCaching setting is disabled and Baritone starts anew + * every time, or when you enter a completely new and very complicated area, it backtracks far more often because it + * 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); + /** * 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 @@ -451,11 +529,19 @@ public class Settings { */ public Setting colorGoalBox = new Setting<>(Color.GREEN); + /** + * A map of lowercase setting field names to their respective setting + */ public final Map> byLowerName; + + /** + * A list of all settings + */ public final List> allSettings; public class Setting { public T value; + public final T defaultValue; private String name; private final Class klass; @@ -465,6 +551,7 @@ public class Settings { throw new IllegalArgumentException("Cannot determine value type class from null"); } this.value = value; + this.defaultValue = value; this.klass = (Class) value.getClass(); } @@ -488,7 +575,7 @@ public class Settings { // here be dragons - { + Settings() { Field[] temp = getClass().getFields(); HashMap> tmpByName = new HashMap<>(); List> tmpAll = new ArrayList<>(); @@ -523,6 +610,4 @@ public class Settings { } return result; } - - Settings() { } } diff --git a/src/api/java/baritone/api/behavior/IBehavior.java b/src/api/java/baritone/api/behavior/IBehavior.java index aee144e2..248148e7 100644 --- a/src/api/java/baritone/api/behavior/IBehavior.java +++ b/src/api/java/baritone/api/behavior/IBehavior.java @@ -18,10 +18,9 @@ package baritone.api.behavior; import baritone.api.event.listener.AbstractGameEventListener; -import baritone.api.utils.interfaces.Toggleable; /** * @author Brady * @since 9/23/2018 */ -public interface IBehavior extends AbstractGameEventListener, Toggleable {} +public interface IBehavior extends AbstractGameEventListener {} diff --git a/src/api/java/baritone/api/behavior/ILookBehavior.java b/src/api/java/baritone/api/behavior/ILookBehavior.java index b5b5d63b..058a5dd8 100644 --- a/src/api/java/baritone/api/behavior/ILookBehavior.java +++ b/src/api/java/baritone/api/behavior/ILookBehavior.java @@ -33,7 +33,7 @@ public interface ILookBehavior extends IBehavior { * otherwise, it should be {@code false}; * * @param rotation The target rotations - * @param force Whether or not to "force" the rotations + * @param force Whether or not to "force" the rotations */ void updateTarget(Rotation rotation, boolean force); } diff --git a/src/api/java/baritone/api/behavior/IMemoryBehavior.java b/src/api/java/baritone/api/behavior/IMemoryBehavior.java index ea5e9007..6b6df1dd 100644 --- a/src/api/java/baritone/api/behavior/IMemoryBehavior.java +++ b/src/api/java/baritone/api/behavior/IMemoryBehavior.java @@ -20,6 +20,8 @@ package baritone.api.behavior; import baritone.api.behavior.memory.IRememberedInventory; import net.minecraft.util.math.BlockPos; +import java.util.Map; + /** * @author Brady * @since 9/23/2018 @@ -33,4 +35,11 @@ public interface IMemoryBehavior extends IBehavior { * @return The remembered inventory */ IRememberedInventory getInventoryByPos(BlockPos pos); + + /** + * Gets the map of all block positions to their remembered inventories. + * + * @return Map of block positions to their respective remembered inventories + */ + Map getRememberedInventories(); } diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java index e21d999d..e9ebe405 100644 --- a/src/api/java/baritone/api/behavior/IPathingBehavior.java +++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java @@ -17,7 +17,10 @@ package baritone.api.behavior; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.path.IPathExecutor; import java.util.Optional; @@ -36,33 +39,49 @@ public interface IPathingBehavior extends IBehavior { */ Optional ticksRemainingInSegment(); - /** - * Sets the pathing goal. - * - * @param goal The pathing goal - */ - void setGoal(Goal goal); - /** * @return The current pathing goal */ Goal getGoal(); - /** - * Begins pathing. Calculation will start in a new thread, and once completed, - * movement will commence. Returns whether or not the operation was successful. - * - * @return Whether or not the operation was successful - */ - boolean path(); - /** * @return Whether or not a path is currently being executed. */ boolean isPathing(); /** - * Cancels the pathing behavior or the current path calculation. + * Cancels the pathing behavior or the current path calculation, and all processes that could be controlling path. + *

+ * Basically, "MAKE IT STOP". + * + * @return Whether or not the pathing behavior was canceled. All processes are guaranteed to be canceled, but the + * PathingBehavior might be in the middle of an uncancelable action like a parkour jump */ - void cancel(); + boolean cancelEverything(); + + /** + * Returns the current path, from the current path executor, if there is one. + * + * @return The current path + */ + default Optional getPath() { + return Optional.ofNullable(getCurrent()).map(IPathExecutor::getPath); + } + + /** + * @return The current path finder being executed + */ + Optional getPathFinder(); + + /** + * @return The current path executor + */ + IPathExecutor getCurrent(); + + /** + * Returns the next path executor, created when planning ahead. + * + * @return The next path executor + */ + IPathExecutor getNext(); } diff --git a/src/api/java/baritone/api/cache/ICachedRegion.java b/src/api/java/baritone/api/cache/ICachedRegion.java index 5f9199fa..6b9048a5 100644 --- a/src/api/java/baritone/api/cache/ICachedRegion.java +++ b/src/api/java/baritone/api/cache/ICachedRegion.java @@ -29,11 +29,10 @@ public interface ICachedRegion extends IBlockTypeAccess { * however, the block coordinates should in on a scale from 0 to 511 (inclusive) * because region sizes are 512x512 blocks. * - * @see ICachedWorld#isCached(int, int) - * * @param blockX The block X coordinate * @param blockZ The block Z coordinate * @return Whether or not the specified XZ location is cached + * @see ICachedWorld#isCached(int, int) */ boolean isCached(int blockX, int blockZ); diff --git a/src/api/java/baritone/api/cache/ICachedWorld.java b/src/api/java/baritone/api/cache/ICachedWorld.java index f8196ebb..5e06a475 100644 --- a/src/api/java/baritone/api/cache/ICachedWorld.java +++ b/src/api/java/baritone/api/cache/ICachedWorld.java @@ -61,8 +61,8 @@ public interface ICachedWorld { * information that is returned by this method may not be up to date, because * older cached chunks can contain data that is much more likely to have changed. * - * @param block The special block to search for - * @param maximum The maximum number of position results to receive + * @param block The special block to search for + * @param maximum The maximum number of position results to receive * @param maxRegionDistanceSq The maximum region distance, squared * @return The locations found that match the special block */ diff --git a/src/api/java/baritone/api/cache/IWaypointCollection.java b/src/api/java/baritone/api/cache/IWaypointCollection.java index 051b199e..4dd80a3e 100644 --- a/src/api/java/baritone/api/cache/IWaypointCollection.java +++ b/src/api/java/baritone/api/cache/IWaypointCollection.java @@ -50,19 +50,17 @@ public interface IWaypointCollection { /** * Gets all of the waypoints that have the specified tag * - * @see IWaypointCollection#getAllWaypoints() - * * @param tag The tag * @return All of the waypoints with the specified tag + * @see IWaypointCollection#getAllWaypoints() */ Set getByTag(IWaypoint.Tag tag); /** * Gets all of the waypoints in this collection, regardless of the tag. * - * @see IWaypointCollection#getByTag(IWaypoint.Tag) - * * @return All of the waypoints in this collection + * @see IWaypointCollection#getByTag(IWaypoint.Tag) */ Set getAllWaypoints(); } diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java new file mode 100644 index 00000000..510e69ed --- /dev/null +++ b/src/api/java/baritone/api/cache/IWorldScanner.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.cache; + +import net.minecraft.block.Block; +import net.minecraft.util.math.BlockPos; + +import java.util.List; + +/** + * @author Brady + * @since 10/6/2018 + */ +public interface IWorldScanner { + + /** + * Scans the world, up to the specified max chunk radius, for the specified blocks. + * + * @param blocks 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 maxSearchRadius The maximum chunk search radius + * @return The matching block positions + */ + List scanChunkRadius(List blocks, int max, int yLevelThreshold, int maxSearchRadius); +} diff --git a/src/api/java/baritone/api/event/events/BlockInteractEvent.java b/src/api/java/baritone/api/event/events/BlockInteractEvent.java index cc98a6ba..fbdadba1 100644 --- a/src/api/java/baritone/api/event/events/BlockInteractEvent.java +++ b/src/api/java/baritone/api/event/events/BlockInteractEvent.java @@ -20,6 +20,8 @@ package baritone.api.event.events; import net.minecraft.util.math.BlockPos; /** + * Called when the local player interacts with a block, can be either {@link Type#BREAK} or {@link Type#USE}. + * * @author Brady * @since 8/22/2018 */ diff --git a/src/api/java/baritone/api/event/events/ChatEvent.java b/src/api/java/baritone/api/event/events/ChatEvent.java index e103377f..ede83f24 100644 --- a/src/api/java/baritone/api/event/events/ChatEvent.java +++ b/src/api/java/baritone/api/event/events/ChatEvent.java @@ -17,20 +17,22 @@ package baritone.api.event.events; -import baritone.api.event.events.type.Cancellable; +import baritone.api.event.events.type.ManagedPlayerEvent; +import net.minecraft.client.entity.EntityPlayerSP; /** * @author Brady * @since 8/1/2018 6:39 PM */ -public final class ChatEvent extends Cancellable { +public final class ChatEvent extends ManagedPlayerEvent.Cancellable { /** * The message being sent */ private final String message; - public ChatEvent(String message) { + public ChatEvent(EntityPlayerSP player, String message) { + super(player); this.message = message; } diff --git a/src/api/java/baritone/api/event/events/PacketEvent.java b/src/api/java/baritone/api/event/events/PacketEvent.java index 9516db4b..9a3d2315 100644 --- a/src/api/java/baritone/api/event/events/PacketEvent.java +++ b/src/api/java/baritone/api/event/events/PacketEvent.java @@ -18,6 +18,7 @@ package baritone.api.event.events; import baritone.api.event.events.type.EventState; +import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; /** @@ -26,15 +27,22 @@ import net.minecraft.network.Packet; */ public final class PacketEvent { + private final NetworkManager networkManager; + private final EventState state; private final Packet packet; - public PacketEvent(EventState state, Packet packet) { + public PacketEvent(NetworkManager networkManager, EventState state, Packet packet) { + this.networkManager = networkManager; this.state = state; this.packet = packet; } + public final NetworkManager getNetworkManager() { + return this.networkManager; + } + public final EventState getState() { return this.state; } diff --git a/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java b/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java index 6ac08bdd..99370552 100644 --- a/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java +++ b/src/api/java/baritone/api/event/events/PlayerUpdateEvent.java @@ -18,19 +18,22 @@ 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 { +public final class PlayerUpdateEvent extends ManagedPlayerEvent { /** * The state of the event */ private final EventState state; - public PlayerUpdateEvent(EventState state) { + public PlayerUpdateEvent(EntityPlayerSP player, EventState state) { + super(player); 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 ef6d9b7e..790318e0 100644 --- a/src/api/java/baritone/api/event/events/RotationMoveEvent.java +++ b/src/api/java/baritone/api/event/events/RotationMoveEvent.java @@ -17,7 +17,8 @@ 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; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; @@ -25,7 +26,7 @@ import net.minecraft.entity.EntityLivingBase; * @author Brady * @since 8/21/2018 */ -public final class RotationMoveEvent { +public final class RotationMoveEvent extends ManagedPlayerEvent { /** * The type of event @@ -33,20 +34,30 @@ public final class RotationMoveEvent { private final Type type; /** - * The state of the event + * The yaw rotation */ - private final EventState state; + private float yaw; - public RotationMoveEvent(EventState state, Type type) { - this.state = state; + public RotationMoveEvent(EntityPlayerSP player, Type type, float yaw) { + super(player); this.type = type; + this.yaw = yaw; } /** - * @return The state of the event + * Set the yaw movement rotation + * + * @param yaw Yaw rotation */ - public final EventState getState() { - return this.state; + public final void setYaw(float yaw) { + this.yaw = yaw; + } + + /** + * @return The yaw rotation + */ + public final float getYaw() { + return this.yaw; } /** diff --git a/src/api/java/baritone/api/event/events/type/Cancellable.java b/src/api/java/baritone/api/event/events/type/Cancellable.java index 331870c6..62ff9049 100644 --- a/src/api/java/baritone/api/event/events/type/Cancellable.java +++ b/src/api/java/baritone/api/event/events/type/Cancellable.java @@ -21,23 +21,19 @@ package baritone.api.event.events.type; * @author Brady * @since 8/1/2018 6:41 PM */ -public class Cancellable { +public class Cancellable implements ICancellable { /** * Whether or not this event has been cancelled */ private boolean cancelled; - /** - * Cancels this event - */ + @Override public final void cancel() { this.cancelled = true; } - /** - * @return Whether or not this event has been cancelled - */ + @Override public final boolean isCancelled() { return this.cancelled; } diff --git a/src/api/java/baritone/api/event/events/type/EventState.java b/src/api/java/baritone/api/event/events/type/EventState.java index 10b633bd..072e12c1 100644 --- a/src/api/java/baritone/api/event/events/type/EventState.java +++ b/src/api/java/baritone/api/event/events/type/EventState.java @@ -24,14 +24,12 @@ package baritone.api.event.events.type; public enum EventState { /** - * Indicates that whatever movement the event is being - * dispatched as a result of is about to occur. + * Before the dispatching of what the event is targetting */ PRE, /** - * Indicates that whatever movement the event is being - * dispatched as a result of has already occurred. + * After the dispatching of what the event is targetting */ POST } diff --git a/src/api/java/baritone/api/behavior/IFollowBehavior.java b/src/api/java/baritone/api/event/events/type/ICancellable.java similarity index 63% rename from src/api/java/baritone/api/behavior/IFollowBehavior.java rename to src/api/java/baritone/api/event/events/type/ICancellable.java index c960fab3..1df5cd79 100644 --- a/src/api/java/baritone/api/behavior/IFollowBehavior.java +++ b/src/api/java/baritone/api/event/events/type/ICancellable.java @@ -15,30 +15,21 @@ * along with Baritone. If not, see . */ -package baritone.api.behavior; - -import net.minecraft.entity.Entity; +package baritone.api.event.events.type; /** * @author Brady - * @since 9/23/2018 + * @since 10/11/2018 */ -public interface IFollowBehavior extends IBehavior { +public interface ICancellable { /** - * Set the follow target to the specified entity; - * - * @param entity The entity to follow - */ - void follow(Entity entity); - - /** - * @return The entity that is currently being followed - */ - Entity following(); - - /** - * Cancels the follow behavior, this will clear the current follow target. + * Cancels this event */ void cancel(); + + /** + * @return Whether or not this event has been cancelled + */ + boolean isCancelled(); } diff --git a/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.java b/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.java new file mode 100644 index 00000000..a3487a67 --- /dev/null +++ b/src/api/java/baritone/api/event/events/type/ManagedPlayerEvent.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.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/pathing/calc/IPath.java b/src/api/java/baritone/api/pathing/calc/IPath.java new file mode 100644 index 00000000..0844ab90 --- /dev/null +++ b/src/api/java/baritone/api/pathing/calc/IPath.java @@ -0,0 +1,168 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for 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.calc; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.movement.IMovement; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.List; + +/** + * @author leijurv, Brady + */ +public interface IPath { + + /** + * Ordered list of movements to carry out. + * movements.get(i).getSrc() should equal positions.get(i) + * movements.get(i).getDest() should equal positions.get(i+1) + * movements.size() should equal positions.size()-1 + * + * @return All of the movements to carry out + */ + List movements(); + + /** + * All positions along the way. + * Should begin with the same as getSrc and end with the same as getDest + * + * @return All of the positions along this path + */ + List positions(); + + /** + * This path is actually going to be executed in the world. Do whatever additional processing is required. + * (as opposed to Path objects that are just constructed every frame for rendering) + */ + default IPath postProcess() { + throw new UnsupportedOperationException(); + } + + /** + * Returns the number of positions in this path. Equivalent to {@code positions().size()}. + * + * @return Number of positions in this path + */ + default int length() { + return positions().size(); + } + + /** + * @return The goal that this path was calculated towards + */ + Goal getGoal(); + + /** + * Returns the number of nodes that were considered during calculation before + * this path was found. + * + * @return The number of nodes that were considered before finding this path + */ + int getNumNodesConsidered(); + + /** + * Returns the start position of this path. This is the first element in the + * {@link List} that is returned by {@link IPath#positions()}. + * + * @return The start position of this path + */ + default BetterBlockPos getSrc() { + return positions().get(0); + } + + /** + * Returns the end position of this path. This is the last element in the + * {@link List} that is returned by {@link IPath#positions()}. + * + * @return The end position of this path. + */ + default BetterBlockPos getDest() { + List pos = positions(); + return pos.get(pos.size() - 1); + } + + /** + * Returns the estimated number of ticks to complete the path from the given node index. + * + * @param pathPosition The index of the node we're calculating from + * @return The estimated number of ticks remaining frm the given position + */ + default double ticksRemainingFrom(int pathPosition) { + double sum = 0; + //this is fast because we aren't requesting recalculation, it's just cached + for (int i = pathPosition; i < movements().size(); i++) { + sum += movements().get(i).getCost(); + } + return sum; + } + + /** + * Cuts off this path at the loaded chunk border, and returns the resulting path. Default + * implementation just returns this path, without the intended functionality. + * + * @return The result of this cut-off operation + */ + default IPath cutoffAtLoadedChunks(World world) { + throw new UnsupportedOperationException(); + } + + /** + * Cuts off this path using the min length and cutoff factor settings, and returns the resulting path. + * Default implementation just returns this path, without the intended functionality. + * + * @return The result of this cut-off operation + * @see Settings#pathCutoffMinimumLength + * @see Settings#pathCutoffFactor + */ + default IPath staticCutoff(Goal destination) { + throw new UnsupportedOperationException(); + } + + + /** + * Performs a series of checks to ensure that the assembly of the path went as expected. + */ + default void sanityCheck() { + List path = positions(); + List movements = movements(); + if (!getSrc().equals(path.get(0))) { + throw new IllegalStateException("Start node does not equal first path element"); + } + if (!getDest().equals(path.get(path.size() - 1))) { + throw new IllegalStateException("End node does not equal last path element"); + } + if (path.size() != movements.size() + 1) { + throw new IllegalStateException("Size of path array is unexpected"); + } + for (int i = 0; i < path.size() - 1; i++) { + BlockPos src = path.get(i); + BlockPos dest = path.get(i + 1); + IMovement movement = movements.get(i); + if (!src.equals(movement.getSrc())) { + throw new IllegalStateException("Path source is not equal to the movement source"); + } + if (!dest.equals(movement.getDest())) { + throw new IllegalStateException("Path destination is not equal to the movement destination"); + } + } + } +} diff --git a/src/main/java/baritone/pathing/calc/IPathFinder.java b/src/api/java/baritone/api/pathing/calc/IPathFinder.java similarity index 91% rename from src/main/java/baritone/pathing/calc/IPathFinder.java rename to src/api/java/baritone/api/pathing/calc/IPathFinder.java index 6ed2b3a7..f70196a6 100644 --- a/src/main/java/baritone/pathing/calc/IPathFinder.java +++ b/src/api/java/baritone/api/pathing/calc/IPathFinder.java @@ -15,11 +15,10 @@ * along with Baritone. If not, see . */ -package baritone.pathing.calc; +package baritone.api.pathing.calc; import baritone.api.pathing.goals.Goal; -import baritone.pathing.path.IPath; -import net.minecraft.util.math.BlockPos; +import baritone.api.utils.PathCalculationResult; import java.util.Optional; @@ -30,8 +29,6 @@ import java.util.Optional; */ public interface IPathFinder { - BlockPos getStart(); - Goal getGoal(); /** @@ -39,7 +36,7 @@ public interface IPathFinder { * * @return The final path */ - Optional calculate(long timeout); + PathCalculationResult calculate(long timeout); /** * Intended to be called concurrently with calculatePath from a different thread to tell if it's finished yet diff --git a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java index cb7a000e..44c5fa80 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java +++ b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java @@ -20,6 +20,7 @@ package baritone.api.pathing.goals; import net.minecraft.util.math.BlockPos; import java.util.Arrays; +import java.util.Optional; /** * Useful for automated combat (retreating specifically) @@ -32,16 +33,26 @@ public class GoalRunAway implements Goal { private final double distanceSq; + private final Optional maintainY; + public GoalRunAway(double distance, BlockPos... from) { + this(distance, Optional.empty(), from); + } + + public GoalRunAway(double distance, Optional maintainY, BlockPos... from) { if (from.length == 0) { throw new IllegalArgumentException(); } this.from = from; this.distanceSq = distance * distance; + this.maintainY = maintainY; } @Override public boolean isInGoal(int x, int y, int z) { + if (maintainY.isPresent() && maintainY.get() != y) { + return false; + } for (BlockPos p : from) { int diffX = x - p.getX(); int diffZ = z - p.getZ(); @@ -62,11 +73,19 @@ public class GoalRunAway implements Goal { min = h; } } - return -min; + min = -min; + if (maintainY.isPresent()) { + min = min * 0.6 + GoalYLevel.calculate(maintainY.get(), y) * 1.5; + } + return min; } @Override public String toString() { - return "GoalRunAwayFrom" + Arrays.asList(from); + if (maintainY.isPresent()) { + return "GoalRunAwayFromMaintainY y=" + maintainY.get() + ", " + Arrays.asList(from); + } else { + return "GoalRunAwayFrom" + Arrays.asList(from); + } } } diff --git a/src/api/java/baritone/api/pathing/movement/ActionCosts.java b/src/api/java/baritone/api/pathing/movement/ActionCosts.java index e1f72dfa..683b1b10 100644 --- a/src/api/java/baritone/api/pathing/movement/ActionCosts.java +++ b/src/api/java/baritone/api/pathing/movement/ActionCosts.java @@ -23,21 +23,21 @@ public interface ActionCosts { * These costs are measured roughly in ticks btw */ double WALK_ONE_BLOCK_COST = 20 / 4.317; // 4.633 - double WALK_ONE_IN_WATER_COST = 20 / 2.2; + double WALK_ONE_IN_WATER_COST = 20 / 2.2; // 9.091 double WALK_ONE_OVER_SOUL_SAND_COST = WALK_ONE_BLOCK_COST * 2; // 0.4 in BlockSoulSand but effectively about half - double SPRINT_ONE_OVER_SOUL_SAND_COST = WALK_ONE_OVER_SOUL_SAND_COST * 0.75; - double LADDER_UP_ONE_COST = 20 / 2.35; - double LADDER_DOWN_ONE_COST = 20 / 3.0; - double SNEAK_ONE_BLOCK_COST = 20 / 1.3; + double LADDER_UP_ONE_COST = 20 / 2.35; // 8.511 + double LADDER_DOWN_ONE_COST = 20 / 3.0; // 6.667 + double SNEAK_ONE_BLOCK_COST = 20 / 1.3; // 15.385 double SPRINT_ONE_BLOCK_COST = 20 / 5.612; // 3.564 + double SPRINT_MULTIPLIER = SPRINT_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST; // 0.769 /** * To walk off an edge you need to walk 0.5 to the edge then 0.3 to start falling off */ - double WALK_OFF_BLOCK_COST = WALK_ONE_BLOCK_COST * 0.8; + double WALK_OFF_BLOCK_COST = WALK_ONE_BLOCK_COST * 0.8; // 3.706 /** * To walk the rest of the way to be centered on the new block */ - double CENTER_AFTER_FALL_COST = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST; + double CENTER_AFTER_FALL_COST = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST; // 0.927 /** * don't make this Double.MAX_VALUE because it's added to other things, maybe other COST_INFs, diff --git a/src/api/java/baritone/api/pathing/movement/IMovement.java b/src/api/java/baritone/api/pathing/movement/IMovement.java new file mode 100644 index 00000000..7b3eca5f --- /dev/null +++ b/src/api/java/baritone/api/pathing/movement/IMovement.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.pathing.movement; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.math.BlockPos; + +import java.util.List; + +/** + * @author Brady + * @since 10/8/2018 + */ +public interface IMovement { + + double getCost(); + + MovementStatus update(); + + /** + * Resets the current state status to {@link MovementStatus#PREPPING} + */ + void reset(); + + /** + * Resets the cache for special break, place, and walk into blocks + */ + void resetBlockCache(); + + /** + * @return Whether or not it is safe to cancel the current movement state + */ + boolean safeToCancel(); + + double recalculateCost(); + + double calculateCostWithoutCaching(); + + boolean calculatedWhileLoaded(); + + BetterBlockPos getSrc(); + + BetterBlockPos getDest(); + + BlockPos getDirection(); + + List toBreak(); + + List toPlace(); + + List toWalkInto(); +} diff --git a/src/api/java/baritone/api/pathing/movement/MovementStatus.java b/src/api/java/baritone/api/pathing/movement/MovementStatus.java new file mode 100644 index 00000000..0190f8e1 --- /dev/null +++ b/src/api/java/baritone/api/pathing/movement/MovementStatus.java @@ -0,0 +1,74 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.pathing.movement; + +/** + * @author Brady + * @since 10/8/2018 + */ +public enum MovementStatus { + + /** + * We are preparing the movement to be executed. This is when any blocks obstructing the destination are broken. + */ + PREPPING(false), + + /** + * We are waiting for the movement to begin, after {@link MovementStatus#PREPPING}. + */ + WAITING(false), + + /** + * The movement is currently in progress, after {@link MovementStatus#WAITING} + */ + RUNNING(false), + + /** + * The movement has been completed and we are at our destination + */ + SUCCESS(true), + + /** + * There was a change in state between calculation and actual + * movement execution, and the movement has now become impossible. + */ + UNREACHABLE(true), + + /** + * Unused + */ + FAILED(true), + + /** + * "Unused" + */ + CANCELED(true); + + /** + * Whether or not this status indicates a complete movement. + */ + private final boolean complete; + + MovementStatus(boolean complete) { + this.complete = complete; + } + + public final boolean isComplete() { + return this.complete; + } +} diff --git a/src/api/java/baritone/api/pathing/path/IPathExecutor.java b/src/api/java/baritone/api/pathing/path/IPathExecutor.java new file mode 100644 index 00000000..f72060dc --- /dev/null +++ b/src/api/java/baritone/api/pathing/path/IPathExecutor.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.pathing.path; + +import baritone.api.pathing.calc.IPath; + +/** + * @author Brady + * @since 10/8/2018 + */ +public interface IPathExecutor { + + IPath getPath(); +} diff --git a/src/api/java/baritone/api/process/IBaritoneProcess.java b/src/api/java/baritone/api/process/IBaritoneProcess.java new file mode 100644 index 00000000..db7588ab --- /dev/null +++ b/src/api/java/baritone/api/process/IBaritoneProcess.java @@ -0,0 +1,99 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for 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.IBaritone; +import baritone.api.behavior.IPathingBehavior; +import baritone.api.event.events.PathEvent; + +/** + * A process that can control the PathingBehavior. + *

+ * 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 + *

+ * That's it actually + * + * @author leijurv + */ +public interface IBaritoneProcess { + + /** + * Would this process like to be in control? + * + * @return Whether or not this process would like to be in contorl. + */ + boolean isActive(); + + /** + * Called when this process is in control of pathing; Returns what Baritone should do. + * + * @param calcFailed {@code true} if this specific process was in control last tick, + * and there was a {@link PathEvent#CALC_FAILED} event last tick + * @param isSafeToCancel {@code true} if a {@link PathingCommandType#REQUEST_PAUSE} would happen this tick, and + * {@link IPathingBehavior} wouldn't actually tick. {@code false} if the PathExecutor reported + * pausing would be unsafe at the end of the last tick. Effectively "could request cancel or + * pause and have it happen right away" + * @return What the {@link IPathingBehavior} should do + */ + PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel); + + /** + * Returns whether or not this process should be treated as "temporary". + *

+ * If a process is temporary, it doesn't call {@link #onLostControl} on the processes that aren't execute because of it. + *

+ * For example, {@code CombatPauserProcess} and {@code PauseForAutoEatProcess} should return {@code true} always, + * and should return {@link #isActive} {@code true} only if there's something in range this tick, or if the player would like + * to start eating this tick. {@code PauseForAutoEatProcess} should only actually right click once onTick is called with + * {@code isSafeToCancel} true though. + * + * @return Whethor or not if this control is temporary + */ + boolean isTemporary(); + + /** + * Called if {@link #isActive} returned {@code true}, but another non-temporary + * process has control. Effectively the same as cancel. You want control but you + * don't get it. + */ + void onLostControl(); + + /** + * Used to determine which Process gains control if multiple are reporting {@link #isActive()}. The one + * that returns the highest value will be given control. + * + * @return A double representing the priority + */ + double priority(); + + /** + * Returns which bot this process is associated with. (5000000iq forward thinking) + * + * @return The Bot associated with this process + */ + IBaritone associatedWith(); + + /** + * Returns a user-friendly name for this process. Suitable for a HUD. + * + * @return A display name that's suitable for a HUD + */ + String displayName(); +} diff --git a/src/api/java/baritone/api/utils/interfaces/Toggleable.java b/src/api/java/baritone/api/process/ICustomGoalProcess.java similarity index 54% rename from src/api/java/baritone/api/utils/interfaces/Toggleable.java rename to src/api/java/baritone/api/process/ICustomGoalProcess.java index 359d6ee1..5084aff2 100644 --- a/src/api/java/baritone/api/utils/interfaces/Toggleable.java +++ b/src/api/java/baritone/api/process/ICustomGoalProcess.java @@ -15,40 +15,36 @@ * along with Baritone. If not, see . */ -package baritone.api.utils.interfaces; +package baritone.api.process; -/** - * @author Brady - * @since 8/20/2018 - */ -public interface Toggleable { +import baritone.api.pathing.goals.Goal; + +public interface ICustomGoalProcess extends IBaritoneProcess { /** - * Toggles the enabled state of this {@link Toggleable}. + * Sets the pathing goal * - * @return The new state. + * @param goal The new goal */ - boolean toggle(); + void setGoal(Goal goal); /** - * Sets the enabled state of this {@link Toggleable}. + * Starts path calculation and execution. + */ + void path(); + + /** + * @return The current goal + */ + Goal getGoal(); + + /** + * Sets the goal and begins the path execution. * - * @return The new state. + * @param goal The new goal */ - boolean setEnabled(boolean enabled); - - /** - * @return Whether or not this {@link Toggleable} object is enabled - */ - boolean isEnabled(); - - /** - * Called when the state changes from disabled to enabled - */ - default void onEnable() {} - - /** - * Called when the state changes from enabled to disabled - */ - default void onDisable() {} + default void setGoalAndPath(Goal goal) { + this.setGoal(goal); + this.path(); + } } diff --git a/src/launch/java/baritone/launch/BaritoneTweakerForge.java b/src/api/java/baritone/api/process/IFollowProcess.java similarity index 50% rename from src/launch/java/baritone/launch/BaritoneTweakerForge.java rename to src/api/java/baritone/api/process/IFollowProcess.java index 7623eed5..ef869da4 100644 --- a/src/launch/java/baritone/launch/BaritoneTweakerForge.java +++ b/src/api/java/baritone/api/process/IFollowProcess.java @@ -15,30 +15,37 @@ * along with Baritone. If not, see . */ -package baritone.launch; +package baritone.api.process; -import net.minecraft.launchwrapper.LaunchClassLoader; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; +import net.minecraft.entity.Entity; -import java.io.File; -import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /** * @author Brady - * @since 7/31/2018 10:09 PM + * @since 9/23/2018 */ -public class BaritoneTweakerForge extends BaritoneTweaker { +public interface IFollowProcess extends IBaritoneProcess { - @Override - public final void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - this.args = new ArrayList<>(); - } + /** + * Set the follow target to any entities matching this predicate + * + * @param filter the predicate + */ + void follow(Predicate filter); - @Override - public final void injectIntoClassLoader(LaunchClassLoader classLoader) { - super.injectIntoClassLoader(classLoader); - MixinEnvironment.getDefaultEnvironment().setObfuscationContext(ObfuscationServiceMCP.SEARGE); + /** + * @return The entities that are currently being followed. null if not currently following, empty if nothing matches the predicate + */ + List following(); + + Predicate currentFilter(); + + /** + * Cancels the follow behavior, this will clear the current follow target. + */ + default void cancel() { + onLostControl(); } } diff --git a/src/api/java/baritone/api/process/IGetToBlockProcess.java b/src/api/java/baritone/api/process/IGetToBlockProcess.java new file mode 100644 index 00000000..ff3dc9b5 --- /dev/null +++ b/src/api/java/baritone/api/process/IGetToBlockProcess.java @@ -0,0 +1,28 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for 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 net.minecraft.block.Block; + +/** + * but it rescans the world every once in a while so it doesn't get fooled by its cache + */ +public interface IGetToBlockProcess extends IBaritoneProcess { + + void getToBlock(Block block); +} diff --git a/src/api/java/baritone/api/behavior/IMineBehavior.java b/src/api/java/baritone/api/process/IMineProcess.java similarity index 81% rename from src/api/java/baritone/api/behavior/IMineBehavior.java rename to src/api/java/baritone/api/process/IMineProcess.java index 78ab6d6a..7ebabc9c 100644 --- a/src/api/java/baritone/api/behavior/IMineBehavior.java +++ b/src/api/java/baritone/api/process/IMineProcess.java @@ -15,7 +15,7 @@ * along with Baritone. If not, see . */ -package baritone.api.behavior; +package baritone.api.process; import net.minecraft.block.Block; @@ -23,7 +23,7 @@ import net.minecraft.block.Block; * @author Brady * @since 9/23/2018 */ -public interface IMineBehavior extends IBehavior { +public interface IMineProcess extends IBaritoneProcess { /** * Begin to search for and mine the specified blocks until @@ -31,9 +31,9 @@ public interface IMineBehavior extends IBehavior { * 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 blocks The blocks to mine */ - void mine(int quantity, String... blocks); + void mineByName(int quantity, String... blocks); /** * Begin to search for and mine the specified blocks until @@ -41,7 +41,7 @@ public interface IMineBehavior extends IBehavior { * 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 blocks The blocks to mine */ void mine(int quantity, Block... blocks); @@ -50,8 +50,8 @@ public interface IMineBehavior extends IBehavior { * * @param blocks The blocks to mine */ - default void mine(String... blocks) { - this.mine(0, blocks); + default void mineByName(String... blocks) { + mineByName(0, blocks); } /** @@ -60,11 +60,13 @@ public interface IMineBehavior extends IBehavior { * @param blocks The blocks to mine */ default void mine(Block... blocks) { - this.mine(0, blocks); + mine(0, blocks); } /** * Cancels the current mining task */ - void cancel(); + default void cancel() { + onLostControl(); + } } diff --git a/src/api/java/baritone/api/process/PathingCommand.java b/src/api/java/baritone/api/process/PathingCommand.java new file mode 100644 index 00000000..2caef158 --- /dev/null +++ b/src/api/java/baritone/api/process/PathingCommand.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.process; + +import baritone.api.pathing.goals.Goal; + +import java.util.Objects; + +/** + * @author leijurv + */ +public class PathingCommand { + + /** + * The target goal, may be {@code null}. + */ + public final Goal goal; + + /** + * The command type. + * + * @see PathingCommandType + */ + public final PathingCommandType commandType; + + /** + * Create a new {@link PathingCommand}. + * + * @param goal The target goal, may be {@code null}. + * @param commandType The command type, cannot be {@code null}. + * @throws NullPointerException if {@code commandType} is {@code null}. + * @see Goal + * @see PathingCommandType + */ + public PathingCommand(Goal goal, PathingCommandType commandType) { + Objects.requireNonNull(commandType); + + this.goal = goal; + this.commandType = commandType; + } +} diff --git a/src/api/java/baritone/api/process/PathingCommandType.java b/src/api/java/baritone/api/process/PathingCommandType.java new file mode 100644 index 00000000..da64748a --- /dev/null +++ b/src/api/java/baritone/api/process/PathingCommandType.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.process; + +import baritone.api.Settings; + +public enum PathingCommandType { + + /** + * Set the goal and path. + *

+ * If you use this alongside a {@code null} goal, it will continue along its current path and current goal. + */ + SET_GOAL_AND_PATH, + + /** + * Has no effect on the current goal or path, just requests a pause + */ + REQUEST_PAUSE, + + /** + * Set the goal (regardless of {@code null}), and request a cancel of the current path (when safe) + */ + CANCEL_AND_SET_GOAL, + + /** + * Set the goal and path. + *

+ * If {@link Settings#cancelOnGoalInvalidation} is {@code true}, revalidate the + * current goal, and cancel if it's no longer valid, or if the new goal is {@code null}. + */ + REVALIDATE_GOAL_AND_PATH, + + /** + * Set the goal and path. + *

+ * Cancel the current path if the goals are not equal + */ + FORCE_REVALIDATE_GOAL_AND_PATH +} diff --git a/src/main/java/baritone/utils/pathing/BetterBlockPos.java b/src/api/java/baritone/api/utils/BetterBlockPos.java similarity index 85% rename from src/main/java/baritone/utils/pathing/BetterBlockPos.java rename to src/api/java/baritone/api/utils/BetterBlockPos.java index 5e8682c9..3aa6a71d 100644 --- a/src/main/java/baritone/utils/pathing/BetterBlockPos.java +++ b/src/api/java/baritone/api/utils/BetterBlockPos.java @@ -15,9 +15,8 @@ * along with Baritone. If not, see . */ -package baritone.utils.pathing; +package baritone.api.utils; -import baritone.pathing.calc.AbstractNodeCostSearch; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -36,7 +35,6 @@ public final class BetterBlockPos extends BlockPos { public final int x; public final int y; public final int z; - public final long hashCode; public static long numCreated; static { @@ -52,7 +50,6 @@ public final class BetterBlockPos extends BlockPos { this.x = x; this.y = y; this.z = z; - this.hashCode = AbstractNodeCostSearch.posHash(x, y, z); } public BetterBlockPos(double x, double y, double z) { @@ -65,7 +62,30 @@ public final class BetterBlockPos extends BlockPos { @Override public int hashCode() { - return (int) hashCode; + return (int) longHash(x, y, z); + } + + public static long longHash(BetterBlockPos pos) { + return longHash(pos.x, pos.y, pos.z); + } + + public static long longHash(int x, int y, int z) { + /* + * This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name) + * + * public int hashCode() { + * return (this.getY() + this.getZ() * 31) * 31 + this.getX(); + * } + * + * That is terrible and has tons of collisions and makes the HashMap terribly inefficient. + * + * That's why we grab out the X, Y, Z and calculate our own hashcode + */ + long hash = 3241; + hash = 3457689L * hash + x; + hash = 8734625L * hash + y; + hash = 2873465L * hash + z; + return hash; } @Override @@ -75,9 +95,6 @@ public final class BetterBlockPos extends BlockPos { } if (o instanceof BetterBlockPos) { BetterBlockPos oth = (BetterBlockPos) o; - if (oth.hashCode != hashCode) { - return false; - } return oth.x == x && oth.y == y && oth.z == z; } // during path execution, like "if (whereShouldIBe.equals(whereAmI)) {" diff --git a/src/api/java/baritone/api/utils/PathCalculationResult.java b/src/api/java/baritone/api/utils/PathCalculationResult.java new file mode 100644 index 00000000..69d2a9b3 --- /dev/null +++ b/src/api/java/baritone/api/utils/PathCalculationResult.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; + +import baritone.api.pathing.calc.IPath; + +import java.util.Optional; + +public class PathCalculationResult { + + public final Optional path; + public final Type type; + + public PathCalculationResult(Type type, Optional path) { + this.path = path; + this.type = type; + } + + public enum Type { + SUCCESS_TO_GOAL, + SUCCESS_SEGMENT, + FAILURE, + CANCELLATION, + EXCEPTION, + } +} diff --git a/src/api/java/baritone/api/utils/RayTraceUtils.java b/src/api/java/baritone/api/utils/RayTraceUtils.java new file mode 100644 index 00000000..8b37ef9d --- /dev/null +++ b/src/api/java/baritone/api/utils/RayTraceUtils.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.api.utils; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; + +import java.util.Optional; + +/** + * @author Brady + * @since 8/25/2018 + */ +public final class RayTraceUtils { + + private static final Minecraft mc = Minecraft.getMinecraft(); + + private RayTraceUtils() {} + + /** + * Performs a block raytrace with the specified rotations. This should only be used when + * any entity collisions can be ignored, because this method will not recognize if an + * entity is in the way or not. The local player's block reach distance will be used. + * + * @param rotation The rotation to raytrace towards + * @return The calculated raytrace result + */ + public static RayTraceResult rayTraceTowards(Entity entity, Rotation rotation, double blockReachDistance) { + Vec3d start = entity.getPositionEyes(1.0F); + Vec3d direction = RotationUtils.calcVec3dFromRotation(rotation); + Vec3d end = start.add( + direction.x * blockReachDistance, + direction.y * blockReachDistance, + direction.z * blockReachDistance + ); + return mc.world.rayTraceBlocks(start, end, false, false, true); + } + + /** + * Returns the block that the crosshair is currently placed over. Updated once per render tick. + * + * @return The position of the highlighted block + */ + public static Optional getSelectedBlock() { + if (mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) { + return Optional.of(mc.objectMouseOver.getBlockPos()); + } + return Optional.empty(); + } + + /** + * Returns the entity that the crosshair is currently placed over. Updated once per render tick. + * + * @return The entity + */ + public static Optional getSelectedEntity() { + if (mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.ENTITY) { + return Optional.of(mc.objectMouseOver.entityHit); + } + return Optional.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/Rotation.java b/src/api/java/baritone/api/utils/Rotation.java index dc697169..ea10c7ec 100644 --- a/src/api/java/baritone/api/utils/Rotation.java +++ b/src/api/java/baritone/api/utils/Rotation.java @@ -86,7 +86,7 @@ public class Rotation { public Rotation clamp() { return new Rotation( this.yaw, - RotationUtils.clampPitch(this.pitch) + clampPitch(this.pitch) ); } @@ -95,7 +95,7 @@ public class Rotation { */ public Rotation normalize() { return new Rotation( - RotationUtils.normalizeYaw(this.yaw), + normalizeYaw(this.yaw), this.pitch ); } @@ -105,8 +105,35 @@ public class Rotation { */ public Rotation normalizeAndClamp() { return new Rotation( - RotationUtils.normalizeYaw(this.yaw), - RotationUtils.clampPitch(this.pitch) + normalizeYaw(this.yaw), + clampPitch(this.pitch) ); } + + /** + * Clamps the specified pitch value between -90 and 90. + * + * @param pitch The input pitch + * @return The clamped pitch + */ + public static float clampPitch(float pitch) { + return Math.max(-90, Math.min(90, pitch)); + } + + /** + * Normalizes the specified yaw value between -180 and 180. + * + * @param yaw The input yaw + * @return The normalized yaw + */ + public static float normalizeYaw(float yaw) { + float newYaw = yaw % 360F; + if (newYaw < -180F) { + newYaw += 360F; + } + if (newYaw >= 180F) { + newYaw -= 360F; + } + return newYaw; + } } diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 0df4b38d..e1726812 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -17,38 +17,192 @@ package baritone.api.utils; +import net.minecraft.block.BlockFire; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.*; + +import java.util.Optional; + /** * @author Brady * @since 9/25/2018 */ public final class RotationUtils { + /** + * Constant that a degree value is multiplied by to get the equivalent radian value + */ + public static final double DEG_TO_RAD = Math.PI / 180.0; + + /** + * Constant that a radian value is multiplied by to get the equivalent degree value + */ + public static final double RAD_TO_DEG = 180.0 / Math.PI; + + /** + * Offsets from the root block position to the center of each side. + */ + private static final Vec3d[] BLOCK_SIDE_MULTIPLIERS = new Vec3d[]{ + new Vec3d(0.5, 0, 0.5), // Down + new Vec3d(0.5, 1, 0.5), // Up + new Vec3d(0.5, 0.5, 0), // North + new Vec3d(0.5, 0.5, 1), // South + new Vec3d(0, 0.5, 0.5), // West + new Vec3d(1, 0.5, 0.5) // East + }; + private RotationUtils() {} /** - * Clamps the specified pitch value between -90 and 90. + * Calculates the rotation from BlockPosdest to BlockPosorig * - * @param pitch The input pitch - * @return The clamped pitch + * @param orig The origin position + * @param dest The destination position + * @return The rotation from the origin to the destination */ - public static float clampPitch(float pitch) { - return Math.max(-90, Math.min(90, pitch)); + public static Rotation calcRotationFromCoords(BlockPos orig, BlockPos dest) { + return calcRotationFromVec3d(new Vec3d(orig), new Vec3d(dest)); } /** - * Normalizes the specified yaw value between -180 and 180. + * Wraps the target angles to a relative value from the current angles. This is done by + * subtracting the current from the target, normalizing it, and then adding the current + * angles back to it. * - * @param yaw The input yaw - * @return The normalized yaw + * @param current The current angles + * @param target The target angles + * @return The wrapped angles */ - public static float normalizeYaw(float yaw) { - float newYaw = yaw % 360F; - if (newYaw < -180F) { - newYaw += 360F; + public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { + return target.subtract(current).normalize().add(current); + } + + /** + * Calculates the rotation from Vecdest to Vecorig and makes the + * return value relative to the specified current rotations. + * + * @param orig The origin position + * @param dest The destination position + * @param current The current rotations + * @return The rotation from the origin to the destination + * @see #wrapAnglesToRelative(Rotation, Rotation) + */ + public static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest, Rotation current) { + return wrapAnglesToRelative(current, calcRotationFromVec3d(orig, dest)); + } + + /** + * Calculates the rotation from Vecdest to Vecorig + * + * @param orig The origin position + * @param dest The destination position + * @return The rotation from the origin to the destination + */ + public 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]); + double pitch = MathHelper.atan2(delta[1], dist); + return new Rotation( + (float) (yaw * RAD_TO_DEG), + (float) (pitch * RAD_TO_DEG) + ); + } + + /** + * Calculates the look vector for the specified yaw/pitch rotations. + * + * @param rotation The input rotation + * @return Look vector for the rotation + */ + public static Vec3d calcVec3dFromRotation(Rotation rotation) { + float f = MathHelper.cos(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); + float f1 = MathHelper.sin(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); + float f2 = -MathHelper.cos(-rotation.getPitch() * (float) DEG_TO_RAD); + float f3 = MathHelper.sin(-rotation.getPitch() * (float) DEG_TO_RAD); + return new Vec3d((double) (f1 * f2), (double) f3, (double) (f * f2)); + } + + /** + * Determines if the specified entity is able to reach the center of any of the sides + * of the specified block. It first checks if the block center is reachable, and if so, + * that rotation will be returned. If not, it will return the first center of a given + * side that is reachable. The return type will be {@link Optional#empty()} if the entity is + * unable to reach any of the sides of the block. + * + * @param entity The viewing entity + * @param pos The target block position + * @return The optional rotation + */ + public static Optional reachable(Entity entity, BlockPos pos, double blockReachDistance) { + if (pos.equals(RayTraceUtils.getSelectedBlock().orElse(null))) { + /* + * why add 0.0001? + * to indicate that we actually have a desired pitch + * the way we indicate that the pitch can be whatever and we only care about the yaw + * is by setting the desired pitch to the current pitch + * setting the desired pitch to the current pitch + 0.0001 means that we do have a desired pitch, it's + * just what it currently is + * + * or if you're a normal person literally all this does it ensure that we don't nudge the pitch to a normal level + */ + return Optional.of(new Rotation(entity.rotationYaw, entity.rotationPitch + 0.0001F)); } - if (newYaw >= 180F) { - newYaw -= 360F; + Optional possibleRotation = reachableCenter(entity, pos, blockReachDistance); + //System.out.println("center: " + possibleRotation); + if (possibleRotation.isPresent()) { + return possibleRotation; } - return newYaw; + + IBlockState state = entity.world.getBlockState(pos); + AxisAlignedBB aabb = state.getBoundingBox(entity.world, pos); + for (Vec3d sideOffset : BLOCK_SIDE_MULTIPLIERS) { + double xDiff = aabb.minX * sideOffset.x + aabb.maxX * (1 - sideOffset.x); + double yDiff = aabb.minY * sideOffset.y + aabb.maxY * (1 - sideOffset.y); + double zDiff = aabb.minZ * sideOffset.z + aabb.maxZ * (1 - sideOffset.z); + possibleRotation = reachableOffset(entity, pos, new Vec3d(pos).add(xDiff, yDiff, zDiff), blockReachDistance); + if (possibleRotation.isPresent()) { + return possibleRotation; + } + } + return Optional.empty(); + } + + /** + * Determines if the specified entity is able to reach the specified block with + * the given offsetted position. The return type will be {@link Optional#empty()} if + * the entity is unable to reach the block with the offset applied. + * + * @param entity The viewing entity + * @param pos The target block position + * @param offsetPos The position of the block with the offset applied. + * @return The optional rotation + */ + public static Optional reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance) { + Rotation rotation = calcRotationFromVec3d(entity.getPositionEyes(1.0F), offsetPos); + RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance); + //System.out.println(result); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { + if (result.getBlockPos().equals(pos)) { + return Optional.of(rotation); + } + if (entity.world.getBlockState(pos).getBlock() instanceof BlockFire && result.getBlockPos().equals(pos.down())) { + return Optional.of(rotation); + } + } + return Optional.empty(); + } + + /** + * Determines if the specified entity is able to reach the specified block where it is + * looking at the direct center of it's hitbox. + * + * @param entity The viewing entity + * @param pos The target block position + * @return The optional rotation + */ + public static Optional reachableCenter(Entity entity, BlockPos pos, double blockReachDistance) { + return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(pos), blockReachDistance); } } diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java new file mode 100644 index 00000000..4d13d027 --- /dev/null +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -0,0 +1,141 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for 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.Settings; +import net.minecraft.client.Minecraft; +import net.minecraft.item.Item; +import net.minecraft.util.ResourceLocation; + +import java.awt.*; +import java.io.File; +import java.io.FileOutputStream; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SettingsUtil { + + private static final File settingsFile = new File(new File(Minecraft.getMinecraft().gameDir, "baritone"), "settings.txt"); + + private static final Map, SettingsIO> map; + + public static void readAndApply(Settings settings) { + try (Scanner scan = new Scanner(settingsFile)) { + while (scan.hasNextLine()) { + String line = scan.nextLine(); + if (line.isEmpty()) { + continue; + } + if (line.startsWith("#") || line.startsWith("//")) { + continue; + } + int space = line.indexOf(" "); + if (space == -1) { + System.out.println("Skipping invalid line with no space: " + line); + continue; + } + String settingName = line.substring(0, space).trim().toLowerCase(); + String settingValue = line.substring(space).trim(); + try { + parseAndApply(settings, settingName, settingValue); + } catch (Exception ex) { + ex.printStackTrace(); + System.out.println("Unable to parse line " + line); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + System.out.println("Exception while reading Baritone settings, some settings may be reset to default values!"); + } + } + + public static synchronized void save(Settings settings) { + try (FileOutputStream out = new FileOutputStream(settingsFile)) { + 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").getBytes()); + } + } catch (Exception ex) { + ex.printStackTrace(); + System.out.println("Exception while saving Baritone settings!"); + } + } + + private 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); + 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 { + 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), + + 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()); + + + Class klass; + Function parser; + Function toString; + + SettingsIO(Class klass, Function parser) { + this(klass, parser, Object::toString); + } + + SettingsIO(Class klass, Function parser, Function toString) { + this.klass = klass; + 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); + } + map = Collections.unmodifiableMap(tempMap); + } +} diff --git a/src/api/java/baritone/api/utils/VecUtils.java b/src/api/java/baritone/api/utils/VecUtils.java new file mode 100644 index 00000000..831e0937 --- /dev/null +++ b/src/api/java/baritone/api/utils/VecUtils.java @@ -0,0 +1,120 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for 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.BlockFire; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +/** + * @author Brady + * @since 10/13/2018 + */ +public final class VecUtils { + /** + * The {@link Minecraft} instance + */ + private static final Minecraft mc = Minecraft.getMinecraft(); + + private VecUtils() {} + + /** + * Calculates the center of the block at the specified position's bounding box + * + * @param pos The block position + * @return The center of the block's bounding box + * @see #getBlockPosCenter(BlockPos) + */ + public static Vec3d calculateBlockCenter(BlockPos pos) { + IBlockState b = mc.world.getBlockState(pos); + AxisAlignedBB bbox = b.getBoundingBox(mc.world, pos); + double xDiff = (bbox.minX + bbox.maxX) / 2; + double yDiff = (bbox.minY + bbox.maxY) / 2; + double zDiff = (bbox.minZ + bbox.maxZ) / 2; + if (b.getBlock() instanceof BlockFire) {//look at bottom of fire when putting it out + yDiff = 0; + } + return new Vec3d( + pos.getX() + xDiff, + pos.getY() + yDiff, + pos.getZ() + zDiff + ); + } + + /** + * Gets the assumed center position of the given block position. + * This is done by adding 0.5 to the X, Y, and Z axes. + *

+ * TODO: We may want to consider replacing many usages of this method with #calculateBlockCenter(BlockPos) + * + * @param pos The block position + * @return The assumed center of the position + * @see #calculateBlockCenter(BlockPos) + */ + public static Vec3d getBlockPosCenter(BlockPos pos) { + return new Vec3d(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + } + + /** + * Gets the distance from the specified position to the assumed center of the specified block position. + * + * @param pos The block position + * @param x The x pos + * @param y The y pos + * @param z The z pos + * @return The distance from the assumed block center to the position + * @see #getBlockPosCenter(BlockPos) + */ + public static double distanceToCenter(BlockPos pos, double x, double y, double z) { + Vec3d center = getBlockPosCenter(pos); + double xdiff = x - center.x; + double ydiff = y - center.y; + double zdiff = z - center.z; + return Math.sqrt(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff); + } + + /** + * Gets the distance from the specified entity's position to the assumed + * center of the specified block position. + * + * @param entity The entity + * @param pos The block position + * @return The distance from the entity to the block's assumed center + * @see #getBlockPosCenter(BlockPos) + */ + public static double entityDistanceToCenter(Entity entity, BlockPos pos) { + return distanceToCenter(pos, entity.posX, entity.posY, entity.posZ); + } + + /** + * Gets the distance from the specified entity's position to the assumed + * center of the specified block position, ignoring the Y axis. + * + * @param entity The entity + * @param pos The block position + * @return The horizontal distance from the entity to the block's assumed center + * @see #getBlockPosCenter(BlockPos) + */ + public static double entityFlatDistanceToCenter(Entity entity, BlockPos pos) { + return distanceToCenter(pos, entity.posX, pos.getY() + 0.5, entity.posZ); + } +} diff --git a/src/launch/java/baritone/launch/BaritoneTweaker.java b/src/launch/java/baritone/launch/BaritoneTweaker.java index 536ecf95..e78da09f 100644 --- a/src/launch/java/baritone/launch/BaritoneTweaker.java +++ b/src/launch/java/baritone/launch/BaritoneTweaker.java @@ -17,61 +17,39 @@ package baritone.launch; -import net.minecraft.launchwrapper.ITweaker; +import io.github.impactdevelopment.simpletweaker.SimpleTweaker; +import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; import org.spongepowered.asm.launch.MixinBootstrap; import org.spongepowered.asm.mixin.MixinEnvironment; import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.tools.obfuscation.mcp.ObfuscationServiceMCP; -import java.io.File; -import java.util.ArrayList; import java.util.List; /** * @author Brady * @since 7/31/2018 9:59 PM */ -public class BaritoneTweaker implements ITweaker { - - List args; - - @Override - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - this.args = new ArrayList<>(args); - if (gameDir != null) { - addArg("gameDir", gameDir.getAbsolutePath()); - } - if (assetsDir != null) { - addArg("assetsDir", assetsDir.getAbsolutePath()); - } - if (profile != null) { - addArg("version", profile); - } - } +public class BaritoneTweaker extends SimpleTweaker { @Override public void injectIntoClassLoader(LaunchClassLoader classLoader) { + super.injectIntoClassLoader(classLoader); + MixinBootstrap.init(); + + // noinspection unchecked + List tweakClasses = (List) Launch.blackboard.get("TweakClasses"); + + String obfuscation = ObfuscationServiceMCP.NOTCH; + if (tweakClasses.stream().anyMatch(s -> s.contains("net.minecraftforge.fml.common.launcher"))) { + obfuscation = ObfuscationServiceMCP.SEARGE; + } + MixinEnvironment.getDefaultEnvironment().setSide(MixinEnvironment.Side.CLIENT); - MixinEnvironment.getDefaultEnvironment().setObfuscationContext(ObfuscationServiceMCP.NOTCH); + MixinEnvironment.getDefaultEnvironment().setObfuscationContext(obfuscation); + Mixins.addConfiguration("mixins.baritone.json"); } - - @Override - public final String getLaunchTarget() { - return "net.minecraft.client.main.Main"; - } - - @Override - public final String[] getLaunchArguments() { - return this.args.toArray(new String[0]); - } - - private void addArg(String label, String value) { - if (!args.contains("--" + label) && value != null) { - this.args.add("--" + label); - this.args.add(value); - } - } } diff --git a/src/launch/java/baritone/launch/mixins/MixinAnvilChunkLoader.java b/src/launch/java/baritone/launch/mixins/MixinAnvilChunkLoader.java index 4ffb5abc..8b3ea0af 100644 --- a/src/launch/java/baritone/launch/mixins/MixinAnvilChunkLoader.java +++ b/src/launch/java/baritone/launch/mixins/MixinAnvilChunkLoader.java @@ -32,7 +32,9 @@ import java.io.File; @Mixin(AnvilChunkLoader.class) public class MixinAnvilChunkLoader implements IAnvilChunkLoader { - @Shadow @Final private File chunkSaveLocation; + @Shadow + @Final + private File chunkSaveLocation; @Override public File getChunkSaveLocation() { diff --git a/src/launch/java/baritone/launch/mixins/MixinBlockPos.java b/src/launch/java/baritone/launch/mixins/MixinBlockPos.java index 013980a9..b0aa75ba 100644 --- a/src/launch/java/baritone/launch/mixins/MixinBlockPos.java +++ b/src/launch/java/baritone/launch/mixins/MixinBlockPos.java @@ -35,6 +35,12 @@ public class MixinBlockPos extends Vec3i { 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. + */ @Override @Nonnull public String toString() { diff --git a/src/launch/java/baritone/launch/mixins/MixinChunkProviderServer.java b/src/launch/java/baritone/launch/mixins/MixinChunkProviderServer.java index 246c368e..6d5a5421 100644 --- a/src/launch/java/baritone/launch/mixins/MixinChunkProviderServer.java +++ b/src/launch/java/baritone/launch/mixins/MixinChunkProviderServer.java @@ -31,7 +31,9 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(ChunkProviderServer.class) public class MixinChunkProviderServer implements IChunkProviderServer { - @Shadow @Final private IChunkLoader chunkLoader; + @Shadow + @Final + private IChunkLoader chunkLoader; @Override public IChunkLoader getChunkLoader() { diff --git a/src/launch/java/baritone/launch/mixins/MixinEntity.java b/src/launch/java/baritone/launch/mixins/MixinEntity.java index b96b5b41..8ca1743f 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntity.java @@ -19,14 +19,17 @@ package baritone.launch.mixins; import baritone.Baritone; import baritone.api.event.events.RotationMoveEvent; -import baritone.api.event.events.type.EventState; -import net.minecraft.client.Minecraft; +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 @@ -34,23 +37,38 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @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) { - Entity _this = (Entity) (Object) this; - if (_this == Minecraft.getMinecraft().player) - Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(new RotationMoveEvent(EventState.PRE, RotationMoveEvent.Type.MOTION_UPDATE)); + // noinspection ConstantConditions + if (EntityPlayerSP.class.isInstance(this)) { + this.motionUpdateRotationEvent = new RotationMoveEvent((EntityPlayerSP) (Object) this, RotationMoveEvent.Type.MOTION_UPDATE, this.rotationYaw); + Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(this.motionUpdateRotationEvent); + } } - @Inject( + @Redirect( method = "moveRelative", - at = @At("RETURN") + at = @At( + value = "FIELD", + opcode = GETFIELD, + target = "net/minecraft/entity/Entity.rotationYaw:F" + ) ) - private void postMoveRelative(float strafe, float up, float forward, float friction, CallbackInfo ci) { - Entity _this = (Entity) (Object) this; - if (_this == Minecraft.getMinecraft().player) - Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(new RotationMoveEvent(EventState.POST, RotationMoveEvent.Type.MOTION_UPDATE)); + 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 e840576e..8e2eb515 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntityLivingBase.java @@ -19,39 +19,59 @@ package baritone.launch.mixins; import baritone.Baritone; import baritone.api.event.events.RotationMoveEvent; -import baritone.api.event.events.type.EventState; -import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static org.spongepowered.asm.lib.Opcodes.GETFIELD; + /** * @author Brady * @since 9/10/2018 */ @Mixin(EntityLivingBase.class) -public class MixinEntityLivingBase { +public abstract class MixinEntityLivingBase extends Entity { + + /** + * Event called to override the movement direction when jumping + */ + private RotationMoveEvent jumpRotationEvent; + + public MixinEntityLivingBase(World worldIn, RotationMoveEvent jumpRotationEvent) { + super(worldIn); + this.jumpRotationEvent = jumpRotationEvent; + } @Inject( method = "jump", at = @At("HEAD") ) - private void preJump(CallbackInfo ci) { - Entity _this = (Entity) (Object) this; - if (_this == Minecraft.getMinecraft().player) - Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(new RotationMoveEvent(EventState.PRE, RotationMoveEvent.Type.JUMP)); + private void preMoveRelative(CallbackInfo ci) { + // noinspection ConstantConditions + if (EntityPlayerSP.class.isInstance(this)) { + this.jumpRotationEvent = new RotationMoveEvent((EntityPlayerSP) (Object) this, RotationMoveEvent.Type.JUMP, this.rotationYaw); + Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); + } } - @Inject( + @Redirect( method = "jump", - at = @At("RETURN") + at = @At( + value = "FIELD", + opcode = GETFIELD, + target = "net/minecraft/entity/EntityLivingBase.rotationYaw:F" + ) ) - private void postJump(CallbackInfo ci) { - Entity _this = (Entity) (Object) this; - if (_this == Minecraft.getMinecraft().player) - Baritone.INSTANCE.getGameEventHandler().onPlayerRotationMove(new RotationMoveEvent(EventState.POST, RotationMoveEvent.Type.JUMP)); + private float overrideYaw(EntityLivingBase self) { + if (self instanceof EntityPlayerSP) { + return this.jumpRotationEvent.getYaw(); + } + return self.rotationYaw; } } diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java index e9a575c5..a180df5c 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java @@ -21,10 +21,13 @@ import baritone.Baritone; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.type.EventState; +import baritone.behavior.PathingBehavior; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.entity.player.PlayerCapabilities; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; /** @@ -40,7 +43,7 @@ public class MixinEntityPlayerSP { cancellable = true ) private void sendChatMessage(String msg, CallbackInfo ci) { - ChatEvent event = new ChatEvent(msg); + ChatEvent event = new ChatEvent((EntityPlayerSP) (Object) this, msg); Baritone.INSTANCE.getGameEventHandler().onSendChatMessage(event); if (event.isCancelled()) { ci.cancel(); @@ -57,7 +60,7 @@ public class MixinEntityPlayerSP { ) ) private void onPreUpdate(CallbackInfo ci) { - Baritone.INSTANCE.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.PRE)); + Baritone.INSTANCE.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent((EntityPlayerSP) (Object) this, EventState.PRE)); } @Inject( @@ -70,6 +73,18 @@ public class MixinEntityPlayerSP { ) ) private void onPostUpdate(CallbackInfo ci) { - Baritone.INSTANCE.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST)); + Baritone.INSTANCE.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent((EntityPlayerSP) (Object) this, EventState.POST)); + } + + @Redirect( + method = "onLivingUpdate", + at = @At( + value = "FIELD", + target = "net/minecraft/entity/player/PlayerCapabilities.allowFlying:Z" + ) + ) + private boolean isAllowFlying(PlayerCapabilities capabilities) { + PathingBehavior pathingBehavior = Baritone.INSTANCE.getPathingBehavior(); + return !pathingBehavior.isPathing() && capabilities.allowFlying; } } diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityRenderer.java b/src/launch/java/baritone/launch/mixins/MixinEntityRenderer.java index 0fc1b246..174b9e3c 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntityRenderer.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntityRenderer.java @@ -33,7 +33,7 @@ public class MixinEntityRenderer { at = @At( value = "INVOKE_STRING", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V", - args = { "ldc=hand" } + args = {"ldc=hand"} ) ) private void renderWorldPass(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) { diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java index 2f0d1ab4..3826f0d7 100644 --- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java +++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java @@ -22,9 +22,7 @@ import baritone.api.event.events.BlockInteractEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.WorldEvent; import baritone.api.event.events.type.EventState; -import baritone.behavior.PathingBehavior; import baritone.utils.BaritoneAutoTest; -import baritone.utils.ExampleBaritoneControl; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.GuiScreen; @@ -40,7 +38,6 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; /** @@ -50,8 +47,6 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(Minecraft.class) public class MixinMinecraft { - @Shadow - private int leftClickCounter; @Shadow public EntityPlayerSP player; @Shadow @@ -63,7 +58,6 @@ public class MixinMinecraft { ) private void postInit(CallbackInfo ci) { Baritone.INSTANCE.init(); - ExampleBaritoneControl.INSTANCE.initAndRegister(); } @Inject( @@ -89,10 +83,9 @@ public class MixinMinecraft { ) ) private void runTick(CallbackInfo ci) { - Minecraft mc = (Minecraft) (Object) this; Baritone.INSTANCE.getGameEventHandler().onTick(new TickEvent( EventState.PRE, - (mc.player != null && mc.world != null) + (player != null && world != null) ? TickEvent.Type.IN : TickEvent.Type.OUT )); @@ -148,7 +141,7 @@ public class MixinMinecraft { ) ) private boolean isAllowUserInput(GuiScreen screen) { - return (PathingBehavior.INSTANCE.getCurrent() != null && player != null) || screen.allowUserInput; + return (Baritone.INSTANCE.getPathingBehavior().getCurrent() != null && player != null) || screen.allowUserInput; } @Inject( diff --git a/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java b/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java index f79e007f..bf15bfc3 100644 --- a/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java +++ b/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java @@ -54,7 +54,7 @@ public class MixinNetworkManager { ) private void preDispatchPacket(Packet inPacket, final GenericFutureListener>[] futureListeners, CallbackInfo ci) { if (this.direction == EnumPacketDirection.CLIENTBOUND) { - Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent(EventState.PRE, inPacket)); + Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent((NetworkManager) (Object) this, EventState.PRE, inPacket)); } } @@ -64,7 +64,7 @@ public class MixinNetworkManager { ) private void postDispatchPacket(Packet inPacket, final GenericFutureListener>[] futureListeners, CallbackInfo ci) { if (this.direction == EnumPacketDirection.CLIENTBOUND) { - Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent(EventState.POST, inPacket)); + Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent((NetworkManager) (Object) this, EventState.POST, inPacket)); } } @@ -77,7 +77,8 @@ public class MixinNetworkManager { ) private void preProcessPacket(ChannelHandlerContext context, Packet packet, CallbackInfo ci) { if (this.direction == EnumPacketDirection.CLIENTBOUND) { - Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent(EventState.PRE, packet));} + Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent((NetworkManager) (Object) this, EventState.PRE, packet)); + } } @Inject( @@ -86,7 +87,7 @@ public class MixinNetworkManager { ) private void postProcessPacket(ChannelHandlerContext context, Packet packet, CallbackInfo ci) { if (this.channel.isOpen() && this.direction == EnumPacketDirection.CLIENTBOUND) { - Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent(EventState.POST, packet)); + Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent((NetworkManager) (Object) this, EventState.POST, packet)); } } } diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index 4a764bcc..f4546012 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -18,13 +18,24 @@ package baritone; import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.event.listener.IGameEventListener; -import baritone.behavior.*; +import baritone.behavior.Behavior; +import baritone.behavior.LookBehavior; +import baritone.behavior.MemoryBehavior; +import baritone.behavior.PathingBehavior; import baritone.cache.WorldProvider; +import baritone.cache.WorldScanner; 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 net.minecraft.client.Minecraft; import java.io.File; @@ -36,97 +47,92 @@ import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; /** * @author Brady * @since 7/31/2018 10:50 PM */ -public enum Baritone { +public enum Baritone implements IBaritone { /** * Singleton instance of this class */ INSTANCE; + private static ThreadPoolExecutor threadPool; + private static File dir; + + static { + threadPool = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); + + dir = new File(Minecraft.getMinecraft().gameDir, "baritone"); + if (!Files.exists(dir.toPath())) { + try { + Files.createDirectories(dir.toPath()); + } catch (IOException ignored) {} + } + } + /** * Whether or not {@link Baritone#init()} has been called yet */ private boolean initialized; private GameEventHandler gameEventHandler; - private InputOverrideHandler inputOverrideHandler; - private Settings settings; + private List behaviors; - private File dir; - private ThreadPoolExecutor threadPool; + private PathingBehavior pathingBehavior; + private LookBehavior lookBehavior; + private MemoryBehavior memoryBehavior; + private InputOverrideHandler inputOverrideHandler; + private FollowProcess followProcess; + private MineProcess mineProcess; + private GetToBlockProcess getToBlockProcess; + private CustomGoalProcess customGoalProcess; - /** - * List of consumers to be called after Baritone has initialized - */ - private List> onInitConsumers; + private PathingControlManager pathingControlManager; - /** - * Whether or not Baritone is active - */ - private boolean active; + private WorldProvider worldProvider; Baritone() { - this.onInitConsumers = new ArrayList<>(); + this.gameEventHandler = new GameEventHandler(this); } public synchronized void init() { if (initialized) { return; } - this.threadPool = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); - this.gameEventHandler = new GameEventHandler(); - this.inputOverrideHandler = new InputOverrideHandler(); - - // Acquire the "singleton" instance of the settings directly from the API - // We might want to change this... - this.settings = BaritoneAPI.getSettings(); - - BaritoneAPI.registerProviders(WorldProvider.INSTANCE); this.behaviors = new ArrayList<>(); { - registerBehavior(PathingBehavior.INSTANCE); - registerBehavior(LookBehavior.INSTANCE); - registerBehavior(MemoryBehavior.INSTANCE); - registerBehavior(LocationTrackingBehavior.INSTANCE); - registerBehavior(FollowBehavior.INSTANCE); - registerBehavior(MineBehavior.INSTANCE); - - // TODO: Clean this up - // Maybe combine this call in someway with the registerBehavior calls? - BaritoneAPI.registerDefaultBehaviors( - FollowBehavior.INSTANCE, - LookBehavior.INSTANCE, - MemoryBehavior.INSTANCE, - MineBehavior.INSTANCE, - PathingBehavior.INSTANCE - ); + // the Behavior constructor calls baritone.registerBehavior(this) so this populates the behaviors arraylist + pathingBehavior = new PathingBehavior(this); + lookBehavior = new LookBehavior(this); + memoryBehavior = new MemoryBehavior(this); + inputOverrideHandler = new InputOverrideHandler(this); + new ExampleBaritoneControl(this); } + + this.pathingControlManager = new PathingControlManager(this); + { + followProcess = new FollowProcess(this); + mineProcess = new MineProcess(this); + customGoalProcess = new CustomGoalProcess(this); // very high iq + getToBlockProcess = new GetToBlockProcess(this); + } + + this.worldProvider = new WorldProvider(); + if (BaritoneAutoTest.ENABLE_AUTO_TEST) { registerEventListener(BaritoneAutoTest.INSTANCE); } - this.dir = new File(Minecraft.getMinecraft().gameDir, "baritone"); - if (!Files.exists(dir.toPath())) { - try { - Files.createDirectories(dir.toPath()); - } catch (IOException ignored) {} - } - this.active = true; this.initialized = true; - - this.onInitConsumers.forEach(consumer -> consumer.accept(this)); } - public boolean isInitialized() { - return this.initialized; + public PathingControlManager getPathingControlManager() { + return pathingControlManager; } public IGameEventListener getGameEventHandler() { @@ -141,36 +147,70 @@ public enum Baritone { return this.behaviors; } - public Executor getExecutor() { - return threadPool; - } - public void registerBehavior(Behavior behavior) { this.behaviors.add(behavior); this.registerEventListener(behavior); } + @Override + public CustomGoalProcess getCustomGoalProcess() { // Iffy + return customGoalProcess; + } + + @Override + public GetToBlockProcess getGetToBlockProcess() { // Iffy + return getToBlockProcess; + } + + @Override + public FollowProcess getFollowProcess() { + return followProcess; + } + + @Override + public LookBehavior getLookBehavior() { + return lookBehavior; + } + + @Override + public MemoryBehavior getMemoryBehavior() { + return memoryBehavior; + } + + @Override + public MineProcess getMineProcess() { + return mineProcess; + } + + @Override + public PathingBehavior getPathingBehavior() { + return pathingBehavior; + } + + @Override + public WorldProvider getWorldProvider() { + return worldProvider; + } + + @Override + public WorldScanner getWorldScanner() { + return WorldScanner.INSTANCE; + } + + @Override public void registerEventListener(IGameEventListener listener) { this.gameEventHandler.registerEventListener(listener); } - public boolean isActive() { - return this.active; - } - - public Settings getSettings() { - return this.settings; - } - public static Settings settings() { - return Baritone.INSTANCE.settings; // yolo + return BaritoneAPI.getSettings(); } - public File getDir() { - return this.dir; + public static File getDir() { + return dir; } - public void registerInitListener(Consumer runnable) { - this.onInitConsumers.add(runnable); + public static Executor getExecutor() { + return threadPool; } } diff --git a/src/launch/java/baritone/launch/BaritoneTweakerOptifine.java b/src/main/java/baritone/BaritoneProvider.java similarity index 68% rename from src/launch/java/baritone/launch/BaritoneTweakerOptifine.java rename to src/main/java/baritone/BaritoneProvider.java index 9f4f8380..a80dfe5e 100644 --- a/src/launch/java/baritone/launch/BaritoneTweakerOptifine.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -15,20 +15,19 @@ * along with Baritone. If not, see . */ -package baritone.launch; +package baritone; -import java.io.File; -import java.util.ArrayList; -import java.util.List; +import baritone.api.IBaritone; +import baritone.api.IBaritoneProvider; +import net.minecraft.client.entity.EntityPlayerSP; /** * @author Brady - * @since 7/31/2018 10:10 PM + * @since 9/29/2018 */ -public class BaritoneTweakerOptifine extends BaritoneTweaker { - +public final class BaritoneProvider implements IBaritoneProvider { @Override - public final void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - this.args = new ArrayList<>(); + public IBaritone getBaritoneForPlayer(EntityPlayerSP player) { + return Baritone.INSTANCE; // pwnage } } diff --git a/src/main/java/baritone/behavior/Behavior.java b/src/main/java/baritone/behavior/Behavior.java index b856e423..66434d7a 100644 --- a/src/main/java/baritone/behavior/Behavior.java +++ b/src/main/java/baritone/behavior/Behavior.java @@ -17,54 +17,21 @@ package baritone.behavior; +import baritone.Baritone; import baritone.api.behavior.IBehavior; /** - * A type of game event listener that can be toggled. + * A type of game event listener that is given {@link Baritone} instance context. * * @author Brady * @since 8/1/2018 6:29 PM */ public class Behavior implements IBehavior { - /** - * Whether or not this behavior is enabled - */ - private boolean enabled = true; + public final Baritone baritone; - /** - * Toggles the enabled state of this {@link Behavior}. - * - * @return The new state. - */ - @Override - public final boolean toggle() { - return this.setEnabled(!this.isEnabled()); - } - - /** - * Sets the enabled state of this {@link Behavior}. - * - * @return The new state. - */ - @Override - public final boolean setEnabled(boolean enabled) { - if (enabled == this.enabled) { - return this.enabled; - } - if (this.enabled = enabled) { - this.onEnable(); - } else { - this.onDisable(); - } - return this.enabled; - } - - /** - * @return Whether or not this {@link Behavior} is active. - */ - @Override - public final boolean isEnabled() { - return this.enabled; + protected Behavior(Baritone baritone) { + this.baritone = baritone; + baritone.registerBehavior(this); } } diff --git a/src/main/java/baritone/behavior/FollowBehavior.java b/src/main/java/baritone/behavior/FollowBehavior.java deleted file mode 100644 index 83ca5135..00000000 --- a/src/main/java/baritone/behavior/FollowBehavior.java +++ /dev/null @@ -1,79 +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.behavior; - -import baritone.Baritone; -import baritone.api.behavior.IFollowBehavior; -import baritone.api.event.events.TickEvent; -import baritone.api.pathing.goals.GoalNear; -import baritone.api.pathing.goals.GoalXZ; -import baritone.utils.Helper; -import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; - -/** - * Follow an entity - * - * @author leijurv - */ -public final class FollowBehavior extends Behavior implements IFollowBehavior, Helper { - - public static final FollowBehavior INSTANCE = new FollowBehavior(); - - private Entity following; - - private FollowBehavior() {} - - @Override - public void onTick(TickEvent event) { - if (event.getType() == TickEvent.Type.OUT) { - following = null; - return; - } - if (following == null) { - return; - } - // lol this is trashy but it works - BlockPos pos; - if (Baritone.settings().followOffsetDistance.get() == 0) { - pos = following.getPosition(); - } else { - GoalXZ g = GoalXZ.fromDirection(following.getPositionVector(), Baritone.settings().followOffsetDirection.get(), Baritone.settings().followOffsetDistance.get()); - pos = new BlockPos(g.getX(), following.posY, g.getZ()); - } - PathingBehavior.INSTANCE.setGoal(new GoalNear(pos, Baritone.settings().followRadius.get())); - PathingBehavior.INSTANCE.revalidateGoal(); - PathingBehavior.INSTANCE.path(); - } - - @Override - public void follow(Entity entity) { - this.following = entity; - } - - @Override - public Entity following() { - return this.following; - } - - @Override - public void cancel() { - PathingBehavior.INSTANCE.cancel(); - follow(null); - } -} diff --git a/src/main/java/baritone/behavior/LocationTrackingBehavior.java b/src/main/java/baritone/behavior/LocationTrackingBehavior.java deleted file mode 100644 index dee74e79..00000000 --- a/src/main/java/baritone/behavior/LocationTrackingBehavior.java +++ /dev/null @@ -1,53 +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.behavior; - -import baritone.api.event.events.BlockInteractEvent; -import baritone.cache.Waypoint; -import baritone.cache.WorldProvider; -import baritone.utils.BlockStateInterface; -import baritone.utils.Helper; -import net.minecraft.block.BlockBed; - -/** - * A collection of event methods that are used to interact with Baritone's - * waypoint system. This class probably needs a better name. - * - * @see Waypoint - * - * @author Brady - * @since 8/22/2018 - */ -public final class LocationTrackingBehavior extends Behavior implements Helper { - - public static final LocationTrackingBehavior INSTANCE = new LocationTrackingBehavior(); - - private LocationTrackingBehavior() {} - - @Override - public void onBlockInteract(BlockInteractEvent event) { - if (event.getType() == BlockInteractEvent.Type.USE && BlockStateInterface.getBlock(event.getPos()) instanceof BlockBed) { - WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, event.getPos())); - } - } - - @Override - public void onPlayerDeath() { - WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("death", Waypoint.Tag.DEATH, playerFeet())); - } -} diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index a42a1e50..ee8548a2 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -27,8 +27,6 @@ import baritone.utils.Helper; public final class LookBehavior extends Behavior implements ILookBehavior, Helper { - public static final LookBehavior INSTANCE = new LookBehavior(); - /** * Target's values are as follows: *

@@ -49,7 +47,9 @@ public final class LookBehavior extends Behavior implements ILookBehavior, Helpe */ private float lastYaw; - private LookBehavior() {} + public LookBehavior(Baritone baritone) { + super(baritone); + } @Override public void updateTarget(Rotation target, boolean force) { @@ -64,7 +64,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior, Helpe } // Whether or not we're going to silently set our angles - boolean silent = Baritone.settings().antiCheatCompatibility.get(); + boolean silent = Baritone.settings().antiCheatCompatibility.get() && !this.force; switch (event.getState()) { case PRE: { @@ -77,14 +77,15 @@ public final class LookBehavior extends Behavior implements ILookBehavior, Helpe nudgeToLevel(); } this.target = null; - } else if (silent) { + } + if (silent) { this.lastYaw = player().rotationYaw; player().rotationYaw = this.target.getYaw(); } break; } case POST: { - if (!this.force && silent) { + if (silent) { player().rotationYaw = this.lastYaw; this.target = null; } @@ -98,26 +99,20 @@ public final class LookBehavior extends Behavior implements ILookBehavior, Helpe @Override public void onPlayerRotationMove(RotationMoveEvent event) { if (this.target != null && !this.force) { - switch (event.getState()) { - case PRE: - this.lastYaw = player().rotationYaw; - player().rotationYaw = this.target.getYaw(); - break; - case POST: - player().rotationYaw = this.lastYaw; - // 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) { - this.target = null; - } - break; - default: - break; + 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) { + this.target = null; } } } + /** + * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1}) + */ private void nudgeToLevel() { if (player().rotationPitch < -20) { player().rotationPitch++; diff --git a/src/main/java/baritone/behavior/LookBehaviorUtils.java b/src/main/java/baritone/behavior/LookBehaviorUtils.java deleted file mode 100644 index 8cbe01ab..00000000 --- a/src/main/java/baritone/behavior/LookBehaviorUtils.java +++ /dev/null @@ -1,139 +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.behavior; - -import baritone.api.utils.Rotation; -import baritone.utils.BlockStateInterface; -import baritone.utils.Helper; -import baritone.utils.RayTraceUtils; -import baritone.utils.Utils; -import net.minecraft.block.BlockFire; -import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.*; - -import java.util.Optional; - -import static baritone.utils.Utils.DEG_TO_RAD; - -public final class LookBehaviorUtils implements Helper { - - /** - * Offsets from the root block position to the center of each side. - */ - private static final Vec3d[] BLOCK_SIDE_MULTIPLIERS = new Vec3d[]{ - new Vec3d(0.5, 0, 0.5), // Down - new Vec3d(0.5, 1, 0.5), // Up - new Vec3d(0.5, 0.5, 0), // North - new Vec3d(0.5, 0.5, 1), // South - new Vec3d(0, 0.5, 0.5), // West - new Vec3d(1, 0.5, 0.5) // East - }; - - /** - * Calculates a vector given a rotation array - * - * @param rotation {@link LookBehavior#target} - * @return vector of the rotation - */ - public static Vec3d calcVec3dFromRotation(Rotation rotation) { - float f = MathHelper.cos(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); - float f1 = MathHelper.sin(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); - float f2 = -MathHelper.cos(-rotation.getPitch() * (float) DEG_TO_RAD); - float f3 = MathHelper.sin(-rotation.getPitch() * (float) DEG_TO_RAD); - return new Vec3d((double) (f1 * f2), (double) f3, (double) (f * f2)); - } - - public static Optional reachable(BlockPos pos) { - if (pos.equals(getSelectedBlock().orElse(null))) { - /* - * why add 0.0001? - * to indicate that we actually have a desired pitch - * the way we indicate that the pitch can be whatever and we only care about the yaw - * is by setting the desired pitch to the current pitch - * setting the desired pitch to the current pitch + 0.0001 means that we do have a desired pitch, it's - * just what it currently is - */ - return Optional.of(new Rotation(mc.player.rotationYaw, mc.player.rotationPitch + 0.0001f)); - } - Optional possibleRotation = reachableCenter(pos); - //System.out.println("center: " + possibleRotation); - if (possibleRotation.isPresent()) { - return possibleRotation; - } - - IBlockState state = BlockStateInterface.get(pos); - AxisAlignedBB aabb = state.getBoundingBox(mc.world, pos); - for (Vec3d sideOffset : BLOCK_SIDE_MULTIPLIERS) { - double xDiff = aabb.minX * sideOffset.x + aabb.maxX * (1 - sideOffset.x); - double yDiff = aabb.minY * sideOffset.y + aabb.maxY * (1 - sideOffset.y); - double zDiff = aabb.minZ * sideOffset.z + aabb.maxZ * (1 - sideOffset.z); - possibleRotation = reachableOffset(pos, new Vec3d(pos).add(xDiff, yDiff, zDiff)); - if (possibleRotation.isPresent()) { - return possibleRotation; - } - } - return Optional.empty(); - } - - /** - * Checks if coordinate is reachable with the given block-face rotation offset - * - * @param pos - * @param offsetPos - * @return - */ - protected static Optional reachableOffset(BlockPos pos, Vec3d offsetPos) { - Rotation rotation = Utils.calcRotationFromVec3d(mc.player.getPositionEyes(1.0F), offsetPos); - RayTraceResult result = RayTraceUtils.rayTraceTowards(rotation); - System.out.println(result); - if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { - if (result.getBlockPos().equals(pos)) { - return Optional.of(rotation); - } - if (BlockStateInterface.get(pos).getBlock() instanceof BlockFire) { - if (result.getBlockPos().equals(pos.down())) { - return Optional.of(rotation); - } - } - } - return Optional.empty(); - } - - /** - * Checks if center of block at coordinate is reachable - * - * @param pos - * @return - */ - protected static Optional reachableCenter(BlockPos pos) { - return reachableOffset(pos, Utils.calcCenterFromCoords(pos, mc.world)); - } - - /** - * The currently highlighted block. - * Updated once a tick by Minecraft. - * - * @return the position of the highlighted block - */ - public static Optional getSelectedBlock() { - if (mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) { - return Optional.of(mc.objectMouseOver.getBlockPos()); - } - return Optional.empty(); - } -} diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index b0980322..1e8e069a 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -17,12 +17,18 @@ package baritone.behavior; +import baritone.Baritone; import baritone.api.behavior.IMemoryBehavior; import baritone.api.behavior.memory.IRememberedInventory; +import baritone.api.cache.IWorldData; +import baritone.api.event.events.BlockInteractEvent; import baritone.api.event.events.PacketEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.type.EventState; +import baritone.cache.Waypoint; +import baritone.utils.BlockStateInterface; import baritone.utils.Helper; +import net.minecraft.block.BlockBed; import net.minecraft.item.ItemStack; import net.minecraft.network.Packet; import net.minecraft.network.play.client.CPacketCloseWindow; @@ -41,29 +47,21 @@ import java.util.*; */ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, Helper { - public static MemoryBehavior INSTANCE = new MemoryBehavior(); + private final Map worldDataContainers = new HashMap<>(); - /** - * Possible future inventories that we will be able to remember - */ - private final List futureInventories = new ArrayList<>(); - - /** - * The current remembered inventories - */ - private final Map rememberedInventories = new HashMap<>(); - - private MemoryBehavior() {} + public MemoryBehavior(Baritone baritone) { + super(baritone); + } @Override - public void onPlayerUpdate(PlayerUpdateEvent event) { + public synchronized void onPlayerUpdate(PlayerUpdateEvent event) { if (event.getState() == EventState.PRE) { updateInventory(); } } @Override - public void onSendPacket(PacketEvent event) { + public synchronized void onSendPacket(PacketEvent event) { Packet p = event.getPacket(); if (event.getState() == EventState.PRE) { @@ -78,7 +76,7 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H TileEntityLockable lockable = (TileEntityLockable) tileEntity; int size = lockable.getSizeInventory(); - this.futureInventories.add(new FutureInventory(System.nanoTime() / 1000000L, size, lockable.getGuiID(), tileEntity.getPos())); + this.getCurrentContainer().futureInventories.add(new FutureInventory(System.nanoTime() / 1000000L, size, lockable.getGuiID(), tileEntity.getPos())); } } @@ -89,24 +87,26 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H } @Override - public void onReceivePacket(PacketEvent event) { + public synchronized void onReceivePacket(PacketEvent event) { Packet p = event.getPacket(); if (event.getState() == EventState.PRE) { if (p instanceof SPacketOpenWindow) { SPacketOpenWindow packet = event.cast(); - // Remove any entries that were created over a second ago, this should make up for INSANE latency - this.futureInventories.removeIf(i -> System.nanoTime() / 1000000L - i.time > 1000); + WorldDataContainer container = this.getCurrentContainer(); - this.futureInventories.stream() + // Remove any entries that were created over a second ago, this should make up for INSANE latency + container.futureInventories.removeIf(i -> System.nanoTime() / 1000000L - i.time > 1000); + + container.futureInventories.stream() .filter(i -> i.type.equals(packet.getGuiId()) && i.slots == packet.getSlotCount()) .findFirst().ifPresent(matched -> { // Remove the future inventory - this.futureInventories.remove(matched); + container.futureInventories.remove(matched); // Setup the remembered inventory - RememberedInventory inventory = this.rememberedInventories.computeIfAbsent(matched.pos, pos -> new RememberedInventory()); + RememberedInventory inventory = container.rememberedInventories.computeIfAbsent(matched.pos, pos -> new RememberedInventory()); inventory.windowId = packet.getWindowId(); inventory.size = packet.getSlotCount(); }); @@ -118,8 +118,20 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H } } + @Override + public void onBlockInteract(BlockInteractEvent event) { + if (event.getType() == BlockInteractEvent.Type.USE && BlockStateInterface.getBlock(event.getPos()) instanceof BlockBed) { + baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, event.getPos())); + } + } + + @Override + public void onPlayerDeath() { + baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("death", Waypoint.Tag.DEATH, playerFeet())); + } + private Optional getInventoryFromWindow(int windowId) { - return this.rememberedInventories.values().stream().filter(i -> i.windowId == windowId).findFirst(); + return this.getCurrentContainer().rememberedInventories.values().stream().filter(i -> i.windowId == windowId).findFirst(); } private void updateInventory() { @@ -129,9 +141,32 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H }); } + private WorldDataContainer getCurrentContainer() { + return this.worldDataContainers.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), data -> new WorldDataContainer()); + } + @Override - public final RememberedInventory getInventoryByPos(BlockPos pos) { - return this.rememberedInventories.get(pos); + public final synchronized RememberedInventory getInventoryByPos(BlockPos pos) { + return this.getCurrentContainer().rememberedInventories.get(pos); + } + + @Override + public final synchronized Map getRememberedInventories() { + // make a copy since this map is modified from the packet thread + return new HashMap<>(this.getCurrentContainer().rememberedInventories); + } + + private static final class WorldDataContainer { + + /** + * Possible future inventories that we will be able to remember + */ + private final List futureInventories = new ArrayList<>(); + + /** + * The current remembered inventories + */ + private final Map rememberedInventories = new HashMap<>(); } /** @@ -170,7 +205,7 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H /** * An inventory that we are aware of. *

- * Associated with a {@link BlockPos} in {@link MemoryBehavior#rememberedInventories}. + * Associated with a {@link BlockPos} in {@link WorldDataContainer#rememberedInventories}. */ public static class RememberedInventory implements IRememberedInventory { @@ -195,7 +230,7 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior, H @Override public final List getContents() { - return this.items; + return Collections.unmodifiableList(this.items); } @Override diff --git a/src/main/java/baritone/behavior/MineBehavior.java b/src/main/java/baritone/behavior/MineBehavior.java deleted file mode 100644 index 0f1a6b34..00000000 --- a/src/main/java/baritone/behavior/MineBehavior.java +++ /dev/null @@ -1,195 +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.behavior; - -import baritone.Baritone; -import baritone.api.behavior.IMineBehavior; -import baritone.api.event.events.PathEvent; -import baritone.api.event.events.TickEvent; -import baritone.api.pathing.goals.Goal; -import baritone.cache.CachedChunk; -import baritone.cache.ChunkPacker; -import baritone.cache.WorldProvider; -import baritone.cache.WorldScanner; -import baritone.api.pathing.goals.GoalBlock; -import baritone.api.pathing.goals.GoalComposite; -import baritone.api.pathing.goals.GoalTwoBlocks; -import baritone.utils.BlockStateInterface; -import baritone.utils.Helper; -import net.minecraft.block.Block; -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.chunk.EmptyChunk; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Mine blocks of a certain type - * - * @author leijurv - */ -public final class MineBehavior extends Behavior implements IMineBehavior, Helper { - - public static final MineBehavior INSTANCE = new MineBehavior(); - - private List mining; - private List locationsCache; - private int quantity; - - private MineBehavior() {} - - @Override - public void onTick(TickEvent event) { - if (event.getType() == TickEvent.Type.OUT) { - cancel(); - return; - } - if (mining == null) { - return; - } - if (quantity > 0) { - Item item = mining.get(0).getItemDropped(mining.get(0).getDefaultState(), new Random(), 0); - int curr = player().inventory.mainInventory.stream().filter(stack -> item.equals(stack.getItem())).mapToInt(ItemStack::getCount).sum(); - System.out.println("Currently have " + curr + " " + item); - if (curr >= quantity) { - logDirect("Have " + curr + " " + item.getItemStackDisplayName(new ItemStack(item, 1))); - cancel(); - return; - } - } - int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.get(); - if (mineGoalUpdateInterval != 0) { - if (event.getCount() % mineGoalUpdateInterval == 0) { - Baritone.INSTANCE.getExecutor().execute(this::updateGoal); - } - } - PathingBehavior.INSTANCE.revalidateGoal(); - } - - @Override - public void onPathEvent(PathEvent event) { - updateGoal(); - } - - private void updateGoal() { - if (mining == null) { - return; - } - if (!locationsCache.isEmpty()) { - locationsCache = prune(new ArrayList<>(locationsCache), mining, 64); - PathingBehavior.INSTANCE.setGoal(coalesce(locationsCache)); - PathingBehavior.INSTANCE.path(); - } - List locs = scanFor(mining, 64); - if (locs.isEmpty()) { - logDebug("No locations for " + mining + " known, cancelling"); - cancel(); - return; - } - locationsCache = locs; - PathingBehavior.INSTANCE.setGoal(coalesce(locs)); - PathingBehavior.INSTANCE.path(); - } - - public GoalComposite coalesce(List locs) { - return new GoalComposite(locs.stream().map(loc -> { - if (!Baritone.settings().forceInternalMining.get()) { - return new GoalTwoBlocks(loc); - } - - boolean upwardGoal = locs.contains(loc.up()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(loc.up()) == Blocks.AIR); - boolean downwardGoal = locs.contains(loc.down()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(loc.up()) == Blocks.AIR); - if (upwardGoal) { - if (downwardGoal) { - return new GoalTwoBlocks(loc); - } else { - return new GoalBlock(loc); - } - } else { - if (downwardGoal) { - return new GoalBlock(loc.down()); - } else { - return new GoalTwoBlocks(loc); - } - } - }).toArray(Goal[]::new)); - } - - public List scanFor(List mining, int max) { - 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(WorldProvider.INSTANCE.getCurrentWorld().getCachedWorld().getLocationsOf(ChunkPacker.blockToString(m), 1, 1)); - } else { - uninteresting.add(m); - } - } - //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.scanLoadedChunks(uninteresting, max, 10, 26)); - //System.out.println("Scan of loaded chunks took " + (System.currentTimeMillis() - before) + "ms"); - } - return prune(locs, mining, max); - } - - public List prune(List locs, List mining, int max) { - BlockPos playerFeet = MineBehavior.INSTANCE.playerFeet(); - locs.sort(Comparator.comparingDouble(playerFeet::distanceSq)); - - // remove any that are within loaded chunks that aren't actually what we want - locs.removeAll(locs.stream() - .filter(pos -> !(MineBehavior.INSTANCE.world().getChunk(pos) instanceof EmptyChunk)) - .filter(pos -> !mining.contains(BlockStateInterface.get(pos).getBlock())) - .collect(Collectors.toList())); - if (locs.size() > max) { - return locs.subList(0, max); - } - return locs; - } - - @Override - public void mine(int quantity, String... blocks) { - this.mining = blocks == null || blocks.length == 0 ? null : Arrays.stream(blocks).map(ChunkPacker::stringToBlock).collect(Collectors.toList()); - this.quantity = quantity; - this.locationsCache = new ArrayList<>(); - updateGoal(); - } - - @Override - public void mine(int quantity, Block... blocks) { - this.mining = blocks == null || blocks.length == 0 ? null : Arrays.asList(blocks); - this.quantity = quantity; - this.locationsCache = new ArrayList<>(); - updateGoal(); - } - - @Override - public void cancel() { - mine(0, (String[]) null); - PathingBehavior.INSTANCE.cancel(); - } -} diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 81636112..b1d2fc56 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -23,39 +23,41 @@ import baritone.api.event.events.PathEvent; import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.RenderEvent; import baritone.api.event.events.TickEvent; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.PathCalculationResult; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; -import baritone.pathing.calc.IPathFinder; +import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; -import baritone.pathing.path.IPath; +import baritone.pathing.path.CutoffPath; import baritone.pathing.path.PathExecutor; import baritone.utils.BlockBreakHelper; -import baritone.utils.BlockStateInterface; import baritone.utils.Helper; import baritone.utils.PathRenderer; -import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.EmptyChunk; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; public final class PathingBehavior extends Behavior implements IPathingBehavior, Helper { - public static final PathingBehavior INSTANCE = new PathingBehavior(); - private PathExecutor current; private PathExecutor next; private Goal goal; + private boolean safeToCancel; + private boolean pauseRequestedLastTick; + private boolean cancelRequested; + private boolean calcFailedLastTick; + private volatile boolean isPathCalcInProgress; private final Object pathCalcLock = new Object(); @@ -63,29 +65,59 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private boolean lastAutoJump; - private PathingBehavior() {} + private final LinkedBlockingQueue toDispatch = new LinkedBlockingQueue<>(); - private void dispatchPathEvent(PathEvent event) { - Baritone.INSTANCE.getExecutor().execute(() -> Baritone.INSTANCE.getGameEventHandler().onPathEvent(event)); + public PathingBehavior(Baritone baritone) { + super(baritone); + } + + private void queuePathEvent(PathEvent event) { + toDispatch.add(event); + } + + private void dispatchEvents() { + ArrayList curr = new ArrayList<>(); + toDispatch.drainTo(curr); + calcFailedLastTick = curr.contains(PathEvent.CALC_FAILED); + for (PathEvent event : curr) { + baritone.getGameEventHandler().onPathEvent(event); + } } @Override public void onTick(TickEvent event) { + dispatchEvents(); if (event.getType() == TickEvent.Type.OUT) { - this.cancel(); + secretInternalSegmentCancel(); + baritone.getPathingControlManager().cancelEverything(); return; } - mc.playerController.setPlayerCapabilities(mc.player); + baritone.getPathingControlManager().preTick(); + tickPath(); + dispatchEvents(); + } + + private void tickPath() { + if (pauseRequestedLastTick && safeToCancel) { + pauseRequestedLastTick = false; + baritone.getInputOverrideHandler().clearAllKeys(); + BlockBreakHelper.stopBreakingBlock(); + return; + } + if (cancelRequested) { + cancelRequested = false; + baritone.getInputOverrideHandler().clearAllKeys(); + } if (current == null) { return; } - boolean safe = current.onTick(event); + safeToCancel = current.onTick(); synchronized (pathPlanLock) { if (current.failed() || current.finished()) { current = null; if (goal == null || goal.isInGoal(playerFeet())) { logDebug("All done. At " + goal); - dispatchPathEvent(PathEvent.AT_GOAL); + queuePathEvent(PathEvent.AT_GOAL); next = null; return; } @@ -97,41 +129,42 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, // but if we fail in the middle of current // we're nowhere close to our planned ahead path // so need to discard it sadly. - dispatchPathEvent(PathEvent.DISCARD_NEXT); + queuePathEvent(PathEvent.DISCARD_NEXT); next = null; } if (next != null) { logDebug("Continuing on to planned next path"); - dispatchPathEvent(PathEvent.CONTINUING_ONTO_PLANNED_NEXT); + queuePathEvent(PathEvent.CONTINUING_ONTO_PLANNED_NEXT); current = next; next = null; + current.onTick(); return; } // at this point, current just ended, but we aren't in the goal and have no plan for the future synchronized (pathCalcLock) { if (isPathCalcInProgress) { - dispatchPathEvent(PathEvent.PATH_FINISHED_NEXT_STILL_CALCULATING); + queuePathEvent(PathEvent.PATH_FINISHED_NEXT_STILL_CALCULATING); // if we aren't calculating right now return; } - dispatchPathEvent(PathEvent.CALC_STARTED); + queuePathEvent(PathEvent.CALC_STARTED); findPathInNewThread(pathStart(), true, Optional.empty()); } return; } // at this point, we know current is in progress - if (safe) { - // a movement just ended - if (next != null) { - if (next.getPath().positions().contains(playerFeet())) { - // jump directly onto the next path - logDebug("Splicing into planned next path early..."); - dispatchPathEvent(PathEvent.SPLICING_ONTO_NEXT_EARLY); - current = next; - next = null; - return; - } - } + if (safeToCancel && next != null && next.snipsnapifpossible()) { + // a movement just ended; jump directly onto the next path + logDebug("Splicing into planned next path early..."); + queuePathEvent(PathEvent.SPLICING_ONTO_NEXT_EARLY); + current = next; + next = null; + current.onTick(); + return; + } + current = current.trySplice(next); + if (next != null && current.getPath().getDest().equals(next.getPath().getDest())) { + next = null; } synchronized (pathCalcLock) { if (isPathCalcInProgress) { @@ -149,7 +182,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, if (ticksRemainingInSegment().get() < Baritone.settings().planningTickLookAhead.get()) { // and this path has 5 seconds or less left logDebug("Path almost over. Planning ahead..."); - dispatchPathEvent(PathEvent.NEXT_SEGMENT_CALC_STARTED); + queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_STARTED); findPathInNewThread(current.getPath().getDest(), false, Optional.of(current.getPath())); } } @@ -181,29 +214,33 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return Optional.of(current.getPath().ticksRemainingFrom(current.getPosition())); } - @Override - public void setGoal(Goal goal) { + public void secretInternalSetGoal(Goal goal) { this.goal = goal; } + public boolean secretInternalSetGoalAndPath(Goal goal) { + secretInternalSetGoal(goal); + return secretInternalPath(); + } + @Override public Goal getGoal() { return goal; } + @Override public PathExecutor getCurrent() { return current; } + @Override public PathExecutor getNext() { return next; } - // TODO: Expose this method in the API? - // In order to do so, we'd need to move over IPath which has a whole lot of references to other - // things that may not need to be exposed necessarily, so we'll need to figure that out. - public Optional getPath() { - return Optional.ofNullable(current).map(PathExecutor::getPath); + @Override + public Optional getPathFinder() { + return Optional.ofNullable(AbstractNodeCostSearch.currentlyRunning()); } @Override @@ -211,17 +248,60 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return this.current != null; } + public boolean isSafeToCancel() { + return current == null || safeToCancel; + } + + public void requestPause() { + pauseRequestedLastTick = true; + } + + public boolean cancelSegmentIfSafe() { + if (isSafeToCancel()) { + secretInternalSegmentCancel(); + return true; + } + return false; + } + @Override - public void cancel() { - dispatchPathEvent(PathEvent.CANCELED); + public boolean cancelEverything() { + boolean doIt = isSafeToCancel(); + if (doIt) { + secretInternalSegmentCancel(); + } + baritone.getPathingControlManager().cancelEverything(); + return doIt; + } + + public boolean calcFailedLastTick() { // NOT exposed on public api + return calcFailedLastTick; + } + + public void softCancelIfSafe() { + if (!isSafeToCancel()) { + return; + } current = null; next = null; - Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); + cancelRequested = true; + AbstractNodeCostSearch.getCurrentlyRunning().ifPresent(AbstractNodeCostSearch::cancel); + // do everything BUT clear keys + } + + // just cancel the current path + public void secretInternalSegmentCancel() { + queuePathEvent(PathEvent.CANCELED); + current = null; + next = null; + baritone.getInputOverrideHandler().clearAllKeys(); AbstractNodeCostSearch.getCurrentlyRunning().ifPresent(AbstractNodeCostSearch::cancel); BlockBreakHelper.stopBreakingBlock(); } public void forceCancel() { // NOT exposed on public api + cancelEverything(); + secretInternalSegmentCancel(); isPathCalcInProgress = false; } @@ -230,8 +310,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, * * @return true if this call started path calculation, false if it was already calculating or executing a path */ - @Override - public boolean path() { + public boolean secretInternalPath() { if (goal == null) { return false; } @@ -246,7 +325,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, if (isPathCalcInProgress) { return false; } - dispatchPathEvent(PathEvent.CALC_STARTED); + queuePathEvent(PathEvent.CALC_STARTED); findPathInNewThread(pathStart(), true, Optional.empty()); return true; } @@ -254,12 +333,46 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } /** + * See issue #209 + * * @return The starting {@link BlockPos} for a new path */ - private BlockPos pathStart() { + public BlockPos pathStart() { BetterBlockPos feet = playerFeet(); - if (BlockStateInterface.get(feet.down()).getBlock().equals(Blocks.AIR) && MovementHelper.canWalkOn(feet.down().down())) { - return feet.down(); + if (!MovementHelper.canWalkOn(feet.down())) { + if (player().onGround) { + double playerX = player().posX; + double playerZ = player().posZ; + ArrayList closest = new ArrayList<>(); + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + closest.add(new BetterBlockPos(feet.x + dx, feet.y, feet.z + dz)); + } + } + closest.sort(Comparator.comparingDouble(pos -> ((pos.x + 0.5D) - playerX) * ((pos.x + 0.5D) - playerX) + ((pos.z + 0.5D) - playerZ) * ((pos.z + 0.5D) - playerZ))); + for (int i = 0; i < 4; i++) { + BetterBlockPos possibleSupport = closest.get(i); + double xDist = Math.abs((possibleSupport.x + 0.5D) - playerX); + double zDist = Math.abs((possibleSupport.z + 0.5D) - playerZ); + if (xDist > 0.8 && zDist > 0.8) { + // can't possibly be sneaking off of this one, we're too far away + continue; + } + if (MovementHelper.canWalkOn(possibleSupport.down()) && MovementHelper.canWalkThrough(possibleSupport) && MovementHelper.canWalkThrough(possibleSupport.up())) { + // this is plausible + logDebug("Faking path start assuming player is standing off the edge of a block"); + return possibleSupport; + } + } + + } else { + // !onGround + // we're in the middle of a jump + if (MovementHelper.canWalkOn(feet.down().down())) { + logDebug("Faking path start assuming player is midair and falling"); + return feet.down(); + } + } } return feet; } @@ -277,48 +390,72 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } isPathCalcInProgress = true; } - Baritone.INSTANCE.getExecutor().execute(() -> { + CalculationContext context = new CalculationContext(); // not safe to create on the other thread, it looks up a lot of stuff in minecraft + Baritone.getExecutor().execute(() -> { if (talkAboutIt) { logDebug("Starting to search for path from " + start + " to " + goal); } - Optional path = findPath(start, previous); + PathCalculationResult calcResult = findPath(start, previous, context); + Optional path = calcResult.path; if (Baritone.settings().cutoffAtLoadBoundary.get()) { - path = path.map(IPath::cutoffAtLoadedChunks); + path = path.map(p -> { + IPath result = p.cutoffAtLoadedChunks(context.world()); + + 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 -> p.staticCutoff(goal)).map(PathExecutor::new); + + 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(PathExecutor::new); + synchronized (pathPlanLock) { if (current == null) { if (executor.isPresent()) { - dispatchPathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING); + queuePathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING); current = executor.get(); } else { - dispatchPathEvent(PathEvent.CALC_FAILED); + if (calcResult.type != PathCalculationResult.Type.CANCELLATION && calcResult.type != PathCalculationResult.Type.EXCEPTION) { + // don't dispatch CALC_FAILED on cancellation + queuePathEvent(PathEvent.CALC_FAILED); + } } } else { if (next == null) { if (executor.isPresent()) { - dispatchPathEvent(PathEvent.NEXT_SEGMENT_CALC_FINISHED); + queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_FINISHED); next = executor.get(); } else { - dispatchPathEvent(PathEvent.NEXT_CALC_FAILED); + queuePathEvent(PathEvent.NEXT_CALC_FAILED); } } else { throw new IllegalStateException("I have no idea what to do with this path"); } } - } - - if (talkAboutIt && current != null && current.getPath() != null) { - if (goal == null || 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"); - + if (talkAboutIt && current != null && current.getPath() != null) { + if (goal == null || 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"); + } + } + synchronized (pathCalcLock) { + isPathCalcInProgress = false; } - } - synchronized (pathCalcLock) { - isPathCalcInProgress = false; } }); } @@ -329,20 +466,15 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, * @param start * @return */ - private Optional findPath(BlockPos start, Optional previous) { + private PathCalculationResult findPath(BlockPos start, Optional previous, CalculationContext context) { Goal goal = this.goal; if (goal == null) { logDebug("no goal"); - return Optional.empty(); + return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION, Optional.empty()); } - if (Baritone.settings().simplifyUnloadedYCoord.get()) { - BlockPos pos = null; - if (goal instanceof IGoalRenderPos) { - pos = ((IGoalRenderPos) goal).getGoalPos(); - } - - // TODO simplify each individual goal in a GoalComposite - if (pos != null && world().getChunk(pos) instanceof EmptyChunk) { + if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos) { + BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); + if (context.world().getChunk(pos) instanceof EmptyChunk) { logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); goal = new GoalXZ(pos.getX(), pos.getZ()); } @@ -353,89 +485,24 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } else { timeout = Baritone.settings().planAheadTimeoutMS.get(); } - Optional> favoredPositions = previous.map(IPath::positions).map(Collection::stream).map(x -> x.map(y -> y.hashCode)).map(x -> x.collect(Collectors.toList())).map(HashSet::new); // <-- okay this is EPIC + Optional> favoredPositions; + if (Baritone.settings().backtrackCostFavoringCoefficient.get() == 1D) { + favoredPositions = Optional.empty(); + } else { + favoredPositions = previous.map(IPath::positions).map(Collection::stream).map(x -> x.map(BetterBlockPos::longHash)).map(x -> x.collect(Collectors.toList())).map(HashSet::new); // <-- okay this is EPIC + } try { - IPathFinder pf = new AStarPathFinder(start, goal, favoredPositions); + IPathFinder pf = new AStarPathFinder(start.getX(), start.getY(), start.getZ(), goal, favoredPositions, context); return pf.calculate(timeout); } catch (Exception e) { logDebug("Pathing exception: " + e); e.printStackTrace(); - return Optional.empty(); - } - } - - public void revalidateGoal() { - if (!Baritone.settings().cancelOnGoalInvalidation.get()) { - return; - } - if (current == null || goal == null) { - return; - } - Goal intended = current.getPath().getGoal(); - BlockPos end = current.getPath().getDest(); - if (intended.isInGoal(end) && !goal.isInGoal(end)) { - // this path used to end in the goal - // but the goal has changed, so there's no reason to continue... - cancel(); + return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION, Optional.empty()); } } @Override public void onRenderPass(RenderEvent event) { - // System.out.println("Render passing"); - // System.out.println(event.getPartialTicks()); - float partialTicks = event.getPartialTicks(); - if (goal != null && Baritone.settings().renderGoal.value) { - PathRenderer.drawLitDankGoalBox(player(), goal, partialTicks, Baritone.settings().colorGoalBox.get()); - } - if (!Baritone.settings().renderPath.get()) { - return; - } - - //long start = System.nanoTime(); - - - PathExecutor current = this.current; // this should prevent most race conditions? - PathExecutor next = this.next; // like, now it's not possible for current!=null to be true, then suddenly false because of another thread - // TODO is this enough, or do we need to acquire a lock here? - // TODO benchmark synchronized in render loop - - // Render the current path, if there is one - if (current != null && current.getPath() != null) { - int renderBegin = Math.max(current.getPosition() - 3, 0); - PathRenderer.drawPath(current.getPath(), renderBegin, player(), partialTicks, Baritone.settings().colorCurrentPath.get(), Baritone.settings().fadePath.get(), 10, 20); - } - if (next != null && next.getPath() != null) { - PathRenderer.drawPath(next.getPath(), 0, player(), partialTicks, Baritone.settings().colorNextPath.get(), Baritone.settings().fadePath.get(), 10, 20); - } - - //long split = System.nanoTime(); - if (current != null) { - PathRenderer.drawManySelectionBoxes(player(), current.toBreak(), partialTicks, Baritone.settings().colorBlocksToBreak.get()); - PathRenderer.drawManySelectionBoxes(player(), current.toPlace(), partialTicks, Baritone.settings().colorBlocksToPlace.get()); - PathRenderer.drawManySelectionBoxes(player(), current.toWalkInto(), partialTicks, Baritone.settings().colorBlocksToWalkInto.get()); - } - - // If there is a path calculation currently running, render the path calculation process - AbstractNodeCostSearch.getCurrentlyRunning().ifPresent(currentlyRunning -> { - currentlyRunning.bestPathSoFar().ifPresent(p -> { - PathRenderer.drawPath(p, 0, player(), partialTicks, Baritone.settings().colorBestPathSoFar.get(), Baritone.settings().fadePath.get(), 10, 20); - currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { - - PathRenderer.drawPath(mr, 0, player(), partialTicks, Baritone.settings().colorMostRecentConsidered.get(), Baritone.settings().fadePath.get(), 10, 20); - PathRenderer.drawManySelectionBoxes(player(), Collections.singletonList(mr.getDest()), partialTicks, Baritone.settings().colorMostRecentConsidered.get()); - }); - }); - }); - //long end = System.nanoTime(); - //System.out.println((end - split) + " " + (split - start)); - // if (end - start > 0) { - // System.out.println("Frame took " + (split - start) + " " + (end - split)); - //} - } - - @Override - public void onDisable() { - Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); + PathRenderer.render(event, this); } } diff --git a/src/main/java/baritone/cache/CachedChunk.java b/src/main/java/baritone/cache/CachedChunk.java index b8d88a49..abc741a5 100644 --- a/src/main/java/baritone/cache/CachedChunk.java +++ b/src/main/java/baritone/cache/CachedChunk.java @@ -17,7 +17,6 @@ package baritone.cache; -import baritone.api.cache.IBlockTypeAccess; import baritone.utils.Helper; import baritone.utils.pathing.PathingBlockType; import net.minecraft.block.Block; @@ -31,42 +30,64 @@ import java.util.*; * @author Brady * @since 8/3/2018 1:04 AM */ -public final class CachedChunk implements IBlockTypeAccess, Helper { +public final class CachedChunk implements Helper { - public static final Set BLOCKS_TO_KEEP_TRACK_OF = Collections.unmodifiableSet(new HashSet() {{ - add(Blocks.DIAMOND_ORE); - add(Blocks.DIAMOND_BLOCK); - //add(Blocks.COAL_ORE); - add(Blocks.COAL_BLOCK); - //add(Blocks.IRON_ORE); - add(Blocks.IRON_BLOCK); - //add(Blocks.GOLD_ORE); - add(Blocks.GOLD_BLOCK); - add(Blocks.EMERALD_ORE); - add(Blocks.EMERALD_BLOCK); + public static final Set BLOCKS_TO_KEEP_TRACK_OF; - add(Blocks.ENDER_CHEST); - add(Blocks.FURNACE); - add(Blocks.CHEST); - add(Blocks.END_PORTAL); - add(Blocks.END_PORTAL_FRAME); - add(Blocks.MOB_SPAWNER); - // TODO add all shulker colors - add(Blocks.PORTAL); - add(Blocks.HOPPER); - add(Blocks.BEACON); - add(Blocks.BREWING_STAND); - add(Blocks.SKULL); - add(Blocks.ENCHANTING_TABLE); - add(Blocks.ANVIL); - add(Blocks.LIT_FURNACE); - add(Blocks.BED); - add(Blocks.DRAGON_EGG); - add(Blocks.JUKEBOX); - add(Blocks.END_GATEWAY); - add(Blocks.WEB); - add(Blocks.NETHER_WART); - }}); + 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); + + 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. @@ -106,7 +127,9 @@ public final class CachedChunk implements IBlockTypeAccess, Helper { private final Map> specialBlockLocations; - CachedChunk(int x, int z, BitSet data, IBlockState[] overview, Map> specialBlockLocations) { + public final long cacheTimestamp; + + CachedChunk(int x, int z, BitSet data, IBlockState[] overview, Map> specialBlockLocations, long cacheTimestamp) { validateSize(data); this.x = x; @@ -115,11 +138,11 @@ public final class CachedChunk implements IBlockTypeAccess, Helper { this.overview = overview; this.heightMap = new int[256]; this.specialBlockLocations = specialBlockLocations; + this.cacheTimestamp = cacheTimestamp; calculateHeightMap(); } - @Override - public final IBlockState getBlock(int x, int y, int z) { + public final IBlockState getBlock(int x, int y, int z, int dimension) { int internalPos = z << 4 | x; if (heightMap[internalPos] == y) { // we have this exact block, it's a surface block @@ -130,10 +153,10 @@ public final class CachedChunk implements IBlockTypeAccess, Helper { return overview[internalPos]; } PathingBlockType type = getType(x, y, z); - if (type == PathingBlockType.SOLID && y == 127 && mc.player.dimension == -1) { + if (type == PathingBlockType.SOLID && y == 127 && dimension == -1) { return Blocks.BEDROCK.getDefaultState(); } - return ChunkPacker.pathingTypeToBlock(type); + return ChunkPacker.pathingTypeToBlock(type, dimension); } private PathingBlockType getType(int x, int y, int z) { diff --git a/src/main/java/baritone/cache/CachedRegion.java b/src/main/java/baritone/cache/CachedRegion.java index e9402bd5..95c20f37 100644 --- a/src/main/java/baritone/cache/CachedRegion.java +++ b/src/main/java/baritone/cache/CachedRegion.java @@ -17,6 +17,7 @@ package baritone.cache; +import baritone.Baritone; import baritone.api.cache.ICachedRegion; import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; @@ -58,22 +59,25 @@ public final class CachedRegion implements ICachedRegion { */ private final int z; + private final int dimension; + /** * Has this region been modified since its most recent load or save */ private boolean hasUnsavedChanges; - CachedRegion(int x, int z) { + CachedRegion(int x, int z, int dimension) { this.x = x; this.z = z; this.hasUnsavedChanges = false; + this.dimension = dimension; } @Override public final IBlockState getBlock(int x, int y, int z) { CachedChunk chunk = chunks[x >> 4][z >> 4]; if (chunk != null) { - return chunk.getBlock(x & 15, y, z & 15); + return chunk.getBlock(x & 15, y, z & 15, dimension); } return null; } @@ -112,6 +116,7 @@ public final class CachedRegion implements ICachedRegion { if (!hasUnsavedChanges) { return; } + removeExpired(); try { Path path = Paths.get(directory); if (!Files.exists(path)) { @@ -129,8 +134,8 @@ public final class CachedRegion implements ICachedRegion { DataOutputStream out = new DataOutputStream(gzipOut) ) { out.writeInt(CACHED_REGION_MAGIC); - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { CachedChunk chunk = this.chunks[x][z]; if (chunk == null) { out.write(CHUNK_NOT_PRESENT); @@ -143,8 +148,8 @@ public final class CachedRegion implements ICachedRegion { } } } - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { + for (int x = 0; x < 32; x++) { + 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())); @@ -152,8 +157,8 @@ public final class CachedRegion implements ICachedRegion { } } } - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { if (chunks[x][z] != null) { Map> locs = chunks[x][z].getRelativeBlocks(); out.writeShort(locs.entrySet().size()); @@ -168,10 +173,17 @@ public final class CachedRegion implements ICachedRegion { } } } + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (chunks[x][z] != null) { + out.writeLong(chunks[x][z].cacheTimestamp); + } + } + } } hasUnsavedChanges = false; System.out.println("Saved region successfully"); - } catch (IOException ex) { + } catch (Exception ex) { ex.printStackTrace(); } } @@ -203,42 +215,42 @@ public final class CachedRegion implements ICachedRegion { // by switching on the magic value, and either loading it normally, or loading through a converter. throw new IOException("Bad magic value " + magic); } - CachedChunk[][] tmpCached = new CachedChunk[32][32]; + boolean[][] present = new boolean[32][32]; + BitSet[][] bitSets = new BitSet[32][32]; Map>[][] location = new Map[32][32]; - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { + IBlockState[][][] overview = new IBlockState[32][32][]; + long[][] cacheTimestamp = new long[32][32]; + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { int isChunkPresent = in.read(); switch (isChunkPresent) { case CHUNK_PRESENT: byte[] bytes = new byte[CachedChunk.SIZE_IN_BYTES]; in.readFully(bytes); + bitSets[x][z] = BitSet.valueOf(bytes); location[x][z] = new HashMap<>(); - int regionX = this.x; - int regionZ = this.z; - int chunkX = x + 32 * regionX; - int chunkZ = z + 32 * regionZ; - tmpCached[x][z] = new CachedChunk(chunkX, chunkZ, BitSet.valueOf(bytes), new IBlockState[256], location[x][z]); + overview[x][z] = new IBlockState[256]; + present[x][z] = true; break; case CHUNK_NOT_PRESENT: - tmpCached[x][z] = null; break; default: throw new IOException("Malformed stream"); } } } - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { - if (tmpCached[x][z] != null) { + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (present[x][z]) { for (int i = 0; i < 256; i++) { - tmpCached[x][z].getOverview()[i] = ChunkPacker.stringToBlock(in.readUTF()).getDefaultState(); + overview[x][z][i] = ChunkPacker.stringToBlock(in.readUTF()).getDefaultState(); } } } } - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { - if (tmpCached[x][z] != null) { + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (present[x][z]) { // 16 * 16 * 256 = 65536 so a short is enough // ^ haha jokes on leijurv, java doesn't have unsigned types so that isn't correct // also why would you have more than 32767 special blocks in a chunk @@ -264,21 +276,67 @@ public final class CachedRegion implements ICachedRegion { } } } + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (present[x][z]) { + cacheTimestamp[x][z] = in.readLong(); + } + } + } // only if the entire file was uncorrupted do we actually set the chunks for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { - this.chunks[x][z] = tmpCached[x][z]; + if (present[x][z]) { + int regionX = this.x; + int regionZ = this.z; + int chunkX = x + 32 * regionX; + int chunkZ = z + 32 * regionZ; + this.chunks[x][z] = new CachedChunk(chunkX, chunkZ, bitSets[x][z], overview[x][z], location[x][z], cacheTimestamp[x][z]); + } } } } + removeExpired(); hasUnsavedChanges = false; long end = System.nanoTime() / 1000000L; System.out.println("Loaded region successfully in " + (end - start) + "ms"); - } catch (IOException ex) { + } catch (Exception ex) { // corrupted files can cause NullPointerExceptions as well as IOExceptions ex.printStackTrace(); } } + public synchronized final void removeExpired() { + long expiry = Baritone.settings().cachedChunksExpirySeconds.get(); + if (expiry < 0) { + return; + } + long now = System.currentTimeMillis(); + long oldestAcceptableAge = now - expiry * 1000L; + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (this.chunks[x][z] != null && this.chunks[x][z].cacheTimestamp < oldestAcceptableAge) { + System.out.println("Removing chunk " + (x + 32 * this.x) + "," + (z + 32 * this.z) + " because it was cached " + (now - this.chunks[x][z].cacheTimestamp) / 1000L + " seconds ago, and max age is " + expiry); + this.chunks[x][z] = null; + } + } + } + } + + public synchronized final CachedChunk mostRecentlyModified() { + CachedChunk recent = null; + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + if (this.chunks[x][z] == null) { + continue; + } + if (recent == null || this.chunks[x][z].cacheTimestamp > recent.cacheTimestamp) { + recent = this.chunks[x][z]; + } + } + } + return recent; + } + /** * @return The region x coordinate */ diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 3901303d..b775902e 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -56,7 +56,9 @@ public final class CachedWorld implements ICachedWorld, Helper { private final LinkedBlockingQueue toPack = new LinkedBlockingQueue<>(); - CachedWorld(Path directory) { + private final int dimension; + + CachedWorld(Path directory, int dimension) { if (!Files.exists(directory)) { try { Files.createDirectories(directory); @@ -64,11 +66,12 @@ 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.INSTANCE.getExecutor().execute(new PackerThread()); - Baritone.INSTANCE.getExecutor().execute(() -> { + Baritone.getExecutor().execute(new PackerThread()); + Baritone.getExecutor().execute(() -> { try { Thread.sleep(30000); while (true) { @@ -142,6 +145,12 @@ public final class CachedWorld implements ICachedWorld, Helper { public final void save() { if (!Baritone.settings().chunkCaching.get()) { System.out.println("Not saving to disk; chunk caching is disabled."); + allRegions().forEach(region -> { + if (region != null) { + region.removeExpired(); + } + }); // even if we aren't saving to disk, still delete expired old chunks from RAM + prune(); return; } long start = System.nanoTime() / 1000000L; @@ -152,6 +161,56 @@ public final class CachedWorld implements ICachedWorld, Helper { }); long now = System.nanoTime() / 1000000L; System.out.println("World save took " + (now - start) + "ms"); + prune(); + } + + /** + * Delete regions that are too far from the player + */ + private synchronized void prune() { + if (!Baritone.settings().pruneRegionsFromRAM.get()) { + return; + } + BlockPos pruneCenter = guessPosition(); + for (CachedRegion region : allRegions()) { + if (region == null) { + continue; + } + int distX = (region.getX() * 512 + 256) - pruneCenter.getX(); + int distZ = (region.getZ() * 512 + 256) - pruneCenter.getZ(); + double dist = Math.sqrt(distX * distX + distZ * distZ); + if (dist > 1024) { + logDebug("Deleting cached region " + region.getX() + "," + region.getZ() + " from ram"); + cachedRegions.remove(getRegionID(region.getX(), region.getZ())); + } + } + } + + /** + * If we are still in this world and dimension, return player feet, otherwise return most recently modified chunk + */ + private BlockPos guessPosition() { + WorldData data = Baritone.INSTANCE.getWorldProvider().getCurrentWorld(); + if (data != null && data.getCachedWorld() == this) { + return playerFeet(); + } + CachedChunk mostRecentlyModified = null; + for (CachedRegion region : allRegions()) { + if (region == null) { + continue; + } + CachedChunk ch = region.mostRecentlyModified(); + if (ch == null) { + continue; + } + if (mostRecentlyModified == null || mostRecentlyModified.cacheTimestamp < ch.cacheTimestamp) { + mostRecentlyModified = ch; + } + } + if (mostRecentlyModified == null) { + return new BlockPos(0, 0, 0); + } + return new BlockPos(mostRecentlyModified.x * 16 + 8, 0, mostRecentlyModified.z * 16 + 8); } private synchronized List allRegions() { @@ -185,7 +244,7 @@ public final class CachedWorld implements ICachedWorld, Helper { */ private synchronized CachedRegion getOrCreateRegion(int regionX, int regionZ) { return cachedRegions.computeIfAbsent(getRegionID(regionX, regionZ), id -> { - CachedRegion newRegion = new CachedRegion(regionX, regionZ); + CachedRegion newRegion = new CachedRegion(regionX, regionZ, dimension); newRegion.load(this.directory); return newRegion; }); diff --git a/src/main/java/baritone/cache/ChunkPacker.java b/src/main/java/baritone/cache/ChunkPacker.java index 4164be3c..d73cdb03 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -106,7 +106,7 @@ public final class ChunkPacker implements Helper { blocks[z << 4 | x] = Blocks.AIR.getDefaultState(); } } - return new CachedChunk(chunk.x, chunk.z, bitSet, blocks, specialBlocks); + return new CachedChunk(chunk.x, chunk.z, bitSet, blocks, specialBlocks, System.currentTimeMillis()); } public static String blockToString(Block block) { @@ -144,7 +144,7 @@ public final class ChunkPacker implements Helper { return PathingBlockType.SOLID; } - public static IBlockState pathingTypeToBlock(PathingBlockType type) { + public static IBlockState pathingTypeToBlock(PathingBlockType type, int dimension) { switch (type) { case AIR: return Blocks.AIR.getDefaultState(); @@ -154,7 +154,7 @@ public final class ChunkPacker implements Helper { return Blocks.LAVA.getDefaultState(); case SOLID: // Dimension solid types - switch (mc.player.dimension) { + switch (dimension) { case -1: return Blocks.NETHERRACK.getDefaultState(); case 0: diff --git a/src/main/java/baritone/cache/Waypoint.java b/src/main/java/baritone/cache/Waypoint.java index 00f4410a..19e574f9 100644 --- a/src/main/java/baritone/cache/Waypoint.java +++ b/src/main/java/baritone/cache/Waypoint.java @@ -42,9 +42,9 @@ public class Waypoint implements IWaypoint { * Constructor called when a Waypoint is read from disk, adds the creationTimestamp * as a parameter so that it is reserved after a waypoint is wrote to the disk. * - * @param name The waypoint name - * @param tag The waypoint tag - * @param location The waypoint location + * @param name The waypoint name + * @param tag The waypoint tag + * @param location The waypoint location * @param creationTimestamp When the waypoint was created */ Waypoint(String name, Tag tag, BlockPos location, long creationTimestamp) { diff --git a/src/main/java/baritone/cache/WorldData.java b/src/main/java/baritone/cache/WorldData.java index 19461d22..897a0d87 100644 --- a/src/main/java/baritone/cache/WorldData.java +++ b/src/main/java/baritone/cache/WorldData.java @@ -35,15 +35,17 @@ public class WorldData implements IWorldData { private final Waypoints waypoints; //public final MapData map; public final Path directory; + public final int dimension; - WorldData(Path directory) { + WorldData(Path directory, int dimension) { this.directory = directory; - this.cache = new CachedWorld(directory.resolve("cache")); + this.cache = new CachedWorld(directory.resolve("cache"), dimension); this.waypoints = new Waypoints(directory.resolve("waypoints")); + this.dimension = dimension; } - void onClose() { - Baritone.INSTANCE.getExecutor().execute(() -> { + public void onClose() { + Baritone.getExecutor().execute(() -> { System.out.println("Started saving the world in a new thread"); cache.save(); }); diff --git a/src/main/java/baritone/cache/WorldProvider.java b/src/main/java/baritone/cache/WorldProvider.java index 2aef54c6..1bd27a98 100644 --- a/src/main/java/baritone/cache/WorldProvider.java +++ b/src/main/java/baritone/cache/WorldProvider.java @@ -22,7 +22,6 @@ import baritone.api.cache.IWorldProvider; import baritone.utils.Helper; import baritone.utils.accessor.IAnvilChunkLoader; import baritone.utils.accessor.IChunkProviderServer; -import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.world.WorldServer; @@ -39,11 +38,9 @@ import java.util.function.Consumer; * @author Brady * @since 8/4/2018 11:06 AM */ -public enum WorldProvider implements IWorldProvider, Helper { +public class WorldProvider implements IWorldProvider, Helper { - INSTANCE; - - private final Map worldCache = new HashMap<>(); + private static final Map worldCache = new HashMap<>(); // this is how the bots have the same cached world private WorldData currentWorld; @@ -52,13 +49,20 @@ public enum WorldProvider implements IWorldProvider, Helper { return this.currentWorld; } - public final void initWorld(WorldClient world) { - int dimensionID = world.provider.getDimensionType().getId(); + /** + * Called when a new world is initialized to discover the + * + * @param dimension The ID of the world's dimension + */ + public final void initWorld(int dimension) { File directory; File readme; + 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) { - WorldServer localServerWorld = integratedServer.getWorld(dimensionID); + WorldServer localServerWorld = integratedServer.getWorld(dimension); IChunkProviderServer provider = (IChunkProviderServer) localServerWorld.getChunkProvider(); IAnvilChunkLoader loader = (IAnvilChunkLoader) provider.getChunkLoader(); directory = loader.getChunkSaveLocation(); @@ -71,27 +75,27 @@ public enum WorldProvider implements IWorldProvider, Helper { directory = new File(directory, "baritone"); readme = directory; - - } else { - //remote - directory = new File(Baritone.INSTANCE.getDir(), mc.getCurrentServerData().serverIP); - readme = Baritone.INSTANCE.getDir(); + } else { // Otherwise, the server must be remote... + directory = new File(Baritone.getDir(), mc.getCurrentServerData().serverIP); + readme = Baritone.getDir(); } + // lol wtf is this baritone folder in my minecraft save? try (FileOutputStream out = new FileOutputStream(new File(readme, "readme.txt"))) { // good thing we have a readme out.write("https://github.com/cabaletta/baritone\n".getBytes()); } catch (IOException ignored) {} - directory = new File(directory, "DIM" + dimensionID); - Path dir = directory.toPath(); + // We will actually store the world data in a subfolder: "DIM" + Path dir = new File(directory, "DIM" + dimension).toPath(); if (!Files.exists(dir)) { try { Files.createDirectories(dir); } catch (IOException ignored) {} } + System.out.println("Baritone world data dir: " + dir); - this.currentWorld = this.worldCache.computeIfAbsent(dir, WorldData::new); + this.currentWorld = worldCache.computeIfAbsent(dir, d -> new WorldData(d, dimension)); } public final void closeWorld() { diff --git a/src/main/java/baritone/cache/WorldScanner.java b/src/main/java/baritone/cache/WorldScanner.java index d6b466ef..6366b157 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -17,6 +17,7 @@ package baritone.cache; +import baritone.api.cache.IWorldScanner; import baritone.utils.Helper; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; @@ -29,20 +30,12 @@ import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import java.util.LinkedList; import java.util.List; -public enum WorldScanner implements Helper { +public enum WorldScanner implements IWorldScanner, Helper { + INSTANCE; - /** - * Scans the world, up to your render distance, for the specified blocks. - * - * @param blocks 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 maxSearchRadius The maximum chunk search radius - * @return The matching block positions - */ - public List scanLoadedChunks(List blocks, int max, int yLevelThreshold, int maxSearchRadius) { + @Override + public List scanChunkRadius(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()); } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 61756e33..084b5562 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -21,16 +21,12 @@ import baritone.Baritone; import baritone.api.event.events.*; import baritone.api.event.events.type.EventState; import baritone.api.event.listener.IGameEventListener; -import baritone.api.utils.interfaces.Toggleable; import baritone.cache.WorldProvider; -import baritone.utils.BlockStateInterface; import baritone.utils.Helper; -import baritone.utils.InputOverrideHandler; -import net.minecraft.client.settings.KeyBinding; import net.minecraft.world.chunk.Chunk; -import org.lwjgl.input.Keyboard; import java.util.ArrayList; +import java.util.List; /** * @author Brady @@ -38,57 +34,32 @@ import java.util.ArrayList; */ public final class GameEventHandler implements IGameEventListener, Helper { - private final ArrayList listeners = new ArrayList<>(); + private final Baritone baritone; + + private final List listeners = new ArrayList<>(); + + public GameEventHandler(Baritone baritone) { + this.baritone = baritone; + } @Override public final void onTick(TickEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onTick(event); - } - }); + listeners.forEach(l -> l.onTick(event)); } @Override public final void onPlayerUpdate(PlayerUpdateEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onPlayerUpdate(event); - } - }); + listeners.forEach(l -> l.onPlayerUpdate(event)); } @Override public final void onProcessKeyBinds() { - InputOverrideHandler inputHandler = Baritone.INSTANCE.getInputOverrideHandler(); - - // Simulate the key being held down this tick - for (InputOverrideHandler.Input input : InputOverrideHandler.Input.values()) { - KeyBinding keyBinding = input.getKeyBinding(); - - if (inputHandler.isInputForcedDown(keyBinding) && !keyBinding.isKeyDown()) { - int keyCode = keyBinding.getKeyCode(); - - if (keyCode < Keyboard.KEYBOARD_SIZE) { - KeyBinding.onTick(keyCode < 0 ? keyCode + 100 : keyCode); - } - } - } - - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onProcessKeyBinds(); - } - }); + listeners.forEach(IGameEventListener::onProcessKeyBinds); } @Override public final void onSendChatMessage(ChatEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onSendChatMessage(event); - } - }); + listeners.forEach(l -> l.onSendChatMessage(event)); } @Override @@ -107,108 +78,67 @@ public final class GameEventHandler implements IGameEventListener, Helper { && mc.world.getChunkProvider().isChunkGeneratedAt(event.getX(), event.getZ()); if (isPostPopulate || isPreUnload) { - WorldProvider.INSTANCE.ifWorldLoaded(world -> { + baritone.getWorldProvider().ifWorldLoaded(world -> { Chunk chunk = mc.world.getChunk(event.getX(), event.getZ()); world.getCachedWorld().queueForPacking(chunk); }); } - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onChunkEvent(event); - } - }); + listeners.forEach(l -> l.onChunkEvent(event)); } @Override public final void onRenderPass(RenderEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onRenderPass(event); - } - }); + listeners.forEach(l -> l.onRenderPass(event)); } @Override public final void onWorldEvent(WorldEvent event) { - WorldProvider cache = WorldProvider.INSTANCE; - - BlockStateInterface.clearCachedChunk(); + WorldProvider cache = baritone.getWorldProvider(); if (event.getState() == EventState.POST) { cache.closeWorld(); if (event.getWorld() != null) { - cache.initWorld(event.getWorld()); + cache.initWorld(event.getWorld().provider.getDimensionType().getId()); } } - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onWorldEvent(event); - } - }); + listeners.forEach(l -> l.onWorldEvent(event)); } @Override public final void onSendPacket(PacketEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onSendPacket(event); - } - }); + listeners.forEach(l -> l.onSendPacket(event)); } @Override public final void onReceivePacket(PacketEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onReceivePacket(event); - } - }); + listeners.forEach(l -> l.onReceivePacket(event)); } @Override public void onPlayerRotationMove(RotationMoveEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onPlayerRotationMove(event); - } - }); + listeners.forEach(l -> l.onPlayerRotationMove(event)); } @Override public void onBlockInteract(BlockInteractEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onBlockInteract(event); - } - }); + listeners.forEach(l -> l.onBlockInteract(event)); } @Override public void onPlayerDeath() { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onPlayerDeath(); - } - }); + listeners.forEach(IGameEventListener::onPlayerDeath); } @Override public void onPathEvent(PathEvent event) { - listeners.forEach(l -> { - if (canDispatch(l)) { - l.onPathEvent(event); - } - }); + listeners.forEach(l -> l.onPathEvent(event)); } public final void registerEventListener(IGameEventListener listener) { this.listeners.add(listener); } - private boolean canDispatch(IGameEventListener listener) { - return !(listener instanceof Toggleable) || ((Toggleable) listener).isEnabled(); - } } diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index fab4fae3..02f6ac63 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -18,20 +18,21 @@ package baritone.pathing.calc; import baritone.Baritone; +import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.ActionCosts; +import baritone.api.utils.BetterBlockPos; import baritone.pathing.calc.openset.BinaryHeapOpenSet; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Moves; -import baritone.pathing.path.IPath; import baritone.utils.BlockStateInterface; import baritone.utils.Helper; -import baritone.utils.pathing.BetterBlockPos; -import baritone.utils.pathing.MoveResult; -import net.minecraft.util.math.BlockPos; +import baritone.utils.pathing.BetterWorldBorder; +import baritone.utils.pathing.MutableMoveResult; import java.util.*; + /** * The actual A* pathfinding * @@ -40,15 +41,17 @@ import java.util.*; public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { private final Optional> favoredPositions; + private final CalculationContext calcContext; - public AStarPathFinder(BlockPos start, Goal goal, Optional> favoredPositions) { - super(start, goal); + public AStarPathFinder(int startX, int startY, int startZ, Goal goal, Optional> favoredPositions, CalculationContext context) { + super(startX, startY, startZ, goal, context); this.favoredPositions = favoredPositions; + this.calcContext = context; } @Override protected Optional calculate0(long timeout) { - startNode = getNodeAtPosition(start.x, start.y, start.z, posHash(start.x, start.y, start.z)); + startNode = getNodeAtPosition(startX, startY, startZ, BetterBlockPos.longHash(startX, startY, startZ)); startNode.cost = 0; startNode.combinedCost = startNode.estimatedCostToGoal; BinaryHeapOpenSet openSet = new BinaryHeapOpenSet(); @@ -60,9 +63,9 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel bestHeuristicSoFar[i] = startNode.estimatedCostToGoal; bestSoFar[i] = startNode; } - CalculationContext calcContext = new CalculationContext(); + MutableMoveResult res = new MutableMoveResult(); HashSet favored = favoredPositions.orElse(null); - BlockStateInterface.clearCachedChunk(); + BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world().getWorldBorder()); long startTime = System.nanoTime() / 1000000L; boolean slowPath = Baritone.settings().slowPath.get(); if (slowPath) { @@ -119,7 +122,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel numNodes++; if (goal.isInGoal(currentNode.x, currentNode.y, currentNode.z)) { logDebug("Took " + (System.nanoTime() / 1000000L - startTime) + "ms, " + numMovementsConsidered + " movements considered"); - return Optional.of(new Path(startNode, currentNode, numNodes, goal)); + return Optional.of(new Path(startNode, currentNode, numNodes, goal, calcContext)); } goalCheck += System.nanoTime() - t; goalCheckCount++; @@ -127,17 +130,21 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel long s = System.nanoTime(); int newX = currentNode.x + moves.xOffset; int newZ = currentNode.z + moves.zOffset; - if (newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) { + if ((newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) && !calcContext.isLoaded(newX, newZ)) { // only need to check if the destination is a loaded chunk if it's in a different chunk than the start of the movement - if (!BlockStateInterface.isLoaded(newX, newZ)) { - if (!moves.dynamicXZ) { // only increment the counter if the movement would have gone out of bounds guaranteed - numEmptyChunk++; - } - long costStart = System.nanoTime(); - chunk += costStart - s; - chunkCount++; - continue; + if (!moves.dynamicXZ) { // only increment the counter if the movement would have gone out of bounds guaranteed + numEmptyChunk++; } + long costStart = System.nanoTime(); + chunk += costStart - s; + chunkCount++; + continue; + } + if (!moves.dynamicXZ && !worldBorder.entirelyContains(newX, newZ)) { + continue; + } + if (currentNode.y + moves.yOffset > 256 || currentNode.y + moves.yOffset < 0) { + continue; } long costStart = System.nanoTime(); chunk += costStart - s; @@ -145,34 +152,38 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel // TODO cache cost int numLookupsBefore = BlockStateInterface.numBlockStateLookups; long numCreatedBefore = BetterBlockPos.numCreated; - - MoveResult res = moves.apply(calcContext, currentNode.x, currentNode.y, currentNode.z); - + res.reset(); + moves.apply(calcContext, currentNode.x, currentNode.y, currentNode.z, res); long costEnd = System.nanoTime(); stateLookup[moves.ordinal()] += BlockStateInterface.numBlockStateLookups - numLookupsBefore; posCreation[moves.ordinal()] += BetterBlockPos.numCreated - numCreatedBefore; timeConsumed[moves.ordinal()] += costEnd - costStart; count[moves.ordinal()]++; - numMovementsConsidered++; double actionCost = res.cost; if (actionCost >= ActionCosts.COST_INF) { continue; } - // check destination after verifying it's not COST_INF -- some movements return a static IMPOSSIBLE object with COST_INF and destination being 0,0,0 to avoid allocating a new result for every failed calculation - if (!moves.dynamicXZ && (res.destX != newX || res.destZ != newZ)) { - throw new IllegalStateException(moves + " " + res.destX + " " + newX + " " + res.destZ + " " + newZ); - } if (actionCost <= 0) { throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); } - long hashCode = posHash(res.destX, res.destY, res.destZ); + if (moves.dynamicXZ && !worldBorder.entirelyContains(res.x, res.z)) { // see issue #218 + continue; + } + // check destination after verifying it's not COST_INF -- some movements return a static IMPOSSIBLE object with COST_INF and destination being 0,0,0 to avoid allocating a new result for every failed calculation + if (!moves.dynamicXZ && (res.x != newX || res.z != newZ)) { + throw new IllegalStateException(moves + " " + res.x + " " + newX + " " + res.z + " " + newZ); + } + if (!moves.dynamicY && res.y != currentNode.y + moves.yOffset) { + throw new IllegalStateException(moves + " " + res.y + " " + (currentNode.y + moves.yOffset)); + } + long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z); if (favoring && favored.contains(hashCode)) { // see issue #18 actionCost *= favorCoeff; } long st = System.nanoTime(); - PathNode neighbor = getNodeAtPosition(res.destX, res.destY, res.destZ, hashCode); + PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode); getNode += System.nanoTime() - st; getNodeCount++; double tentativeCost = currentNode.cost + actionCost; @@ -270,7 +281,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel 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)); + return Optional.of(new Path(startNode, bestSoFar[i], numNodes, goal, calcContext)); } } logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); diff --git a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java index 49e726b0..79a56037 100644 --- a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java @@ -18,11 +18,12 @@ package baritone.pathing.calc; import baritone.Baritone; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.goals.Goal; -import baritone.pathing.path.IPath; -import baritone.utils.pathing.BetterBlockPos; +import baritone.api.utils.PathCalculationResult; +import baritone.pathing.movement.CalculationContext; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import net.minecraft.util.math.BlockPos; import java.util.Optional; @@ -38,10 +39,14 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { */ private static AbstractNodeCostSearch currentlyRunning = null; - protected final BetterBlockPos start; + protected final int startX; + protected final int startY; + protected final int startZ; protected final Goal goal; + private final CalculationContext context; + /** * @see Issue #107 */ @@ -69,9 +74,12 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { */ protected final static double MIN_DIST_PATH = 5; - AbstractNodeCostSearch(BlockPos start, Goal goal) { - this.start = new BetterBlockPos(start.getX(), start.getY(), start.getZ()); + AbstractNodeCostSearch(int startX, int startY, int startZ, Goal goal, CalculationContext context) { + this.startX = startX; + this.startY = startY; + this.startZ = startZ; this.goal = goal; + this.context = context; this.map = new Long2ObjectOpenHashMap<>(Baritone.settings().pathingMapDefaultSize.value, Baritone.settings().pathingMapLoadFactor.get()); } @@ -79,16 +87,26 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { cancelRequested = true; } - public synchronized Optional calculate(long timeout) { + public synchronized PathCalculationResult calculate(long timeout) { if (isFinished) { throw new IllegalStateException("Path Finder is currently in use, and cannot be reused!"); } this.cancelRequested = false; try { Optional path = calculate0(timeout); - path.ifPresent(IPath::postprocess); + path = path.map(IPath::postProcess); isFinished = true; - return path; + if (cancelRequested) { + return new PathCalculationResult(PathCalculationResult.Type.CANCELLATION, path); + } + if (!path.isPresent()) { + return new PathCalculationResult(PathCalculationResult.Type.FAILURE, path); + } + if (goal.isInGoal(path.get().getDest())) { + return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_TO_GOAL, path); + } else { + return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path); + } } finally { // this is run regardless of what exception may or may not be raised by calculate0 currentlyRunning = null; @@ -115,14 +133,14 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { * @return The distance, squared */ protected double getDistFromStartSq(PathNode n) { - int xDiff = n.x - start.x; - int yDiff = n.y - start.y; - int zDiff = n.z - start.z; + int xDiff = n.x - startX; + int yDiff = n.y - startY; + int zDiff = n.z - startZ; return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; } /** - * Attempts to search the {@link BlockPos} to {@link PathNode} map + * Attempts to search the block position hashCode long to {@link PathNode} map * for the node mapped to the specified pos. If no node is found, * a new node is created. * @@ -138,25 +156,6 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { return node; } - public static long posHash(int x, int y, int z) { - /* - * This is the hashcode implementation of Vec3i, the superclass of BlockPos - * - * public int hashCode() { - * return (this.getY() + this.getZ() * 31) * 31 + this.getX(); - * } - * - * That is terrible and has tons of collisions and makes the HashMap terribly inefficient. - * - * That's why we grab out the X, Y, Z and calculate our own hashcode - */ - long hash = 3241; - hash = 3457689L * hash + x; - hash = 8734625L * hash + y; - hash = 2873465L * hash + z; - return hash; - } - public static void forceCancel() { currentlyRunning = null; } @@ -176,7 +175,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { @Override public Optional pathToMostRecentNodeConsidered() { try { - return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(startNode, node, 0, goal)); + return Optional.ofNullable(mostRecentConsidered).map(node -> new Path(startNode, node, 0, goal, context)); } catch (IllegalStateException ex) { System.out.println("Unable to construct path to render"); return Optional.empty(); @@ -198,7 +197,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { } if (getDistFromStartSq(bestSoFar[i]) > MIN_DIST_PATH * MIN_DIST_PATH) { // square the comparison since distFromStartSq is squared try { - return Optional.of(new Path(startNode, bestSoFar[i], 0, goal)); + return Optional.of(new Path(startNode, bestSoFar[i], 0, goal, context)); } catch (IllegalStateException ex) { System.out.println("Unable to construct path to render"); return Optional.empty(); @@ -220,12 +219,11 @@ public abstract class AbstractNodeCostSearch implements IPathFinder { return goal; } - @Override - public final BlockPos getStart() { - return start; - } - public static Optional getCurrentlyRunning() { return Optional.ofNullable(currentlyRunning); } + + public static AbstractNodeCostSearch currentlyRunning() { + return currentlyRunning; + } } diff --git a/src/main/java/baritone/pathing/calc/Path.java b/src/main/java/baritone/pathing/calc/Path.java index 7f8cf085..0b5b2cc7 100644 --- a/src/main/java/baritone/pathing/calc/Path.java +++ b/src/main/java/baritone/pathing/calc/Path.java @@ -17,12 +17,16 @@ package baritone.pathing.calc; +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.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.Moves; -import baritone.pathing.path.IPath; -import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.util.math.BlockPos; +import baritone.pathing.path.CutoffPath; +import baritone.utils.Helper; +import baritone.utils.pathing.PathBase; import java.util.ArrayList; import java.util.Collections; @@ -34,7 +38,7 @@ import java.util.List; * * @author leijurv */ -class Path implements IPath { +class Path extends PathBase { /** * The start position of this path @@ -54,20 +58,26 @@ class Path implements IPath { private final List movements; + private final List nodes; + private final Goal goal; private final int numNodes; + private final CalculationContext context; + private volatile boolean verified; - Path(PathNode start, PathNode end, int numNodes, Goal goal) { + Path(PathNode start, PathNode end, int numNodes, Goal goal, CalculationContext context) { 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; - assemblePath(start, end); + this.context = context; + assemblePath(end); } @Override @@ -76,83 +86,84 @@ class Path implements IPath { } /** - * Assembles this path given the start and end nodes. + * Assembles this path given the end node. * - * @param start The start node - * @param end The end node + * @param end The end node */ - private void assemblePath(PathNode start, PathNode end) { + private void assemblePath(PathNode end) { if (!path.isEmpty() || !movements.isEmpty()) { throw new IllegalStateException(); } PathNode current = end; - LinkedList tempPath = new LinkedList<>(); // Repeatedly inserting to the beginning of an arraylist is O(n^2) - LinkedList tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end - while (!current.equals(start)) { + LinkedList tempPath = 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) { + tempNodes.addFirst(current); tempPath.addFirst(new BetterBlockPos(current.x, current.y, current.z)); - tempMovements.addFirst(runBackwards(current.previous, current)); current = current.previous; } - tempPath.addFirst(this.start); // 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); - movements.addAll(tempMovements); + nodes.addAll(tempNodes); } - private static Movement runBackwards(PathNode src0, PathNode dest0) { // TODO this is horrifying - BetterBlockPos src = new BetterBlockPos(src0.x, src0.y, src0.z); - BetterBlockPos dest = new BetterBlockPos(dest0.x, dest0.y, dest0.z); + private boolean assembleMovements() { + if (path.isEmpty() || !movements.isEmpty()) { + throw new IllegalStateException(); + } + for (int i = 0; i < path.size() - 1; i++) { + double cost = nodes.get(i + 1).cost - nodes.get(i).cost; + Movement move = runBackwards(path.get(i), path.get(i + 1), cost); + if (move == null) { + return true; + } else { + movements.add(move); + } + } + return false; + } + + private Movement runBackwards(BetterBlockPos src, BetterBlockPos dest, double cost) { for (Moves moves : Moves.values()) { - Movement move = moves.apply0(src); + Movement move = moves.apply0(context, src); if (move.getDest().equals(dest)) { + // have to calculate the cost at calculation time so we can accurately judge whether a cost increase happened between cached calculation and real execution + move.override(cost); return move; } } - // leave this as IllegalStateException; it's caught in AbstractNodeCostSearch - throw new IllegalStateException("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); - } - - /** - * Performs a series of checks to ensure that the assembly of the path went as expected. - */ - private void sanityCheck() { - if (!start.equals(path.get(0))) { - throw new IllegalStateException("Start node does not equal first path element"); - } - if (!end.equals(path.get(path.size() - 1))) { - throw new IllegalStateException("End node does not equal last path element"); - } - if (path.size() != movements.size() + 1) { - throw new IllegalStateException("Size of path array is unexpected"); - } - for (int i = 0; i < path.size() - 1; i++) { - BlockPos src = path.get(i); - BlockPos dest = path.get(i + 1); - Movement movement = movements.get(i); - if (!src.equals(movement.getSrc())) { - throw new IllegalStateException("Path source is not equal to the movement source"); - } - if (!dest.equals(movement.getDest())) { - throw new IllegalStateException("Path destination is not equal to the movement destination"); - } - } + // this is no longer called from bestPathSoFar, now it's in postprocessing + Helper.HELPER.logDebug("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); + return null; } @Override - public void postprocess() { + public IPath postProcess() { if (verified) { throw new IllegalStateException(); } verified = true; + boolean failed = assembleMovements(); + movements.forEach(m -> m.checkLoadedChunk(context)); + + if (failed) { // at least one movement became impossible during calculation + CutoffPath res = new CutoffPath(this, movements().size()); + if (res.movements().size() != movements.size()) { + throw new IllegalStateException(); + } + return res; + } // more post processing here - movements.forEach(Movement::checkLoadedChunk); sanityCheck(); + return this; } @Override - public List movements() { + public List movements() { if (!verified) { throw new IllegalStateException(); } diff --git a/src/main/java/baritone/pathing/calc/PathNode.java b/src/main/java/baritone/pathing/calc/PathNode.java index 8e42d564..e12a2458 100644 --- a/src/main/java/baritone/pathing/calc/PathNode.java +++ b/src/main/java/baritone/pathing/calc/PathNode.java @@ -19,6 +19,7 @@ package baritone.pathing.calc; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.ActionCosts; +import baritone.api.utils.BetterBlockPos; /** * A node in the path, containing the cost and steps to get to it. @@ -30,20 +31,20 @@ public final class PathNode { /** * The position of this node */ - final int x; - final int y; - final int z; + public final int x; + public final int y; + public final int z; /** * Cached, should always be equal to goal.heuristic(pos) */ - final double estimatedCostToGoal; + public final double estimatedCostToGoal; /** * Total cost of getting from start to here * Mutable and changed by PathFinder */ - double cost; + public double cost; /** * Should always be equal to estimatedCosttoGoal + cost @@ -55,13 +56,13 @@ public final class PathNode { * In the graph search, what previous node contributed to the cost * Mutable and changed by PathFinder */ - PathNode previous; + public PathNode previous; /** * Is this a member of the open set in A*? (only used during pathfinding) * Instead of doing a costly member check in the open set, cache membership in each node individually too. */ - boolean isOpen; + public boolean isOpen; /** * Where is this node in the array flattenization of the binary heap? Needed for decrease-key operations. @@ -85,7 +86,7 @@ public final class PathNode { */ @Override public int hashCode() { - return (int) AbstractNodeCostSearch.posHash(x, y, z); + return (int) BetterBlockPos.longHash(x, y, z); } @Override diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index 3149cfe9..e419bb72 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -18,20 +18,34 @@ package baritone.pathing.movement; import baritone.Baritone; +import baritone.api.pathing.movement.ActionCosts; +import baritone.cache.WorldData; +import baritone.utils.BlockStateInterface; import baritone.utils.Helper; import baritone.utils.ToolSet; +import baritone.utils.pathing.BetterWorldBorder; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; /** * @author Brady * @since 8/7/2018 4:30 PM */ -public class CalculationContext implements Helper { +public class CalculationContext { private static final ItemStack STACK_BUCKET_WATER = new ItemStack(Items.WATER_BUCKET); + private final EntityPlayerSP player; + private final World world; + private final WorldData worldData; + private final BlockStateInterface bsi; private final ToolSet toolSet; private final boolean hasWaterBucket; private final boolean hasThrowaway; @@ -40,27 +54,93 @@ public class CalculationContext implements Helper { private final boolean allowBreak; private final int maxFallHeightNoWater; private final int maxFallHeightBucket; + private final double waterWalkSpeed; + private final double breakBlockAdditionalCost; + private final BetterWorldBorder worldBorder; public CalculationContext() { - this(new ToolSet()); - } - - public CalculationContext(ToolSet toolSet) { - this.toolSet = toolSet; + this.player = Helper.HELPER.player(); + this.world = Helper.HELPER.world(); + this.worldData = Baritone.INSTANCE.getWorldProvider().getCurrentWorld(); + this.bsi = new BlockStateInterface(world, worldData); // TODO TODO TODO + // new CalculationContext() needs to happen, can't add an argument (i'll beat you), can we get the world provider from currentlyTicking? + this.toolSet = new ToolSet(player); this.hasThrowaway = Baritone.settings().allowPlace.get() && MovementHelper.throwaway(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.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.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.get(); this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.get(); + 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(); // 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. + this.worldBorder = new BetterWorldBorder(world.getWorldBorder()); + } + + public IBlockState get(int x, int y, int z) { + return bsi.get0(x, y, z); // laughs maniacally + } + + public boolean isLoaded(int x, int z) { + return bsi.isLoaded(x, z); + } + + public IBlockState get(BlockPos pos) { + return get(pos.getX(), pos.getY(), pos.getZ()); + } + + public Block getBlock(int x, int y, int z) { + return get(x, y, z).getBlock(); + } + + public boolean canPlaceThrowawayAt(int x, int y, int z) { + if (!hasThrowaway()) { // only true if allowPlace is true, see constructor + return false; + } + if (isPossiblyProtected(x, y, z)) { + return false; + } + return worldBorder.canPlaceAt(x, z); // TODO perhaps MovementHelper.canPlaceAgainst could also use this? + } + + public boolean canBreakAt(int x, int y, int z) { + if (!allowBreak()) { + return false; + } + return !isPossiblyProtected(x, y, z); + } + + public boolean isPossiblyProtected(int x, int y, int z) { + // TODO more protection logic here; see #220 + return false; + } + + public World world() { + return world; + } + + public EntityPlayerSP player() { + return player; + } + + public BlockStateInterface bsi() { + return bsi; + } + + public WorldData worldData() { + return worldData; } public ToolSet getToolSet() { - return this.toolSet; + return toolSet; } public boolean hasWaterBucket() { @@ -91,4 +171,11 @@ public class CalculationContext implements Helper { return maxFallHeightBucket; } + public double waterWalkSpeed() { + return waterWalkSpeed; + } + + public double breakBlockAdditionalCost() { + return breakBlockAdditionalCost; + } } diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 21341fa4..e492a795 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -18,25 +18,25 @@ package baritone.pathing.movement; import baritone.Baritone; -import baritone.api.utils.Rotation; -import baritone.behavior.LookBehavior; -import baritone.behavior.LookBehaviorUtils; -import baritone.pathing.movement.MovementState.MovementStatus; -import baritone.utils.*; -import baritone.utils.pathing.BetterBlockPos; +import baritone.api.pathing.movement.IMovement; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.*; +import baritone.utils.BlockStateInterface; +import baritone.utils.Helper; +import baritone.utils.InputOverrideHandler; import net.minecraft.block.BlockLiquid; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.chunk.EmptyChunk; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import static baritone.utils.InputOverrideHandler.Input; -public abstract class Movement implements Helper, MovementHelper { +public abstract class Movement implements IMovement, Helper, MovementHelper { protected static final EnumFacing[] HORIZONTALS = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST}; @@ -56,8 +56,6 @@ public abstract class Movement implements Helper, MovementHelper { */ protected final BetterBlockPos positionToPlace; - private boolean didBreakLastTick; - private Double cost; public List toBreakCached = null; @@ -77,24 +75,27 @@ public abstract class Movement implements Helper, MovementHelper { this(src, dest, toBreak, null); } - public double getCost(CalculationContext context) { + @Override + public double getCost() { if (cost == null) { - cost = calculateCost(context != null ? context : new CalculationContext()); + cost = calculateCost(new CalculationContext()); } return cost; } protected abstract double calculateCost(CalculationContext context); + @Override public double recalculateCost() { cost = null; - return getCost(null); + return getCost(); } - protected void override(double cost) { + public void override(double cost) { this.cost = cost; } + @Override public double calculateCostWithoutCaching() { return calculateCost(new CalculationContext()); } @@ -105,50 +106,34 @@ public abstract class Movement implements Helper, MovementHelper { * * @return Status */ + @Override public MovementStatus update() { - player().capabilities.allowFlying = false; - MovementState latestState = updateState(currentState); - if (BlockStateInterface.isLiquid(playerFeet())) { - latestState.setInput(Input.JUMP, true); + player().capabilities.isFlying = false; + currentState = updateState(currentState); + if (MovementHelper.isLiquid(playerFeet())) { + currentState.setInput(Input.JUMP, true); + } + if (player().isEntityInsideOpaqueBlock()) { + currentState.setInput(Input.CLICK_LEFT, true); } // If the movement target has to force the new rotations, or we aren't using silent move, then force the rotations - latestState.getTarget().getRotation().ifPresent(rotation -> - LookBehavior.INSTANCE.updateTarget( + currentState.getTarget().getRotation().ifPresent(rotation -> + Baritone.INSTANCE.getLookBehavior().updateTarget( rotation, - latestState.getTarget().hasToForceRotations())); + currentState.getTarget().hasToForceRotations())); // TODO: calculate movement inputs from latestState.getGoal().position // latestState.getTarget().position.ifPresent(null); NULL CONSUMER REALLY SHOULDN'T BE THE FINAL THING YOU SHOULD REALLY REPLACE THIS WITH ALMOST ACTUALLY ANYTHING ELSE JUST PLEASE DON'T LEAVE IT AS IT IS THANK YOU KANYE - this.didBreakLastTick = false; - - latestState.getInputStates().forEach((input, forced) -> { - if (Baritone.settings().leftClickWorkaround.get()) { - RayTraceResult trace = mc.objectMouseOver; - boolean isBlockTrace = trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK; - boolean isLeftClick = forced && input == Input.CLICK_LEFT; - - // If we're forcing left click, we're in a gui screen, and we're looking - // at a block, break the block without a direct game input manipulation. - if (mc.currentScreen != null && isLeftClick && isBlockTrace) { - BlockBreakHelper.tryBreakBlock(trace.getBlockPos(), trace.sideHit); - this.didBreakLastTick = true; - return; - } - } + currentState.getInputStates().forEach((input, forced) -> { Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(input, forced); }); - latestState.getInputStates().replaceAll((input, forced) -> false); + currentState.getInputStates().replaceAll((input, forced) -> false); - if (!this.didBreakLastTick) { - BlockBreakHelper.stopBreakingBlock(); - } - - currentState = latestState; - - if (isFinished()) { - onFinish(latestState); + // If the current status indicates a completed movement + if (currentState.getStatus().isComplete()) { + Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); } return currentState.getStatus(); @@ -162,19 +147,24 @@ public abstract class Movement implements Helper, MovementHelper { for (BetterBlockPos blockPos : positionsToBreak) { if (!MovementHelper.canWalkThrough(blockPos) && !(BlockStateInterface.getBlock(blockPos) instanceof BlockLiquid)) { // can't break liquid, so don't try somethingInTheWay = true; - Optional reachable = LookBehaviorUtils.reachable(blockPos); + Optional reachable = RotationUtils.reachable(player(), blockPos, playerController().getBlockReachDistance()); if (reachable.isPresent()) { MovementHelper.switchToBestToolFor(BlockStateInterface.get(blockPos)); - state.setTarget(new MovementState.MovementTarget(reachable.get(), true)).setInput(Input.CLICK_LEFT, true); + state.setTarget(new MovementState.MovementTarget(reachable.get(), true)); + if (Objects.equals(RayTraceUtils.getSelectedBlock().orElse(null), blockPos)) { + state.setInput(Input.CLICK_LEFT, true); + } return false; } //get rekt minecraft //i'm doing it anyway //i dont care if theres snow in the way!!!!!!! //you dont own me!!!! - state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(mc.player.getPositionEyes(1.0F), - Utils.getBlockPosCenter(blockPos)), true) - ).setInput(InputOverrideHandler.Input.CLICK_LEFT, true); + state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(player().getPositionEyes(1.0F), + VecUtils.getBlockPosCenter(blockPos)), 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(InputOverrideHandler.Input.CLICK_LEFT, true); return false; } } @@ -187,34 +177,26 @@ public abstract class Movement implements Helper, MovementHelper { return true; } - public boolean isFinished() { - return (currentState.getStatus() != MovementStatus.RUNNING - && currentState.getStatus() != MovementStatus.PREPPING - && currentState.getStatus() != MovementStatus.WAITING); + @Override + public boolean safeToCancel() { + return safeToCancel(currentState); } + protected boolean safeToCancel(MovementState currentState) { + return true; + } + + @Override public BetterBlockPos getSrc() { return src; } + @Override public BetterBlockPos getDest() { return dest; } - /** - * Run cleanup on state finish and declare success. - */ - public void onFinish(MovementState state) { - state.getInputStates().replaceAll((input, forced) -> false); - state.getInputStates().forEach((input, forced) -> Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(input, forced)); - } - - public void cancel() { - currentState.getInputStates().replaceAll((input, forced) -> false); - currentState.getInputStates().forEach((input, forced) -> Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(input, forced)); - currentState.setStatus(MovementStatus.CANCELED); - } - + @Override public void reset() { currentState = new MovementState().setStatus(MovementStatus.PREPPING); } @@ -239,18 +221,28 @@ public abstract class Movement implements Helper, MovementHelper { return state; } + @Override public BlockPos getDirection() { return getDest().subtract(getSrc()); } - public void checkLoadedChunk() { - calculatedWhileLoaded = !(world().getChunk(getDest()) instanceof EmptyChunk); + public void checkLoadedChunk(CalculationContext context) { + calculatedWhileLoaded = !(context.world().getChunk(getDest()) instanceof EmptyChunk); } + @Override public boolean calculatedWhileLoaded() { return calculatedWhileLoaded; } + @Override + public void resetBlockCache() { + toBreakCached = null; + toPlaceCached = null; + toWalkIntoCached = null; + } + + @Override public List toBreak() { if (toBreakCached != null) { return toBreakCached; @@ -265,6 +257,7 @@ public abstract class Movement implements Helper, MovementHelper { return result; } + @Override public List toPlace() { if (toPlaceCached != null) { return toPlaceCached; @@ -277,6 +270,7 @@ public abstract class Movement implements Helper, MovementHelper { return result; } + @Override public List toWalkInto() { // overridden by movementdiagonal if (toWalkIntoCached == null) { toWalkIntoCached = new ArrayList<>(); diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index d614e544..a7e9c378 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -19,27 +19,24 @@ package baritone.pathing.movement; import baritone.Baritone; import baritone.api.pathing.movement.ActionCosts; -import baritone.api.utils.Rotation; -import baritone.behavior.LookBehaviorUtils; +import baritone.api.utils.*; import baritone.pathing.movement.MovementState.MovementTarget; -import baritone.utils.*; -import baritone.utils.pathing.BetterBlockPos; +import baritone.utils.BlockStateInterface; +import baritone.utils.Helper; +import baritone.utils.InputOverrideHandler; +import baritone.utils.ToolSet; import net.minecraft.block.*; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.entity.Entity; 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.world.chunk.EmptyChunk; -import java.util.Optional; - /** * Static helpers for cost calculation * @@ -47,20 +44,16 @@ import java.util.Optional; */ public interface MovementHelper extends ActionCosts, Helper { - static boolean avoidBreaking(BetterBlockPos pos, IBlockState state) { - return avoidBreaking(pos.x, pos.y, pos.z, state); - } - - static boolean avoidBreaking(int x, int y, int z, IBlockState state) { + static boolean avoidBreaking(CalculationContext context, int x, int y, int z, IBlockState state) { Block b = state.getBlock(); return b == Blocks.ICE // ice becomes water, and water can mess up the path || b instanceof BlockSilverfish // obvious reasons - // call BlockStateInterface.get directly with x,y,z. no need to make 5 new BlockPos for no reason - || BlockStateInterface.get(x, y + 1, z).getBlock() instanceof BlockLiquid//don't break anything touching liquid on any side - || BlockStateInterface.get(x + 1, y, z).getBlock() instanceof BlockLiquid - || BlockStateInterface.get(x - 1, y, z).getBlock() instanceof BlockLiquid - || BlockStateInterface.get(x, y, z + 1).getBlock() instanceof BlockLiquid - || BlockStateInterface.get(x, y, z - 1).getBlock() instanceof BlockLiquid; + // call context.get directly with x,y,z. no need to make 5 new BlockPos for no reason + || context.get(x, y + 1, z).getBlock() instanceof BlockLiquid//don't break anything touching liquid on any side + || context.get(x + 1, y, z).getBlock() instanceof BlockLiquid + || context.get(x - 1, y, z).getBlock() instanceof BlockLiquid + || context.get(x, y, z + 1).getBlock() instanceof BlockLiquid + || context.get(x, y, z - 1).getBlock() instanceof BlockLiquid; } /** @@ -70,18 +63,14 @@ public interface MovementHelper extends ActionCosts, Helper { * @return */ static boolean canWalkThrough(BetterBlockPos pos) { - return canWalkThrough(pos.x, pos.y, pos.z, BlockStateInterface.get(pos)); + return canWalkThrough(new CalculationContext(), pos.x, pos.y, pos.z, BlockStateInterface.get(pos)); } - static boolean canWalkThrough(BetterBlockPos pos, IBlockState state) { - return canWalkThrough(pos.x, pos.y, pos.z, state); + static boolean canWalkThrough(CalculationContext context, int x, int y, int z) { + return canWalkThrough(context, x, y, z, context.get(x, y, z)); } - static boolean canWalkThrough(int x, int y, int z) { - return canWalkThrough(x, y, z, BlockStateInterface.get(x, y, z)); - } - - static boolean canWalkThrough(int x, int y, int z, IBlockState state) { + static boolean canWalkThrough(CalculationContext context, int x, int y, int z, IBlockState state) { Block block = state.getBlock(); if (block == Blocks.AIR) { // early return for most common case return true; @@ -106,21 +95,23 @@ public interface MovementHelper extends ActionCosts, Helper { return true; } if (snow) { - return state.getValue(BlockSnow.LAYERS) < 5; // see BlockSnow.isPassable + // 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 + return state.getValue(BlockSnow.LAYERS) < 3; } if (trapdoor) { return !state.getValue(BlockTrapDoor.OPEN); // see BlockTrapDoor.isPassable } throw new IllegalStateException(); } - if (BlockStateInterface.isFlowing(state)) { + if (isFlowing(state)) { return false; // Don't walk through flowing liquids } if (block instanceof BlockLiquid) { if (Baritone.settings().assumeWalkOnWater.get()) { return false; } - IBlockState up = BlockStateInterface.get(x, y + 1, z); + IBlockState up = context.get(x, y + 1, z); if (up.getBlock() instanceof BlockLiquid || up.getBlock() instanceof BlockLilyPad) { return false; } @@ -138,12 +129,8 @@ public interface MovementHelper extends ActionCosts, Helper { * * @return */ - static boolean fullyPassable(BlockPos pos) { - return fullyPassable(BlockStateInterface.get(pos)); - } - - static boolean fullyPassable(int x, int y, int z) { - return fullyPassable(BlockStateInterface.get(x, y, z)); + static boolean fullyPassable(CalculationContext context, int x, int y, int z) { + return fullyPassable(context.get(x, y, z)); } static boolean fullyPassable(IBlockState state) { @@ -169,10 +156,6 @@ public interface MovementHelper extends ActionCosts, Helper { return block.isPassable(null, null); } - static boolean isReplacable(BlockPos pos, IBlockState state) { - return isReplacable(pos.getX(), pos.getY(), pos.getZ(), state); - } - static boolean isReplacable(int x, int y, int z, IBlockState state) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case @@ -196,7 +179,7 @@ public interface MovementHelper extends ActionCosts, Helper { BlockDoublePlant.EnumPlantType kek = state.getValue(BlockDoublePlant.VARIANT); return kek == BlockDoublePlant.EnumPlantType.FERN || kek == BlockDoublePlant.EnumPlantType.GRASS; } - return state.getBlock().isReplaceable(null, null); + return state.getMaterial().isReplaceable(); } static boolean isDoorPassable(BlockPos doorPos, BlockPos playerPos) { @@ -242,7 +225,7 @@ public interface MovementHelper extends ActionCosts, Helper { return true; } - return facing == playerFacing == open; + return (facing == playerFacing) == open; } static boolean avoidWalkingInto(Block block) { @@ -262,7 +245,7 @@ public interface MovementHelper extends ActionCosts, Helper { * * @return */ - static boolean canWalkOn(int x, int y, int z, IBlockState state) { + static boolean canWalkOn(CalculationContext context, int x, int y, int z, IBlockState state) { Block block = state.getBlock(); if (block == Blocks.AIR || block == Blocks.MAGMA) { // early return for most common case (air) @@ -270,9 +253,6 @@ public interface MovementHelper extends ActionCosts, Helper { return false; } if (state.isBlockNormalCube()) { - if (BlockStateInterface.isLava(block) || BlockStateInterface.isWater(block)) { - throw new IllegalStateException(); - } return true; } if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.get())) { // TODO reconsider this @@ -284,20 +264,20 @@ public interface MovementHelper extends ActionCosts, Helper { if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST) { return true; } - if (BlockStateInterface.isWater(block)) { + if (isWater(block)) { // 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 = BlockStateInterface.get(x, y + 1, z).getBlock(); + Block up = context.get(x, y + 1, z).getBlock(); if (up == Blocks.WATERLILY) { return true; } - if (BlockStateInterface.isFlowing(state) || block == Blocks.FLOWING_WATER) { + if (isFlowing(state) || 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 BlockStateInterface.isWater(up) && !Baritone.settings().assumeWalkOnWater.get(); + return isWater(up) && !Baritone.settings().assumeWalkOnWater.get(); } // 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 BlockStateInterface.isWater(up) ^ Baritone.settings().assumeWalkOnWater.get(); + return isWater(up) ^ Baritone.settings().assumeWalkOnWater.get(); } if (block instanceof BlockGlass || block instanceof BlockStainedGlass) { return true; @@ -311,26 +291,23 @@ public interface MovementHelper extends ActionCosts, Helper { } return true; } - if (block instanceof BlockStairs) { - return true; - } - return false; + return block instanceof BlockStairs; } static boolean canWalkOn(BetterBlockPos pos, IBlockState state) { - return canWalkOn(pos.x, pos.y, pos.z, state); + return canWalkOn(new CalculationContext(), pos.x, pos.y, pos.z, state); } static boolean canWalkOn(BetterBlockPos pos) { - return canWalkOn(pos.x, pos.y, pos.z, BlockStateInterface.get(pos)); + return canWalkOn(new CalculationContext(), pos.x, pos.y, pos.z, BlockStateInterface.get(pos)); } - static boolean canWalkOn(int x, int y, int z) { - return canWalkOn(x, y, z, BlockStateInterface.get(x, y, z)); + static boolean canWalkOn(CalculationContext context, int x, int y, int z) { + return canWalkOn(context, x, y, z, context.get(x, y, z)); } - static boolean canPlaceAgainst(int x, int y, int z) { - return canPlaceAgainst(BlockStateInterface.get(x, y, z)); + static boolean canPlaceAgainst(CalculationContext context, int x, int y, int z) { + return canPlaceAgainst(context.get(x, y, z)); } static boolean canPlaceAgainst(BlockPos pos) { @@ -342,26 +319,17 @@ public interface MovementHelper extends ActionCosts, Helper { return state.isBlockNormalCube(); } - static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, boolean includeFalling) { - IBlockState state = BlockStateInterface.get(position); - return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); - } - - static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, IBlockState state, boolean includeFalling) { - return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); - } - static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { - return getMiningDurationTicks(context, x, y, z, BlockStateInterface.get(x, y, z), includeFalling); + return getMiningDurationTicks(context, x, y, z, context.get(x, y, z), includeFalling); } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); - if (!canWalkThrough(x, y, z, state)) { - if (!context.allowBreak()) { + if (!canWalkThrough(context, x, y, z, state)) { + if (!context.canBreakAt(x, y, z)) { return COST_INF; } - if (avoidBreaking(x, y, z, state)) { + if (avoidBreaking(context, x, y, z, state)) { return COST_INF; } if (block instanceof BlockLiquid) { @@ -374,8 +342,9 @@ public interface MovementHelper extends ActionCosts, Helper { } double result = m / strVsBlock; + result += context.breakBlockAdditionalCost(); if (includeFalling) { - IBlockState above = BlockStateInterface.get(x, y + 1, z); + IBlockState above = context.get(x, y + 1, z); if (above.getBlock() instanceof BlockFalling) { result += getMiningDurationTicks(context, x, y + 1, z, above, true); } @@ -391,27 +360,11 @@ public interface MovementHelper extends ActionCosts, Helper { && state.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM; } - static boolean isBottomSlab(BlockPos pos) { - return isBottomSlab(BlockStateInterface.get(pos)); - } - - /** - * The entity the player is currently looking at - * - * @return the entity object - */ - static Optional whatEntityAmILookingAt() { - if (mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == RayTraceResult.Type.ENTITY) { - return Optional.of(mc.objectMouseOver.entityHit); - } - return Optional.empty(); - } - /** * AutoTool */ static void switchToBestTool() { - LookBehaviorUtils.getSelectedBlock().ifPresent(pos -> { + RayTraceUtils.getSelectedBlock().ifPresent(pos -> { IBlockState state = BlockStateInterface.get(pos); if (state.getBlock().equals(Blocks.AIR)) { return; @@ -426,7 +379,7 @@ public interface MovementHelper extends ActionCosts, Helper { * @param b the blockstate to mine */ static void switchToBestToolFor(IBlockState b) { - switchToBestToolFor(b, new ToolSet()); + switchToBestToolFor(b, new ToolSet(Helper.HELPER.player())); } /** @@ -436,11 +389,11 @@ public interface MovementHelper extends ActionCosts, Helper { * @param ts previously calculated ToolSet */ static void switchToBestToolFor(IBlockState b, ToolSet ts) { - mc.player.inventory.currentItem = ts.getBestSlot(b); + Helper.HELPER.player().inventory.currentItem = ts.getBestSlot(b.getBlock()); } static boolean throwaway(boolean select) { - EntityPlayerSP p = Minecraft.getMinecraft().player; + EntityPlayerSP p = Helper.HELPER.player(); NonNullList inv = p.inventory.mainInventory; for (byte i = 0; i < 9; i++) { ItemStack item = inv.get(i); @@ -456,15 +409,75 @@ public interface MovementHelper extends ActionCosts, Helper { 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 moveTowards(MovementState state, BlockPos pos) { + EntityPlayerSP player = Helper.HELPER.player(); state.setTarget(new MovementTarget( - new Rotation(Utils.calcRotationFromVec3d(mc.player.getPositionEyes(1.0F), - Utils.getBlockPosCenter(pos), - new Rotation(mc.player.rotationYaw, mc.player.rotationPitch)).getYaw(), mc.player.rotationPitch), + new Rotation(RotationUtils.calcRotationFromVec3d(player.getPositionEyes(1.0F), + VecUtils.getBlockPosCenter(pos), + new Rotation(player.rotationYaw, player.rotationPitch)).getYaw(), player.rotationPitch), false )).setInput(InputOverrideHandler.Input.MOVE_FORWARD, true); } + + /** + * Returns whether or not the specified block is + * water, regardless of whether or not it is flowing. + * + * @param b The block + * @return Whether or not the block is water + */ + static boolean isWater(Block b) { + return b == Blocks.FLOWING_WATER || b == Blocks.WATER; + } + + /** + * Returns whether or not the block at the specified pos is + * water, regardless of whether or not it is flowing. + * + * @param bp The block pos + * @return Whether or not the block is water + */ + static boolean isWater(BlockPos bp) { + return isWater(BlockStateInterface.getBlock(bp)); + } + + static boolean isLava(Block b) { + return b == Blocks.FLOWING_LAVA || b == Blocks.LAVA; + } + + /** + * Returns whether or not the specified pos has a liquid + * + * @param p The pos + * @return Whether or not the block is a liquid + */ + static boolean isLiquid(BlockPos p) { + return BlockStateInterface.getBlock(p) instanceof BlockLiquid; + } + + static boolean isFlowing(IBlockState state) { + // Will be IFluidState in 1.13 + return state.getBlock() instanceof BlockLiquid + && state.getPropertyKeys().contains(BlockLiquid.LEVEL) + && state.getValue(BlockLiquid.LEVEL) != 0; + } } diff --git a/src/main/java/baritone/pathing/movement/MovementState.java b/src/main/java/baritone/pathing/movement/MovementState.java index 6d0262e6..8b1b0e0d 100644 --- a/src/main/java/baritone/pathing/movement/MovementState.java +++ b/src/main/java/baritone/pathing/movement/MovementState.java @@ -17,6 +17,7 @@ package baritone.pathing.movement; +import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.Rotation; import baritone.utils.InputOverrideHandler.Input; import net.minecraft.util.math.Vec3d; @@ -72,10 +73,6 @@ public class MovementState { return this.inputState; } - public enum MovementStatus { - PREPPING, WAITING, RUNNING, SUCCESS, UNREACHABLE, FAILED, CANCELED - } - public static class MovementTarget { /** @@ -87,9 +84,6 @@ public class MovementState { /** * Yaw and pitch angles that must be matched - *

- * getFirst() -> YAW - * getSecond() -> PITCH */ public Rotation rotation; diff --git a/src/main/java/baritone/pathing/movement/Moves.java b/src/main/java/baritone/pathing/movement/Moves.java index 6f49dfbd..340122b3 100644 --- a/src/main/java/baritone/pathing/movement/Moves.java +++ b/src/main/java/baritone/pathing/movement/Moves.java @@ -17,9 +17,9 @@ package baritone.pathing.movement; +import baritone.api.utils.BetterBlockPos; import baritone.pathing.movement.movements.*; -import baritone.utils.pathing.BetterBlockPos; -import baritone.utils.pathing.MoveResult; +import baritone.utils.pathing.MutableMoveResult; import net.minecraft.util.EnumFacing; /** @@ -28,306 +28,326 @@ import net.minecraft.util.EnumFacing; * @author leijurv */ public enum Moves { - DOWNWARD(0, 0) { + DOWNWARD(0, -1, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDownward(src, src.down()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y - 1, z, MovementDownward.cost(context, x, y, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementDownward.cost(context, x, y, z); } }, - PILLAR(0, 0) { + PILLAR(0, +1, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementPillar(src, src.up()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y + 1, z, MovementPillar.cost(context, x, y, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementPillar.cost(context, x, y, z); } }, - TRAVERSE_NORTH(0, -1) { + TRAVERSE_NORTH(0, 0, -1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(src, src.north()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y, z - 1, MovementTraverse.cost(context, x, y, z, x, z - 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementTraverse.cost(context, x, y, z, x, z - 1); } }, - TRAVERSE_SOUTH(0, +1) { + TRAVERSE_SOUTH(0, 0, +1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(src, src.south()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y, z + 1, MovementTraverse.cost(context, x, y, z, x, z + 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementTraverse.cost(context, x, y, z, x, z + 1); } }, - TRAVERSE_EAST(+1, 0) { + TRAVERSE_EAST(+1, 0, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(src, src.east()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x + 1, y, z, MovementTraverse.cost(context, x, y, z, x + 1, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementTraverse.cost(context, x, y, z, x + 1, z); } }, - TRAVERSE_WEST(-1, 0) { + TRAVERSE_WEST(-1, 0, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementTraverse(src, src.west()); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x - 1, y, z, MovementTraverse.cost(context, x, y, z, x - 1, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementTraverse.cost(context, x, y, z, x - 1, z); } }, - ASCEND_NORTH(0, -1) { + ASCEND_NORTH(0, +1, -1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z - 1)); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y + 1, z - 1, MovementAscend.cost(context, x, y, z, x, z - 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementAscend.cost(context, x, y, z, x, z - 1); } }, - ASCEND_SOUTH(0, +1) { + ASCEND_SOUTH(0, +1, +1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z + 1)); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x, y + 1, z + 1, MovementAscend.cost(context, x, y, z, x, z + 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementAscend.cost(context, x, y, z, x, z + 1); } }, - ASCEND_EAST(+1, 0) { + ASCEND_EAST(+1, +1, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(src, new BetterBlockPos(src.x + 1, src.y + 1, src.z)); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x + 1, y + 1, z, MovementAscend.cost(context, x, y, z, x + 1, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementAscend.cost(context, x, y, z, x + 1, z); } }, - ASCEND_WEST(-1, 0) { + ASCEND_WEST(-1, +1, 0) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementAscend(src, new BetterBlockPos(src.x - 1, src.y + 1, src.z)); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x - 1, y + 1, z, MovementAscend.cost(context, x, y, z, x - 1, z)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementAscend.cost(context, x, y, z, x - 1, z); } }, - DESCEND_EAST(+1, 0) { + DESCEND_EAST(+1, -1, 0, false, true) { @Override - public Movement apply0(BetterBlockPos src) { - MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); - if (res.destY == src.y - 1) { - return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + MutableMoveResult res = new MutableMoveResult(); + apply(context, src.x, src.y, src.z, res); + if (res.y == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.x, res.y, res.z)); } else { - return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + return new MovementFall(src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementDescend.cost(context, x, y, z, x + 1, z); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementDescend.cost(context, x, y, z, x + 1, z, result); } }, - DESCEND_WEST(-1, 0) { + DESCEND_WEST(-1, -1, 0, false, true) { @Override - public Movement apply0(BetterBlockPos src) { - MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); - if (res.destY == src.y - 1) { - return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + MutableMoveResult res = new MutableMoveResult(); + apply(context, src.x, src.y, src.z, res); + if (res.y == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.x, res.y, res.z)); } else { - return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + return new MovementFall(src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementDescend.cost(context, x, y, z, x - 1, z); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementDescend.cost(context, x, y, z, x - 1, z, result); } }, - DESCEND_NORTH(0, -1) { + DESCEND_NORTH(0, -1, -1, false, true) { @Override - public Movement apply0(BetterBlockPos src) { - MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); - if (res.destY == src.y - 1) { - return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + MutableMoveResult res = new MutableMoveResult(); + apply(context, src.x, src.y, src.z, res); + if (res.y == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.x, res.y, res.z)); } else { - return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + return new MovementFall(src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementDescend.cost(context, x, y, z, x, z - 1); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementDescend.cost(context, x, y, z, x, z - 1, result); } }, - DESCEND_SOUTH(0, +1) { + DESCEND_SOUTH(0, -1, +1, false, true) { @Override - public Movement apply0(BetterBlockPos src) { - MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); - if (res.destY == src.y - 1) { - return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + MutableMoveResult res = new MutableMoveResult(); + apply(context, src.x, src.y, src.z, res); + if (res.y == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.x, res.y, res.z)); } else { - return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + return new MovementFall(src, new BetterBlockPos(res.x, res.y, res.z)); } } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementDescend.cost(context, x, y, z, x, z + 1); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementDescend.cost(context, x, y, z, x, z + 1, result); } }, - DIAGONAL_NORTHEAST(+1, -1) { + DIAGONAL_NORTHEAST(+1, 0, -1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.EAST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x + 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x + 1, z - 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementDiagonal.cost(context, x, y, z, x + 1, z - 1); } }, - DIAGONAL_NORTHWEST(-1, -1) { + DIAGONAL_NORTHWEST(-1, 0, -1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.WEST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x - 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x - 1, z - 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementDiagonal.cost(context, x, y, z, x - 1, z - 1); } }, - DIAGONAL_SOUTHEAST(+1, +1) { + DIAGONAL_SOUTHEAST(+1, 0, +1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.EAST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x + 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x + 1, z + 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementDiagonal.cost(context, x, y, z, x + 1, z + 1); } }, - DIAGONAL_SOUTHWEST(-1, +1) { + DIAGONAL_SOUTHWEST(-1, 0, +1) { @Override - public Movement apply0(BetterBlockPos src) { + public Movement apply0(CalculationContext context, BetterBlockPos src) { return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.WEST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return new MoveResult(x - 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x - 1, z + 1)); + public double cost(CalculationContext context, int x, int y, int z) { + return MovementDiagonal.cost(context, x, y, z, x - 1, z + 1); } }, - PARKOUR_NORTH(0, -4, true) { + PARKOUR_NORTH(0, 0, -4, true, false) { @Override - public Movement apply0(BetterBlockPos src) { - return MovementParkour.cost(new CalculationContext(), src, EnumFacing.NORTH); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + return MovementParkour.cost(context, src, EnumFacing.NORTH); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementParkour.cost(context, x, y, z, EnumFacing.NORTH); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementParkour.cost(context, x, y, z, EnumFacing.NORTH, result); } }, - PARKOUR_SOUTH(0, +4, true) { + PARKOUR_SOUTH(0, 0, +4, true, false) { @Override - public Movement apply0(BetterBlockPos src) { - return MovementParkour.cost(new CalculationContext(), src, EnumFacing.SOUTH); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + return MovementParkour.cost(context, src, EnumFacing.SOUTH); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementParkour.cost(context, x, y, z, EnumFacing.SOUTH); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementParkour.cost(context, x, y, z, EnumFacing.SOUTH, result); } }, - PARKOUR_EAST(+4, 0, true) { + PARKOUR_EAST(+4, 0, 0, true, false) { @Override - public Movement apply0(BetterBlockPos src) { - return MovementParkour.cost(new CalculationContext(), src, EnumFacing.EAST); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + return MovementParkour.cost(context, src, EnumFacing.EAST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementParkour.cost(context, x, y, z, EnumFacing.EAST); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementParkour.cost(context, x, y, z, EnumFacing.EAST, result); } }, - PARKOUR_WEST(-4, 0, true) { + PARKOUR_WEST(-4, 0, 0, true, false) { @Override - public Movement apply0(BetterBlockPos src) { - return MovementParkour.cost(new CalculationContext(), src, EnumFacing.WEST); + public Movement apply0(CalculationContext context, BetterBlockPos src) { + return MovementParkour.cost(context, src, EnumFacing.WEST); } @Override - public MoveResult apply(CalculationContext context, int x, int y, int z) { - return MovementParkour.cost(context, x, y, z, EnumFacing.WEST); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + MovementParkour.cost(context, x, y, z, EnumFacing.WEST, result); } }; public final boolean dynamicXZ; + public final boolean dynamicY; public final int xOffset; + public final int yOffset; public final int zOffset; - Moves(int x, int z, boolean dynamicXZ) { + Moves(int x, int y, int z, boolean dynamicXZ, boolean dynamicY) { this.xOffset = x; + this.yOffset = y; this.zOffset = z; this.dynamicXZ = dynamicXZ; + this.dynamicY = dynamicY; } - Moves(int x, int z) { - this(x, z, false); + Moves(int x, int y, int z) { + this(x, y, z, false, false); } - public abstract Movement apply0(BetterBlockPos src); + public abstract Movement apply0(CalculationContext context, BetterBlockPos src); - public abstract MoveResult apply(CalculationContext context, int x, int y, int z); + public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) { + if (dynamicXZ || dynamicY) { + throw new UnsupportedOperationException(); + } + result.x = x + xOffset; + result.y = y + yOffset; + result.z = z + zOffset; + result.cost = cost(context, x, y, z); + } + + public double cost(CalculationContext context, int x, int y, int z) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 6d313c48..1d161554 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -18,16 +18,16 @@ package baritone.pathing.movement.movements; import baritone.Baritone; -import baritone.behavior.LookBehaviorUtils; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.RayTraceUtils; +import baritone.api.utils.RotationUtils; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; -import baritone.pathing.movement.MovementState.MovementStatus; import baritone.utils.BlockStateInterface; import baritone.utils.InputOverrideHandler; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -58,24 +58,24 @@ public class MovementAscend extends Movement { } public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { - IBlockState srcDown = BlockStateInterface.get(x, y - 1, z); + IBlockState srcDown = context.get(x, y - 1, z); if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) { return COST_INF; } // we can jump from soul sand, but not from a bottom slab boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown); - IBlockState toPlace = BlockStateInterface.get(destX, y, destZ); + IBlockState toPlace = context.get(destX, y, destZ); boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace); if (jumpingFromBottomSlab && !jumpingToBottomSlab) { return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab } boolean hasToPlace = false; - if (!MovementHelper.canWalkOn(destX, y, z, toPlace)) { - if (!context.hasThrowaway()) { + if (!MovementHelper.canWalkOn(context, destX, y, destZ, toPlace)) { + if (!context.canPlaceThrowawayAt(destX, y, destZ)) { return COST_INF; } - if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y, destZ, toPlace)) { + if (toPlace.getBlock() != Blocks.AIR && !MovementHelper.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y, destZ, toPlace)) { return COST_INF; } // TODO: add ability to place against .down() as well as the cardinal directions @@ -87,7 +87,7 @@ public class MovementAscend extends Movement { if (againstX == x && againstZ == z) { continue; } - if (MovementHelper.canPlaceAgainst(againstX, y, againstZ)) { + if (MovementHelper.canPlaceAgainst(context, againstX, y, againstZ)) { hasToPlace = true; break; } @@ -97,20 +97,19 @@ public class MovementAscend extends Movement { } } IBlockState srcUp2 = null; - if (BlockStateInterface.get(x, y + 3, z).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us + if (context.get(x, y + 3, z).getBlock() instanceof BlockFalling && (MovementHelper.canWalkThrough(context, x, y + 1, z) || !((srcUp2 = context.get(x, y + 2, z)).getBlock() instanceof BlockFalling))) {//it would fall on us and possibly suffocate us // HOWEVER, we assume that we're standing in the start position // that means that src and src.up(1) are both air // maybe they aren't now, but they will be by the time this starts - if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !((srcUp2 = BlockStateInterface.get(x, y + 2, z)).getBlock() instanceof BlockFalling)) { - // if both of those are BlockFalling, that means that by standing on src - // (the presupposition of this Movement) - // we have necessarily already cleared the entire BlockFalling stack - // on top of our head + // if the lower one is can't walk through and the upper one is falling, that means that by standing on src + // (the presupposition of this Movement) + // we have necessarily already cleared the entire BlockFalling stack + // on top of our head - // but if either of them aren't BlockFalling, that means we're still in suffocation danger - // so don't do it - return COST_INF; - } + // as in, if we have a block, then two BlockFallings on top of it + // and that block is x, y+1, z, and we'd have to clear it to even start this movement + // we don't need to worry about those BlockFallings because we've already cleared them + return COST_INF; // you may think we only need to check srcUp2, not srcUp // however, in the scenario where glitchy world gen where unsupported sand / gravel generates // it's possible srcUp is AIR from the start, and srcUp2 is falling @@ -139,7 +138,7 @@ public class MovementAscend extends Movement { totalCost += context.placeBlockCost(); } if (srcUp2 == null) { - srcUp2 = BlockStateInterface.get(x, y + 2, z); + srcUp2 = context.get(x, y + 2, z); } totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above if (totalCost >= COST_INF) { @@ -180,10 +179,10 @@ public class MovementAscend extends Movement { 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(Utils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); + state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); EnumFacing side = Minecraft.getMinecraft().objectMouseOver.sideHit; - LookBehaviorUtils.getSelectedBlock().ifPresent(selectedBlock -> { + RayTraceUtils.getSelectedBlock().ifPresent(selectedBlock -> { if (Objects.equals(selectedBlock, anAgainst) && selectedBlock.offset(side).equals(positionToPlace)) { ticksWithoutPlacement++; state.setInput(InputOverrideHandler.Input.SNEAK, true); @@ -197,7 +196,7 @@ public class MovementAscend extends Movement { } else { state.setInput(InputOverrideHandler.Input.CLICK_LEFT, true); // break whatever replaceable block is in the way } - System.out.println("Trying to look at " + anAgainst + ", actually looking at" + selectedBlock); + //System.out.println("Trying to look at " + anAgainst + ", actually looking at" + selectedBlock); }); return state; } @@ -205,10 +204,8 @@ public class MovementAscend extends Movement { return state.setStatus(MovementStatus.UNREACHABLE); } MovementHelper.moveTowards(state, dest); - if (MovementHelper.isBottomSlab(jumpingOnto)) { - if (!MovementHelper.isBottomSlab(src.down())) { - return state; // don't jump while walking from a non double slab into a bottom slab - } + if (MovementHelper.isBottomSlab(jumpingOnto) && !MovementHelper.isBottomSlab(BlockStateInterface.get(src.down()))) { + return state; // don't jump while walking from a non double slab into a bottom slab } if (Baritone.settings().assumeStep.get()) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 79d5a305..84f0d781 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -18,23 +18,20 @@ package baritone.pathing.movement.movements; import baritone.Baritone; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; -import baritone.pathing.movement.MovementState.MovementStatus; -import baritone.utils.BlockStateInterface; import baritone.utils.InputOverrideHandler; -import baritone.utils.pathing.BetterBlockPos; -import baritone.utils.pathing.MoveResult; +import baritone.utils.pathing.MutableMoveResult; import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; -import static baritone.utils.pathing.MoveResult.IMPOSSIBLE; - public class MovementDescend extends Movement { private int numTicks = 0; @@ -51,32 +48,33 @@ public class MovementDescend extends Movement { @Override protected double calculateCost(CalculationContext context) { - MoveResult result = cost(context, src.x, src.y, src.z, dest.x, dest.z); - if (result.destY != dest.y) { + MutableMoveResult result = new MutableMoveResult(); + cost(context, src.x, src.y, src.z, dest.x, dest.z, result); + if (result.y != dest.y) { return COST_INF; // doesn't apply to us, this position is a fall not a descend } return result.cost; } - public static MoveResult cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { - Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); + public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { + Block fromDown = context.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { - return IMPOSSIBLE; + return; } double totalCost = 0; - IBlockState destDown = BlockStateInterface.get(destX, y - 1, destZ); + IBlockState destDown = context.get(destX, y - 1, destZ); totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, destDown, false); if (totalCost >= COST_INF) { - return IMPOSSIBLE; + return; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); if (totalCost >= COST_INF) { - return IMPOSSIBLE; + return; } totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above if (totalCost >= COST_INF) { - return IMPOSSIBLE; + return; } // A @@ -89,13 +87,14 @@ public class MovementDescend extends Movement { //A is plausibly breakable by either descend or fall //C, D, etc determine the length of the fall - IBlockState below = BlockStateInterface.get(destX, y - 2, destZ); - if (!MovementHelper.canWalkOn(destX, y - 2, destZ, below)) { - return dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below); + IBlockState below = context.get(destX, y - 2, destZ); + if (!MovementHelper.canWalkOn(context, destX, y - 2, destZ, below)) { + dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res); + return; } if (destDown.getBlock() == Blocks.LADDER || destDown.getBlock() == Blocks.VINE) { - return IMPOSSIBLE; + return; } // we walk half the block plus 0.3 to get to the edge, then we walk the other 0.2 while simultaneously falling (math.max because of how it's in parallel) @@ -105,55 +104,72 @@ public class MovementDescend extends Movement { walk = WALK_ONE_OVER_SOUL_SAND_COST; } totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); - return new MoveResult(destX, y - 1, destZ, totalCost); + res.x = destX; + res.y = y - 1; + res.z = destZ; + res.cost = totalCost; } - public static MoveResult dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below) { - if (frontBreak != 0 && BlockStateInterface.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) { + public static void dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below, MutableMoveResult res) { + if (frontBreak != 0 && context.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) { // if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall // but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column, // and potentially replace the water we're going to fall into - return IMPOSSIBLE; + return; } - if (!MovementHelper.canWalkThrough(destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) { - return IMPOSSIBLE; + if (!MovementHelper.canWalkThrough(context, destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) { + return; } for (int fallHeight = 3; true; fallHeight++) { int newY = y - fallHeight; if (newY < 0) { // when pathing in the end, where you could plausibly fall into the void // this check prevents it from getting the block at y=-1 and crashing - return IMPOSSIBLE; + return; } - IBlockState ontoBlock = BlockStateInterface.get(destX, newY, destZ); + IBlockState ontoBlock = context.get(destX, newY, destZ); double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[fallHeight] + frontBreak; - if (ontoBlock.getBlock() == Blocks.WATER && !BlockStateInterface.isFlowing(ontoBlock)) { // TODO flowing check required here? + if (ontoBlock.getBlock() == Blocks.WATER && !MovementHelper.isFlowing(ontoBlock) && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) { // TODO flowing check required here? + // 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 (Baritone.settings().assumeWalkOnWater.get()) { - return IMPOSSIBLE; // TODO fix + return; // TODO fix } // found a fall into water - return new MoveResult(destX, newY, destZ, tentativeCost); // TODO incorporate water swim up cost? + res.x = destX; + res.y = newY; + res.z = destZ; + res.cost = tentativeCost;// TODO incorporate water swim up cost? + return; } if (ontoBlock.getBlock() == Blocks.FLOWING_WATER) { - return IMPOSSIBLE; + return; } - if (MovementHelper.canWalkThrough(destX, newY, destZ, ontoBlock)) { + if (MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { continue; } - if (!MovementHelper.canWalkOn(destX, newY, destZ, ontoBlock)) { - return IMPOSSIBLE; + if (!MovementHelper.canWalkOn(context, destX, newY, destZ, ontoBlock)) { + return; } if (MovementHelper.isBottomSlab(ontoBlock)) { - return IMPOSSIBLE; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect + return; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect } if (context.hasWaterBucket() && fallHeight <= context.maxFallHeightBucket() + 1) { - return new MoveResult(destX, newY + 1, destZ, tentativeCost + context.placeBlockCost()); // this is the block we're falling onto, so dest is +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; } if (fallHeight <= context.maxFallHeightNoWater() + 1) { // fallHeight = 4 means onto.up() is 3 blocks down, which is the max - return new MoveResult(destX, newY + 1, destZ, tentativeCost); + res.x = destX; + res.y = newY + 1; + res.z = destZ; + res.cost = tentativeCost; + return; } else { - return IMPOSSIBLE; + return; } } } @@ -166,13 +182,12 @@ public class MovementDescend extends Movement { } BlockPos playerFeet = playerFeet(); - if (playerFeet.equals(dest)) { - if (BlockStateInterface.isLiquid(dest) || player().posY - playerFeet.getY() < 0.094) { // 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 { - System.out.println(player().posY + " " + playerFeet.getY() + " " + (player().posY - playerFeet.getY())); - } + if (playerFeet.equals(dest) && (MovementHelper.isLiquid(dest) || player().posY - playerFeet.getY() < 0.094)) { // 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 { + // System.out.println(player().posY + " " + playerFeet.getY() + " " + (player().posY - playerFeet.getY())); + }*/ } double diffX = player().posX - (dest.getX() + 0.5); double diffZ = player().posZ - (dest.getZ() + 0.5); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index d75515d3..8e5dd910 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -17,13 +17,13 @@ package baritone.pathing.movement.movements; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; 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 baritone.utils.InputOverrideHandler; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; @@ -56,16 +56,16 @@ public class MovementDiagonal extends Movement { } public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { - Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); + Block fromDown = context.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { return COST_INF; } - IBlockState destInto = BlockStateInterface.get(destX, y, destZ); - if (!MovementHelper.canWalkThrough(destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(destX, y + 1, destZ)) { + IBlockState destInto = context.get(destX, y, destZ); + if (!MovementHelper.canWalkThrough(context, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context, destX, y + 1, destZ)) { return COST_INF; } - IBlockState destWalkOn = BlockStateInterface.get(destX, y - 1, destZ); - if (!MovementHelper.canWalkOn(destX, y - 1, destZ, destWalkOn)) { + IBlockState destWalkOn = context.get(destX, y - 1, destZ); + if (!MovementHelper.canWalkOn(context, destX, y - 1, destZ, destWalkOn)) { return COST_INF; } double multiplier = WALK_ONE_BLOCK_COST; @@ -76,18 +76,16 @@ public class MovementDiagonal extends Movement { if (fromDown == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } - Block cuttingOver1 = BlockStateInterface.get(x, y - 1, destZ).getBlock(); - if (cuttingOver1 == Blocks.MAGMA || BlockStateInterface.isLava(cuttingOver1)) { + Block cuttingOver1 = context.get(x, y - 1, destZ).getBlock(); + if (cuttingOver1 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver1)) { return COST_INF; } - Block cuttingOver2 = BlockStateInterface.get(destX, y - 1, z).getBlock(); - if (cuttingOver2 == Blocks.MAGMA || BlockStateInterface.isLava(cuttingOver2)) { + Block cuttingOver2 = context.get(destX, y - 1, z).getBlock(); + if (cuttingOver2 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver2)) { return COST_INF; } - IBlockState pb0 = BlockStateInterface.get(x, y, destZ); - IBlockState pb1 = BlockStateInterface.get(x, y + 1, destZ); - IBlockState pb2 = BlockStateInterface.get(destX, y, z); - IBlockState pb3 = BlockStateInterface.get(destX, y + 1, z); + IBlockState pb0 = context.get(x, y, destZ); + IBlockState pb2 = context.get(destX, y, z); 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) { @@ -95,41 +93,42 @@ public class MovementDiagonal extends Movement { // so no need to check pb1 as well, might as well return early here return COST_INF; } + IBlockState pb1 = context.get(x, y + 1, destZ); optionA += MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); if (optionA != 0 && optionB != 0) { // same deal, if pb1 makes optionA nonzero and option B already was nonzero, pb3 can't affect the result return COST_INF; } - if (optionA == 0) { + 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))) { // at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction - if (MovementHelper.avoidWalkingInto(pb2.getBlock()) || MovementHelper.avoidWalkingInto(pb3.getBlock())) { - return COST_INF; - } + return COST_INF; } optionB += MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); if (optionA != 0 && optionB != 0) { // and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible return COST_INF; } - if (optionB == 0) { + if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0.getBlock()) && pb0.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb1.getBlock()) && pb1.getBlock() != Blocks.WATER))) { // and now that option B is fully calculated, see if we can edge around that way - if (MovementHelper.avoidWalkingInto(pb0.getBlock()) || MovementHelper.avoidWalkingInto(pb1.getBlock())) { - return COST_INF; - } + return COST_INF; } - if (BlockStateInterface.isWater(BlockStateInterface.getBlock(x, y, z)) || BlockStateInterface.isWater(destInto.getBlock())) { + boolean water = false; + if (MovementHelper.isWater(context.getBlock(x, y, z)) || 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 = WALK_ONE_IN_WATER_COST; + multiplier = context.waterWalkSpeed(); + water = true; } if (optionA != 0 || optionB != 0) { multiplier *= SQRT_2 - 0.001; // TODO tune } - if (multiplier == WALK_ONE_BLOCK_COST && context.canSprint()) { - // If we aren't edging around anything, and we aren't in water or soul sand + if (context.canSprint() && !water) { + // If we aren't edging around anything, and we aren't in water // We can sprint =D - multiplier = SPRINT_ONE_BLOCK_COST; + // Don't check for soul sand, since we can sprint on that too + multiplier *= SPRINT_MULTIPLIER; } return multiplier * SQRT_2; } @@ -137,15 +136,15 @@ public class MovementDiagonal extends Movement { @Override public MovementState updateState(MovementState state) { super.updateState(state); - if (state.getStatus() != MovementState.MovementStatus.RUNNING) { + if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (playerFeet().equals(dest)) { - state.setStatus(MovementState.MovementStatus.SUCCESS); + state.setStatus(MovementStatus.SUCCESS); return state; } - if (!BlockStateInterface.isLiquid(playerFeet())) { + if (!MovementHelper.isLiquid(playerFeet())) { state.setInput(InputOverrideHandler.Input.SPRINT, true); } MovementHelper.moveTowards(state, dest); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index 148dc3b3..07c101e6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -17,12 +17,12 @@ package baritone.pathing.movement.movements; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; 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 baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; @@ -47,10 +47,10 @@ public class MovementDownward extends Movement { } public static double cost(CalculationContext context, int x, int y, int z) { - if (!MovementHelper.canWalkOn(x, y - 2, z)) { + if (!MovementHelper.canWalkOn(context, x, y - 2, z)) { return COST_INF; } - IBlockState d = BlockStateInterface.get(x, y - 1, z); + IBlockState d = context.get(x, y - 1, z); Block td = d.getBlock(); boolean ladder = td == Blocks.LADDER || td == Blocks.VINE; if (ladder) { @@ -64,12 +64,12 @@ public class MovementDownward extends Movement { @Override public MovementState updateState(MovementState state) { super.updateState(state); - if (state.getStatus() != MovementState.MovementStatus.RUNNING) { + if (state.getStatus() != MovementStatus.RUNNING) { return state; } if (playerFeet().equals(dest)) { - return state.setStatus(MovementState.MovementStatus.SUCCESS); + return state.setStatus(MovementStatus.SUCCESS); } double diffX = player().posX - (dest.getX() + 0.5); double diffZ = 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 0f165f6f..26018d3b 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -18,19 +18,18 @@ package baritone.pathing.movement.movements; import baritone.Baritone; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; +import baritone.api.utils.RotationUtils; +import baritone.api.utils.VecUtils; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; -import baritone.pathing.movement.MovementState.MovementStatus; import baritone.pathing.movement.MovementState.MovementTarget; -import baritone.utils.BlockStateInterface; import baritone.utils.InputOverrideHandler; -import baritone.utils.RayTraceUtils; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; -import baritone.utils.pathing.MoveResult; +import baritone.utils.pathing.MutableMoveResult; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -49,8 +48,9 @@ public class MovementFall extends Movement { @Override protected double calculateCost(CalculationContext context) { - MoveResult result = MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z); - if (result.destY != dest.y) { + MutableMoveResult result = new MutableMoveResult(); + MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z, result); + if (result.y != dest.y) { return COST_INF; // doesn't apply to us, this position is a descend not a fall } return result.cost; @@ -64,19 +64,20 @@ public class MovementFall extends Movement { } BlockPos playerFeet = playerFeet(); + Rotation toDest = RotationUtils.calcRotationFromVec3d(playerHead(), VecUtils.getBlockPosCenter(dest)); Rotation targetRotation = null; - if (!BlockStateInterface.isWater(dest) && src.getY() - dest.getY() > Baritone.settings().maxFallHeightNoWater.get() && !playerFeet.equals(dest)) { + if (!MovementHelper.isWater(dest) && src.getY() - dest.getY() > Baritone.settings().maxFallHeightNoWater.get() && !playerFeet.equals(dest)) { if (!InventoryPlayer.isHotbar(player().inventory.getSlotFor(STACK_BUCKET_WATER)) || world().provider.isNether()) { return state.setStatus(MovementStatus.UNREACHABLE); } - if (player().posY - dest.getY() < mc.playerController.getBlockReachDistance()) { + if (player().posY - dest.getY() < playerController().getBlockReachDistance() && !player().onGround) { player().inventory.currentItem = player().inventory.getSlotFor(STACK_BUCKET_WATER); - targetRotation = new Rotation(player().rotationYaw, 90.0F); + targetRotation = new Rotation(toDest.getYaw(), 90.0F); - RayTraceResult trace = RayTraceUtils.simulateRayTrace(player().rotationYaw, 90.0F); - if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK) { + RayTraceResult trace = mc.objectMouseOver; + if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && player().rotationPitch > 89.0F) { state.setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } } @@ -84,10 +85,10 @@ public class MovementFall extends Movement { if (targetRotation != null) { state.setTarget(new MovementTarget(targetRotation, true)); } else { - state.setTarget(new MovementTarget(Utils.calcRotationFromVec3d(playerHead(), Utils.getBlockPosCenter(dest)), false)); + state.setTarget(new MovementTarget(toDest, false)); } - if (playerFeet.equals(dest) && (player().posY - playerFeet.getY() < 0.094 || BlockStateInterface.isWater(dest))) { // 0.094 because lilypads - if (BlockStateInterface.isWater(dest)) { + if (playerFeet.equals(dest) && (player().posY - playerFeet.getY() < 0.094 || MovementHelper.isWater(dest))) { // 0.094 because lilypads + if (MovementHelper.isWater(dest)) { if (InventoryPlayer.isHotbar(player().inventory.getSlotFor(STACK_BUCKET_EMPTY))) { player().inventory.currentItem = player().inventory.getSlotFor(STACK_BUCKET_EMPTY); if (player().motionY >= 0) { @@ -104,13 +105,20 @@ public class MovementFall extends Movement { return state.setStatus(MovementStatus.SUCCESS); } } - Vec3d destCenter = Utils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder) - if (Math.abs(player().posX - destCenter.x) > 0.2 || Math.abs(player().posZ - destCenter.z) > 0.2) { + Vec3d destCenter = VecUtils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder) + if (Math.abs(player().posX - destCenter.x) > 0.15 || Math.abs(player().posZ - destCenter.z) > 0.15) { state.setInput(InputOverrideHandler.Input.MOVE_FORWARD, true); } return state; } + @Override + public boolean safeToCancel(MovementState state) { + // if we haven't started walking off the edge yet, or if we're in the process of breaking blocks before doing the fall + // then it's safe to cancel this + return playerFeet().equals(src) || state.getStatus() != MovementStatus.RUNNING; + } + private static BetterBlockPos[] buildPositionsToBreak(BetterBlockPos src, BetterBlockPos dest) { BetterBlockPos[] toBreak; int diffX = src.getX() - dest.getX(); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index 43391b01..0b0e0e75 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -18,27 +18,30 @@ package baritone.pathing.movement.movements; import baritone.Baritone; -import baritone.behavior.LookBehaviorUtils; +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.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; +import baritone.utils.Helper; import baritone.utils.InputOverrideHandler; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; -import baritone.utils.pathing.MoveResult; +import baritone.utils.pathing.MutableMoveResult; +import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; 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.Objects; -import static baritone.utils.pathing.MoveResult.IMPOSSIBLE; - public class MovementParkour extends Movement { private static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; @@ -48,72 +51,91 @@ public class MovementParkour extends Movement { private final int dist; private MovementParkour(BetterBlockPos src, int dist, EnumFacing dir) { - super(src, src.offset(dir, dist), EMPTY); + super(src, src.offset(dir, dist), EMPTY, src.offset(dir, dist).down()); this.direction = dir; this.dist = dist; } public static MovementParkour cost(CalculationContext context, BetterBlockPos src, EnumFacing direction) { - MoveResult res = cost(context, src.x, src.y, src.z, direction); - int dist = Math.abs(res.destX - src.x) + Math.abs(res.destZ - src.z); + 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(src, dist, direction); } - public static MoveResult cost(CalculationContext context, int x, int y, int z, EnumFacing dir) { + public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) { if (!Baritone.settings().allowParkour.get()) { - return IMPOSSIBLE; + return; } - IBlockState standingOn = BlockStateInterface.get(x, y - 1, z); + IBlockState standingOn = context.get(x, y - 1, z); if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || MovementHelper.isBottomSlab(standingOn)) { - return IMPOSSIBLE; + return; } int xDiff = dir.getXOffset(); int zDiff = dir.getZOffset(); - IBlockState adj = BlockStateInterface.get(x + xDiff, y - 1, z + zDiff); + IBlockState adj = context.get(x + xDiff, y - 1, z + zDiff); if (MovementHelper.avoidWalkingInto(adj.getBlock()) && adj.getBlock() != Blocks.WATER && adj.getBlock() != Blocks.FLOWING_WATER) { // magma sucks - return IMPOSSIBLE; + return; } - if (MovementHelper.canWalkOn(x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now) - return IMPOSSIBLE; + if (MovementHelper.canWalkOn(context, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now) + return; } - if (!MovementHelper.fullyPassable(x + xDiff, y, z + zDiff)) { - return IMPOSSIBLE; + if (!MovementHelper.fullyPassable(context, x + xDiff, y, z + zDiff)) { + return; } - if (!MovementHelper.fullyPassable(x + xDiff, y + 1, z + zDiff)) { - return IMPOSSIBLE; + if (!MovementHelper.fullyPassable(context, x + xDiff, y + 1, z + zDiff)) { + return; } - if (!MovementHelper.fullyPassable(x + xDiff, y + 2, z + zDiff)) { - return IMPOSSIBLE; + if (!MovementHelper.fullyPassable(context, x + xDiff, y + 2, z + zDiff)) { + return; } - if (!MovementHelper.fullyPassable(x, y + 2, z)) { - return IMPOSSIBLE; + if (!MovementHelper.fullyPassable(context, x, y + 2, z)) { + return; } - for (int i = 2; i <= (context.canSprint() ? 4 : 3); i++) { + int maxJump; + if (standingOn.getBlock() == Blocks.SOUL_SAND) { + maxJump = 2; // 1 block gap + } else { + if (context.canSprint()) { + maxJump = 4; + } else { + maxJump = 3; + } + } + 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(x + xDiff * i, y + y2, z + zDiff * i)) { - return IMPOSSIBLE; + if (!MovementHelper.fullyPassable(context, x + xDiff * i, y + y2, z + zDiff * i)) { + return; } } - if (MovementHelper.canWalkOn(x + xDiff * i, y - 1, z + zDiff * i)) { - return new MoveResult(x + xDiff * i, y, z + zDiff * i, costFromJumpDistance(i)); + if (MovementHelper.canWalkOn(context, 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); + return; } } - if (!context.canSprint()) { - return IMPOSSIBLE; + if (maxJump != 4) { + return; } if (!Baritone.settings().allowParkourPlace.get()) { - return IMPOSSIBLE; + return; + } + if (!Baritone.settings().allowPlace.get()) { + Helper.HELPER.logDirect("allowParkourPlace enabled but allowPlace disabled?"); + return; } int destX = x + 4 * xDiff; int destZ = z + 4 * zDiff; - IBlockState toPlace = BlockStateInterface.get(destX, y - 1, destZ); - if (!context.hasThrowaway()) { - return IMPOSSIBLE; + IBlockState toPlace = context.get(destX, y - 1, destZ); + if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { + return; } - if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y - 1, destZ, toPlace)) { - return IMPOSSIBLE; + if (toPlace.getBlock() != Blocks.AIR && !MovementHelper.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y - 1, destZ, toPlace)) { + return; } for (int i = 0; i < 5; i++) { int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); @@ -121,11 +143,14 @@ public class MovementParkour extends Movement { if (againstX == x + xDiff * 3 && againstZ == z + zDiff * 3) { // we can't turn around that fast continue; } - if (MovementHelper.canPlaceAgainst(againstX, y - 1, againstZ)) { - return new MoveResult(destX, y, destZ, costFromJumpDistance(i) + context.placeBlockCost()); + if (MovementHelper.canPlaceAgainst(context, againstX, y - 1, againstZ)) { + res.x = destX; + res.y = y; + res.z = destZ; + res.cost = costFromJumpDistance(4) + context.placeBlockCost(); + return; } } - return IMPOSSIBLE; } private static double costFromJumpDistance(int dist) { @@ -137,24 +162,37 @@ public class MovementParkour extends Movement { case 4: return SPRINT_ONE_BLOCK_COST * 4; default: - throw new IllegalStateException("LOL"); + throw new IllegalStateException("LOL " + dist); } } @Override protected double calculateCost(CalculationContext context) { - MoveResult res = cost(context, src.x, src.y, src.z, direction); - if (res.destX != dest.x || res.destZ != dest.z) { + MutableMoveResult res = new MutableMoveResult(); + cost(context, src.x, src.y, src.z, direction, res); + if (res.x != dest.x || res.z != dest.z) { return COST_INF; } return res.cost; } + @Override + public boolean safeToCancel(MovementState state) { + // once this movement is instantiated, the state is default to PREPPING + // but once it's ticked for the first time it changes to RUNNING + // since we don't really know anything about momentum, it suffices to say Parkour can only be canceled on the 0th tick + return state.getStatus() != MovementStatus.RUNNING; + } + @Override public MovementState updateState(MovementState state) { super.updateState(state); - if (state.getStatus() != MovementState.MovementStatus.RUNNING) { + if (state.getStatus() != MovementStatus.RUNNING) { + return state; + } + if (player().isHandActive()) { + logDebug("Pausing parkour since hand is active"); return state; } if (dist >= 4) { @@ -162,13 +200,19 @@ public class MovementParkour extends Movement { } MovementHelper.moveTowards(state, dest); if (playerFeet().equals(dest)) { - if (player().posY - playerFeet().getY() < 0.01) { - state.setStatus(MovementState.MovementStatus.SUCCESS); + Block d = BlockStateInterface.getBlock(dest); + if (d == Blocks.VINE || d == Blocks.LADDER) { + // it physically hurt me to add support for parkour jumping onto a vine + // but i did it anyway + return state.setStatus(MovementStatus.SUCCESS); + } + if (player().posY - playerFeet().getY() < 0.094) { // lilypads + state.setStatus(MovementStatus.SUCCESS); } } else if (!playerFeet().equals(src)) { if (playerFeet().equals(src.offset(direction)) || player().posY - playerFeet().getY() > 0.0001) { - if (!MovementHelper.canWalkOn(dest.down())) { + if (!MovementHelper.canWalkOn(dest.down()) && !player().onGround) { BlockPos positionToPlace = dest.down(); for (int i = 0; i < 5; i++) { BlockPos against1 = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); @@ -177,15 +221,18 @@ public class MovementParkour extends Movement { } if (MovementHelper.canPlaceAgainst(against1)) { if (!MovementHelper.throwaway(true)) {//get ready to place a throwaway block - return state.setStatus(MovementState.MovementStatus.UNREACHABLE); + 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; - state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); - EnumFacing side = Minecraft.getMinecraft().objectMouseOver.sideHit; - - LookBehaviorUtils.getSelectedBlock().ifPresent(selectedBlock -> { + Rotation place = RotationUtils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()); + RayTraceResult res = RayTraceUtils.rayTraceTowards(player(), place, 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)); + } + RayTraceUtils.getSelectedBlock().ifPresent(selectedBlock -> { + EnumFacing side = Minecraft.getMinecraft().objectMouseOver.sideHit; if (Objects.equals(selectedBlock, against1) && selectedBlock.offset(side).equals(dest.down())) { state.setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } @@ -193,9 +240,17 @@ public class MovementParkour extends Movement { } } } + if (dist == 3) { // this is a 2 block gap, dest = src + direction * 3 + double xDiff = (src.x + 0.5) - player().posX; + double zDiff = (src.z + 0.5) - player().posZ; + double distFromStart = Math.max(Math.abs(xDiff), Math.abs(zDiff)); + if (distFromStart < 0.7) { + return state; + } + } state.setInput(InputOverrideHandler.Input.JUMP, true); - } else { + } else if (!playerFeet().equals(dest.offset(direction, -1))) { state.setInput(InputOverrideHandler.Input.SPRINT, false); if (playerFeet().equals(src.offset(direction, -1))) { MovementHelper.moveTowards(state, src); @@ -206,4 +261,4 @@ public class MovementParkour extends Movement { } return state; } -} \ No newline at end of file +} diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index be524185..f961b043 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -17,72 +17,62 @@ package baritone.pathing.movement.movements; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; +import baritone.api.utils.RotationUtils; +import baritone.api.utils.VecUtils; 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 baritone.utils.InputOverrideHandler; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; public class MovementPillar extends Movement { - private int numTicks = 0; public MovementPillar(BetterBlockPos start, BetterBlockPos end) { super(start, end, new BetterBlockPos[]{start.up(2)}, start); } - @Override - public void reset() { - super.reset(); - numTicks = 0; - } - @Override protected double calculateCost(CalculationContext context) { return cost(context, src.x, src.y, src.z); } public static double cost(CalculationContext context, int x, int y, int z) { - Block fromDown = BlockStateInterface.get(x, y, z).getBlock(); + Block fromDown = context.get(x, y, z).getBlock(); boolean ladder = fromDown instanceof BlockLadder || fromDown instanceof BlockVine; - IBlockState fromDownDown = BlockStateInterface.get(x, y - 1, z); + IBlockState fromDownDown = context.get(x, y - 1, z); if (!ladder) { if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) { return COST_INF; } - if (fromDownDown.getBlock() instanceof BlockSlab) { - if (!((BlockSlab) fromDownDown.getBlock()).isDouble() && fromDownDown.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM) { - return COST_INF; // can't pillar up from a bottom slab onto a non ladder - } + if (fromDownDown.getBlock() instanceof BlockSlab && !((BlockSlab) fromDownDown.getBlock()).isDouble() && fromDownDown.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM) { + return COST_INF; // can't pillar up from a bottom slab onto a non ladder } } - if (fromDown instanceof BlockVine) { - if (!hasAgainst(x, y, z)) { - return COST_INF; - } + if (fromDown instanceof BlockVine && !hasAgainst(context, x, y, z)) { + return COST_INF; } - IBlockState toBreak = BlockStateInterface.get(x, y + 2, z); + IBlockState toBreak = context.get(x, y + 2, z); Block toBreakBlock = toBreak.getBlock(); if (toBreakBlock instanceof BlockFenceGate) { return COST_INF; } Block srcUp = null; - if (BlockStateInterface.isWater(toBreakBlock) && BlockStateInterface.isWater(fromDown)) { - srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); - if (BlockStateInterface.isWater(srcUp)) { + if (MovementHelper.isWater(toBreakBlock) && MovementHelper.isWater(fromDown)) { + srcUp = context.get(x, y + 1, z).getBlock(); + if (MovementHelper.isWater(srcUp)) { return LADDER_UP_ONE_COST; } } - if (!context.hasThrowaway() && !ladder) { + if (!ladder && !context.canPlaceThrowawayAt(x, y, z)) { return COST_INF; } double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); @@ -93,11 +83,11 @@ public class MovementPillar extends Movement { if (toBreakBlock instanceof BlockLadder || toBreakBlock instanceof BlockVine) { hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it } else { - IBlockState check = BlockStateInterface.get(x, y + 3, z); + IBlockState check = context.get(x, y + 3, z); if (check.getBlock() instanceof BlockFalling) { // see MovementAscend's identical check for breaking a falling block above our head if (srcUp == null) { - srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); + srcUp = context.get(x, y + 1, z).getBlock(); } if (!(toBreakBlock instanceof BlockFalling) || !(srcUp instanceof BlockFalling)) { return COST_INF; @@ -122,24 +112,24 @@ public class MovementPillar extends Movement { } } - public static boolean hasAgainst(int x, int y, int z) { - return BlockStateInterface.get(x + 1, y, z).isBlockNormalCube() || - BlockStateInterface.get(x - 1, y, z).isBlockNormalCube() || - BlockStateInterface.get(x, y, z + 1).isBlockNormalCube() || - BlockStateInterface.get(x, y, z - 1).isBlockNormalCube(); + public static boolean hasAgainst(CalculationContext context, int x, int y, int z) { + return context.get(x + 1, y, z).isBlockNormalCube() || + context.get(x - 1, y, z).isBlockNormalCube() || + context.get(x, y, z + 1).isBlockNormalCube() || + context.get(x, y, z - 1).isBlockNormalCube(); } - public static BlockPos getAgainst(BlockPos vine) { - if (BlockStateInterface.get(vine.north()).isBlockNormalCube()) { + public static BlockPos getAgainst(CalculationContext context, BetterBlockPos vine) { + if (context.get(vine.north()).isBlockNormalCube()) { return vine.north(); } - if (BlockStateInterface.get(vine.south()).isBlockNormalCube()) { + if (context.get(vine.south()).isBlockNormalCube()) { return vine.south(); } - if (BlockStateInterface.get(vine.east()).isBlockNormalCube()) { + if (context.get(vine.east()).isBlockNormalCube()) { return vine.east(); } - if (BlockStateInterface.get(vine.west()).isBlockNormalCube()) { + if (context.get(vine.west()).isBlockNormalCube()) { return vine.west(); } return null; @@ -148,43 +138,44 @@ public class MovementPillar extends Movement { @Override public MovementState updateState(MovementState state) { super.updateState(state); - if (state.getStatus() != MovementState.MovementStatus.RUNNING) { + if (state.getStatus() != MovementStatus.RUNNING) { return state; } IBlockState fromDown = BlockStateInterface.get(src); - if (BlockStateInterface.isWater(fromDown.getBlock()) && BlockStateInterface.isWater(dest)) { + if (MovementHelper.isWater(fromDown.getBlock()) && MovementHelper.isWater(dest)) { // stay centered while swimming up a water column - state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(playerHead(), Utils.getBlockPosCenter(dest)), false)); - Vec3d destCenter = Utils.getBlockPosCenter(dest); + state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(playerHead(), VecUtils.getBlockPosCenter(dest)), false)); + Vec3d destCenter = VecUtils.getBlockPosCenter(dest); if (Math.abs(player().posX - destCenter.x) > 0.2 || Math.abs(player().posZ - destCenter.z) > 0.2) { state.setInput(InputOverrideHandler.Input.MOVE_FORWARD, true); } if (playerFeet().equals(dest)) { - return state.setStatus(MovementState.MovementStatus.SUCCESS); + return state.setStatus(MovementStatus.SUCCESS); } return state; } boolean ladder = fromDown.getBlock() instanceof BlockLadder || fromDown.getBlock() instanceof BlockVine; boolean vine = fromDown.getBlock() instanceof BlockVine; + Rotation rotation = RotationUtils.calcRotationFromVec3d(player().getPositionEyes(1.0F), + VecUtils.getBlockPosCenter(positionToPlace), + new Rotation(player().rotationYaw, player().rotationPitch)); if (!ladder) { - state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(mc.player.getPositionEyes(1.0F), - Utils.getBlockPosCenter(positionToPlace), - new Rotation(mc.player.rotationYaw, mc.player.rotationPitch)), true)); + state.setTarget(new MovementState.MovementTarget(new Rotation(player().rotationYaw, rotation.getPitch()), true)); } boolean blockIsThere = MovementHelper.canWalkOn(src) || ladder; if (ladder) { - BlockPos against = vine ? getAgainst(src) : src.offset(fromDown.getValue(BlockLadder.FACING).getOpposite()); + BlockPos against = vine ? getAgainst(new CalculationContext(), src) : src.offset(fromDown.getValue(BlockLadder.FACING).getOpposite()); if (against == null) { logDebug("Unable to climb vines"); - return state.setStatus(MovementState.MovementStatus.UNREACHABLE); + return state.setStatus(MovementStatus.UNREACHABLE); } if (playerFeet().equals(against.up()) || playerFeet().equals(dest)) { - return state.setStatus(MovementState.MovementStatus.SUCCESS); + return state.setStatus(MovementStatus.SUCCESS); } - if (MovementHelper.isBottomSlab(src.down())) { + if (MovementHelper.isBottomSlab(BlockStateInterface.get(src.down()))) { state.setInput(InputOverrideHandler.Input.JUMP, true); } /* @@ -198,34 +189,37 @@ public class MovementPillar extends Movement { } else { // Get ready to place a throwaway block if (!MovementHelper.throwaway(true)) { - return state.setStatus(MovementState.MovementStatus.UNREACHABLE); + return state.setStatus(MovementStatus.UNREACHABLE); } - numTicks++; - // If our Y coordinate is above our goal, stop jumping - state.setInput(InputOverrideHandler.Input.JUMP, player().posY < dest.getY()); - state.setInput(InputOverrideHandler.Input.SNEAK, true); - // Otherwise jump - if (numTicks > 20) { - double diffX = player().posX - (dest.getX() + 0.5); - double diffZ = player().posZ - (dest.getZ() + 0.5); - double dist = Math.sqrt(diffX * diffX + diffZ * diffZ); - if (dist > 0.17) {//why 0.17? because it seemed like a good number, that's why - //[explanation added after baritone port lol] also because it needs to be less than 0.2 because of the 0.3 sneak limit - //and 0.17 is reasonably less than 0.2 + state.setInput(InputOverrideHandler.Input.SNEAK, player().posY > dest.getY()); // delay placement by 1 tick for ncp compatibility + // since (lower down) we only right click once player.isSneaking, and that happens the tick after we request to sneak - // If it's been more than forty ticks of trying to jump and we aren't done yet, go forward, maybe we are stuck - state.setInput(InputOverrideHandler.Input.MOVE_FORWARD, true); - } + double diffX = player().posX - (dest.getX() + 0.5); + double diffZ = player().posZ - (dest.getZ() + 0.5); + double dist = Math.sqrt(diffX * diffX + diffZ * diffZ); + if (dist > 0.17) {//why 0.17? because it seemed like a good number, that's why + //[explanation added after baritone port lol] also because it needs to be less than 0.2 because of the 0.3 sneak limit + //and 0.17 is reasonably less than 0.2 + + // If it's been more than forty ticks of trying to jump and we aren't done yet, go forward, maybe we are stuck + state.setInput(InputOverrideHandler.Input.MOVE_FORWARD, true); + + // revise our target to both yaw and pitch if we're going to be moving forward + state.setTarget(new MovementState.MovementTarget(rotation, true)); + } else { + // If our Y coordinate is above our goal, stop jumping + state.setInput(InputOverrideHandler.Input.JUMP, player().posY < dest.getY()); } + if (!blockIsThere) { Block fr = BlockStateInterface.get(src).getBlock(); - if (!(fr instanceof BlockAir || fr.isReplaceable(Minecraft.getMinecraft().world, src))) { + if (!(fr instanceof BlockAir || fr.isReplaceable(world(), src))) { state.setInput(InputOverrideHandler.Input.CLICK_LEFT, true); blockIsThere = false; - } else if (Minecraft.getMinecraft().player.isSneaking()) { + } else if (player().isSneaking()) { // 1 tick after we're able to place state.setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } } @@ -233,7 +227,7 @@ public class MovementPillar extends Movement { // If we are at our goal and the block below us is placed if (playerFeet().equals(dest) && blockIsThere) { - return state.setStatus(MovementState.MovementStatus.SUCCESS); + return state.setStatus(MovementStatus.SUCCESS); } return state; @@ -247,9 +241,9 @@ public class MovementPillar extends Movement { state.setInput(InputOverrideHandler.Input.SNEAK, true); } } - if (BlockStateInterface.isWater(dest.up())) { + if (MovementHelper.isWater(dest.up())) { return true; } return super.prepared(state); } -} \ No newline at end of file +} diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index a16a50a1..a4f32855 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -18,16 +18,14 @@ package baritone.pathing.movement.movements; import baritone.Baritone; -import baritone.api.utils.Rotation; -import baritone.behavior.LookBehaviorUtils; +import baritone.api.pathing.movement.MovementStatus; +import baritone.api.utils.*; 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 baritone.utils.InputOverrideHandler; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -61,14 +59,16 @@ public class MovementTraverse extends Movement { } public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { - IBlockState pb0 = BlockStateInterface.get(destX, y + 1, destZ); - IBlockState pb1 = BlockStateInterface.get(destX, y, destZ); - IBlockState destOn = BlockStateInterface.get(destX, y - 1, destZ); - Block srcDown = BlockStateInterface.getBlock(x, y - 1, z); - if (MovementHelper.canWalkOn(destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge + IBlockState pb0 = context.get(destX, y + 1, destZ); + IBlockState pb1 = context.get(destX, y, destZ); + IBlockState destOn = context.get(destX, y - 1, destZ); + Block srcDown = context.getBlock(x, y - 1, z); + if (MovementHelper.canWalkOn(context, destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; - if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) { - WC = WALK_ONE_IN_WATER_COST; + boolean water = false; + if (MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock())) { + WC = context.waterWalkSpeed(); + water = true; } else { if (destOn.getBlock() == Blocks.SOUL_SAND) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; @@ -83,10 +83,11 @@ public class MovementTraverse extends Movement { } double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); if (hardness1 == 0 && hardness2 == 0) { - if (WC == WALK_ONE_BLOCK_COST && context.canSprint()) { - // If there's nothing in the way, and this isn't water or soul sand, and we aren't sneak placing + if (!water && context.canSprint()) { + // If there's nothing in the way, and this isn't water, and we aren't sneak placing // We can sprint =D - WC = SPRINT_ONE_BLOCK_COST; + // Don't check for soul sand, since we can sprint on that too + WC *= SPRINT_MULTIPLIER; } return WC; } @@ -100,11 +101,11 @@ public class MovementTraverse extends Movement { return COST_INF; } if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(destX, y - 1, destZ, destOn)) { - boolean throughWater = BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock()); - if (BlockStateInterface.isWater(destOn.getBlock()) && throughWater) { + boolean throughWater = MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock()); + if (MovementHelper.isWater(destOn.getBlock()) && throughWater) { return COST_INF; } - if (!context.hasThrowaway()) { + if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { return COST_INF; } double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb0, false); @@ -113,14 +114,14 @@ public class MovementTraverse extends Movement { } double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb1, true); - double WC = throughWater ? WALK_ONE_IN_WATER_COST : WALK_ONE_BLOCK_COST; + double WC = throughWater ? context.waterWalkSpeed() : WALK_ONE_BLOCK_COST; for (int i = 0; i < 4; i++) { int againstX = destX + HORIZONTALS[i].getXOffset(); int againstZ = destZ + HORIZONTALS[i].getZOffset(); if (againstX == x && againstZ == z) { continue; } - if (MovementHelper.canPlaceAgainst(againstX, y - 1, againstZ)) { + if (MovementHelper.canPlaceAgainst(context, againstX, y - 1, againstZ)) { return WC + context.placeBlockCost() + hardness1 + hardness2; } } @@ -141,13 +142,13 @@ public class MovementTraverse extends Movement { @Override public MovementState updateState(MovementState state) { super.updateState(state); - if (state.getStatus() != MovementState.MovementStatus.RUNNING) { + if (state.getStatus() != MovementStatus.RUNNING) { // if the setting is enabled if (!Baritone.settings().walkWhileBreaking.get()) { return state; } // and if we're prepping (aka mining the block in front) - if (state.getStatus() != MovementState.MovementStatus.PREPPING) { + if (state.getStatus() != MovementStatus.PREPPING) { return state; } // and if it's fine to walk into the blocks in front @@ -165,7 +166,7 @@ public class MovementTraverse extends Movement { // 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 = Utils.calcRotationFromVec3d(playerHead(), Utils.calcCenterFromCoords(dest, world())).getYaw(); + float yawToDest = RotationUtils.calcRotationFromVec3d(playerHead(), VecUtils.calculateBlockCenter(dest)).getYaw(); float pitchToBreak = state.getTarget().getRotation().get().getPitch(); state.setTarget(new MovementState.MovementTarget(new Rotation(yawToDest, pitchToBreak), true)); @@ -188,11 +189,9 @@ public class MovementTraverse extends Movement { } else if (pb1.getBlock() instanceof BlockDoor && !MovementHelper.isDoorPassable(dest, src)) { isDoorActuallyBlockingUs = true; } - if (isDoorActuallyBlockingUs) { - if (!(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock()))) { - return state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(playerHead(), Utils.calcCenterFromCoords(positionsToBreak[0], world())), true)) - .setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); - } + if (isDoorActuallyBlockingUs && !(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock()))) { + return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(playerHead(), VecUtils.calculateBlockCenter(positionsToBreak[0])), true)) + .setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } } @@ -205,7 +204,7 @@ public class MovementTraverse extends Movement { } if (blocked != null) { - return state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(playerHead(), Utils.calcCenterFromCoords(blocked, world())), true)) + return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(playerHead(), VecUtils.calculateBlockCenter(blocked)), true)) .setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } } @@ -222,9 +221,9 @@ public class MovementTraverse extends Movement { if (isTheBridgeBlockThere) { if (playerFeet().equals(dest)) { - return state.setStatus(MovementState.MovementStatus.SUCCESS); + return state.setStatus(MovementStatus.SUCCESS); } - if (wasTheBridgeBlockAlwaysThere && !BlockStateInterface.isLiquid(playerFeet())) { + if (wasTheBridgeBlockAlwaysThere && !MovementHelper.isLiquid(playerFeet())) { state.setInput(InputOverrideHandler.Input.SPRINT, true); } Block destDown = BlockStateInterface.get(dest.down()).getBlock(); @@ -245,7 +244,7 @@ public class MovementTraverse extends Movement { if (MovementHelper.canPlaceAgainst(against1)) { if (!MovementHelper.throwaway(true)) { // get ready to place a throwaway block logDebug("bb pls get me some blocks. dirt or cobble"); - return state.setStatus(MovementState.MovementStatus.UNREACHABLE); + return state.setStatus(MovementStatus.UNREACHABLE); } if (!Baritone.settings().assumeSafeWalk.get()) { state.setInput(InputOverrideHandler.Input.SNEAK, true); @@ -263,16 +262,13 @@ public class MovementTraverse extends Movement { 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(Utils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); + state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); EnumFacing side = Minecraft.getMinecraft().objectMouseOver.sideHit; - if (Objects.equals(LookBehaviorUtils.getSelectedBlock().orElse(null), against1) && Minecraft.getMinecraft().player.isSneaking()) { - if (LookBehaviorUtils.getSelectedBlock().get().offset(side).equals(positionToPlace)) { - return state.setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); - } - // wrong side? + if (Objects.equals(RayTraceUtils.getSelectedBlock().orElse(null), against1) && (player().isSneaking() || Baritone.settings().assumeSafeWalk.get()) && RayTraceUtils.getSelectedBlock().get().offset(side).equals(positionToPlace)) { + return state.setInput(InputOverrideHandler.Input.CLICK_RIGHT, true); } - System.out.println("Trying to look at " + against1 + ", actually looking at" + LookBehaviorUtils.getSelectedBlock()); + //System.out.println("Trying to look at " + against1 + ", actually looking at" + RayTraceUtils.getSelectedBlock()); return state.setInput(InputOverrideHandler.Input.CLICK_LEFT, true); } } @@ -284,18 +280,26 @@ public class MovementTraverse extends Movement { // Out.log(from + " " + to + " " + faceX + "," + faceY + "," + faceZ + " " + whereAmI); if (!MovementHelper.throwaway(true)) {// get ready to place a throwaway block logDebug("bb pls get me some blocks. dirt or cobble"); - return state.setStatus(MovementState.MovementStatus.UNREACHABLE); + 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; // faceX, faceY, faceZ is the middle of the face between from and to BlockPos goalLook = src.down(); // this is the block we were just standing on, and the one we want to place against - state.setTarget(new MovementState.MovementTarget(Utils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()), true)); - state.setInput(InputOverrideHandler.Input.MOVE_BACK, true); + Rotation backToFace = RotationUtils.calcRotationFromVec3d(playerHead(), new Vec3d(faceX, faceY, faceZ), playerRotations()); + float pitch = backToFace.getPitch(); + double dist = Math.max(Math.abs(player().posX - faceX), Math.abs(player().posZ - faceZ)); + if (dist < 0.29) { + float yaw = RotationUtils.calcRotationFromVec3d(VecUtils.getBlockPosCenter(dest), playerHead(), playerRotations()).getYaw(); + state.setTarget(new MovementState.MovementTarget(new Rotation(yaw, pitch), true)); + state.setInput(InputOverrideHandler.Input.MOVE_BACK, true); + } else { + state.setTarget(new MovementState.MovementTarget(backToFace, true)); + } state.setInput(InputOverrideHandler.Input.SNEAK, true); - if (Objects.equals(LookBehaviorUtils.getSelectedBlock().orElse(null), goalLook)) { + if (Objects.equals(RayTraceUtils.getSelectedBlock().orElse(null), goalLook)) { return state.setInput(InputOverrideHandler.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()); @@ -308,6 +312,14 @@ public class MovementTraverse extends Movement { } } + @Override + public boolean safeToCancel(MovementState state) { + // if we're in the process of breaking blocks before walking forwards + // or if this isn't a sneak place (the block is already there) + // then it's safe to cancel this + return state.getStatus() != MovementStatus.RUNNING || MovementHelper.canWalkOn(dest.down()); + } + @Override protected boolean prepared(MovementState state) { if (playerFeet().equals(src) || playerFeet().equals(src.down())) { diff --git a/src/main/java/baritone/pathing/path/CutoffPath.java b/src/main/java/baritone/pathing/path/CutoffPath.java index e517452e..30b2d4e9 100644 --- a/src/main/java/baritone/pathing/path/CutoffPath.java +++ b/src/main/java/baritone/pathing/path/CutoffPath.java @@ -17,28 +17,35 @@ package baritone.pathing.path; +import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; -import baritone.pathing.movement.Movement; -import baritone.utils.pathing.BetterBlockPos; +import baritone.api.pathing.movement.IMovement; +import baritone.api.utils.BetterBlockPos; +import baritone.utils.pathing.PathBase; import java.util.Collections; import java.util.List; -public class CutoffPath implements IPath { +public class CutoffPath extends PathBase { private final List path; - private final List movements; + private final List movements; private final int numNodes; private final Goal goal; - public CutoffPath(IPath prev, int lastPositionToInclude) { - path = prev.positions().subList(0, lastPositionToInclude + 1); - movements = prev.movements().subList(0, lastPositionToInclude + 1); + public CutoffPath(IPath prev, int firstPositionToInclude, int lastPositionToInclude) { + path = prev.positions().subList(firstPositionToInclude, lastPositionToInclude + 1); + movements = prev.movements().subList(firstPositionToInclude, lastPositionToInclude); numNodes = prev.getNumNodesConsidered(); goal = prev.getGoal(); + sanityCheck(); + } + + public CutoffPath(IPath prev, int lastPositionToInclude) { + this(prev, 0, lastPositionToInclude); } @Override @@ -47,7 +54,7 @@ public class CutoffPath implements IPath { } @Override - public List movements() { + public List movements() { return Collections.unmodifiableList(movements); } diff --git a/src/main/java/baritone/pathing/path/IPath.java b/src/main/java/baritone/pathing/path/IPath.java deleted file mode 100644 index 0701abf6..00000000 --- a/src/main/java/baritone/pathing/path/IPath.java +++ /dev/null @@ -1,138 +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.pathing.path; - -import baritone.Baritone; -import baritone.api.pathing.goals.Goal; -import baritone.pathing.movement.Movement; -import baritone.utils.Helper; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.client.Minecraft; -import net.minecraft.util.Tuple; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.EmptyChunk; - -import java.util.List; - -/** - * @author leijurv - */ -public interface IPath extends Helper { - - /** - * Ordered list of movements to carry out. - * movements.get(i).getSrc() should equal positions.get(i) - * movements.get(i).getDest() should equal positions.get(i+1) - * movements.size() should equal positions.size()-1 - */ - List movements(); - - /** - * All positions along the way. - * Should begin with the same as getSrc and end with the same as getDest - */ - List positions(); - - /** - * This path is actually going to be executed in the world. Do whatever additional processing is required. - * (as opposed to Path objects that are just constructed every frame for rendering) - */ - default void postprocess() {} - - /** - * Number of positions in this path - * - * @return Number of positions in this path - */ - default int length() { - return positions().size(); - } - - /** - * What goal was this path calculated towards? - * - * @return - */ - Goal getGoal(); - - default Tuple closestPathPos() { - double best = -1; - BlockPos bestPos = null; - for (BlockPos pos : positions()) { - double dist = Utils.playerDistanceToCenter(pos); - if (dist < best || best == -1) { - best = dist; - bestPos = pos; - } - } - return new Tuple<>(best, bestPos); - } - - /** - * Where does this path start - */ - default BetterBlockPos getSrc() { - return positions().get(0); - } - - /** - * Where does this path end - */ - default BetterBlockPos getDest() { - List pos = positions(); - return pos.get(pos.size() - 1); - } - - default double ticksRemainingFrom(int pathPosition) { - double sum = 0; - //this is fast because we aren't requesting recalculation, it's just cached - for (int i = pathPosition; i < movements().size(); i++) { - sum += movements().get(i).getCost(null); - } - return sum; - } - - int getNumNodesConsidered(); - - default IPath cutoffAtLoadedChunks() { - for (int i = 0; i < positions().size(); i++) { - BlockPos pos = positions().get(i); - if (Minecraft.getMinecraft().world.getChunk(pos) instanceof EmptyChunk) { - logDebug("Cutting off path at edge of loaded chunks"); - logDebug("Length decreased by " + (positions().size() - i - 1)); - return new CutoffPath(this, i); - } - } - logDebug("Path ends within loaded chunks"); - return this; - } - - default IPath staticCutoff(Goal destination) { - if (length() < Baritone.settings().pathCutoffMinimumLength.get()) { - return this; - } - if (destination == null || destination.isInGoal(getDest())) { - return this; - } - double factor = Baritone.settings().pathCutoffFactor.get(); - int newLength = (int) (length() * factor); - logDebug("Static cutoff " + length() + " to " + newLength); - return new CutoffPath(this, newLength); - } -} diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index e8e74c7d..5cc472e6 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -18,23 +18,28 @@ package baritone.pathing.path; import baritone.Baritone; -import baritone.api.event.events.TickEvent; +import baritone.api.pathing.calc.IPath; import baritone.api.pathing.movement.ActionCosts; -import baritone.pathing.movement.*; +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.VecUtils; +import baritone.pathing.calc.AbstractNodeCostSearch; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.movements.*; +import baritone.utils.BlockBreakHelper; import baritone.utils.BlockStateInterface; import baritone.utils.Helper; -import baritone.utils.Utils; -import baritone.utils.pathing.BetterBlockPos; +import baritone.utils.InputOverrideHandler; import net.minecraft.init.Blocks; import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; -import static baritone.pathing.movement.MovementState.MovementStatus.*; +import static baritone.api.pathing.movement.MovementStatus.*; /** * Behavior to execute a precomputed path. Does not (yet) deal with path segmentation or stitching @@ -42,7 +47,7 @@ import static baritone.pathing.movement.MovementState.MovementStatus.*; * * @author leijurv */ -public class PathExecutor implements Helper { +public class PathExecutor implements IPathExecutor, Helper { private static final double MAX_MAX_DIST_FROM_PATH = 3; private static final double MAX_DIST_FROM_PATH = 2; @@ -75,14 +80,10 @@ public class PathExecutor implements Helper { /** * Tick this executor * - * @param event * @return True if a movement just finished (and the player is therefore in a "stable" state, like, * not sneaking out over lava), false otherwise */ - public boolean onTick(TickEvent event) { - if (event.getType() == TickEvent.Type.OUT) { - throw new IllegalStateException(); - } + public boolean onTick() { if (pathPosition == path.length() - 1) { pathPosition++; } @@ -110,24 +111,25 @@ public class PathExecutor implements Helper { for (int j = pathPosition; j <= previousPos; j++) { path.movements().get(j).reset(); } - clearKeys(); + onChangeInPathPosition(); return false; } } - for (int i = pathPosition + 2; i < path.length(); i++) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing) + 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"); + //System.out.println("Double skip sundae"); pathPosition = i - 1; - clearKeys(); + onChangeInPathPosition(); return false; } } } } - Tuple status = path.closestPathPos(); + Tuple status = closestPathPos(path); if (possiblyOffPath(status, MAX_DIST_FROM_PATH)) { ticksAway++; System.out.println("FAR AWAY FROM PATH FOR " + ticksAway + " TICKS. Current distance: " + status.getFirst() + ". Threshold: " + MAX_DIST_FROM_PATH); @@ -176,18 +178,16 @@ public class PathExecutor implements Helper { } } }*/ - long start = System.nanoTime() / 1000000L; + //long start = System.nanoTime() / 1000000L; for (int i = pathPosition - 10; i < pathPosition + 10; i++) { if (i < 0 || i >= path.movements().size()) { continue; } - Movement m = path.movements().get(i); + IMovement m = path.movements().get(i); HashSet prevBreak = new HashSet<>(m.toBreak()); HashSet prevPlace = new HashSet<>(m.toPlace()); HashSet prevWalkInto = new HashSet<>(m.toWalkInto()); - m.toBreakCached = null; - m.toPlaceCached = null; - m.toWalkIntoCached = null; + m.resetBlockCache(); if (!prevBreak.equals(new HashSet<>(m.toBreak()))) { recalcBP = true; } @@ -212,17 +212,18 @@ public class PathExecutor implements Helper { toWalkInto = newWalkInto; recalcBP = false; } - long end = System.nanoTime() / 1000000L; + /*long end = System.nanoTime() / 1000000L; if (end - start > 0) { System.out.println("Recalculating break and place took " + (end - start) + "ms"); - } - Movement movement = path.movements().get(pathPosition); + }*/ + IMovement movement = path.movements().get(pathPosition); + 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(null); + 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) { + if (path.movements().get(pathPosition + i).calculateCostWithoutCaching() >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and a future movement has become impossible. Cancelling."); cancel(); return true; @@ -230,17 +231,22 @@ public class PathExecutor implements Helper { } } double currentCost = movement.recalculateCost(); - if (currentCost >= ActionCosts.COST_INF) { + 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()) { + if (!movement.calculatedWhileLoaded() && currentCost - currentMovementOriginalCostEstimate > Baritone.settings().maxCostIncrease.get() && canCancel) { logDebug("Original cost " + currentMovementOriginalCostEstimate + " current cost " + currentCost + ". Cancelling."); cancel(); return true; } - MovementState.MovementStatus movementStatus = movement.update(); + if (shouldPause()) { + logDebug("Pausing since current best path is a backtrack"); + clearKeys(); + return true; + } + MovementStatus movementStatus = movement.update(); if (movementStatus == UNREACHABLE || movementStatus == FAILED) { logDebug("Movement returns status " + movementStatus); cancel(); @@ -249,9 +255,8 @@ public class PathExecutor implements Helper { if (movementStatus == SUCCESS) { //System.out.println("Movement done, next path"); pathPosition++; - ticksOnCurrent = 0; - clearKeys(); - onTick(event); + onChangeInPathPosition(); + onTick(); return true; } else { sprintIfRequested(); @@ -266,7 +271,53 @@ public class PathExecutor implements Helper { return true; } } - return false; // movement is in progress + return canCancel; // movement is in progress, but if it reports cancellable, PathingBehavior is good to cut onto the next path + } + + private Tuple closestPathPos(IPath path) { + double best = -1; + BlockPos bestPos = null; + for (BlockPos pos : path.positions()) { + double dist = VecUtils.entityDistanceToCenter(player(), pos); + if (dist < best || best == -1) { + best = dist; + bestPos = pos; + } + } + return new Tuple<>(best, bestPos); + } + + private boolean shouldPause() { + Optional current = AbstractNodeCostSearch.getCurrentlyRunning(); + if (!current.isPresent()) { + return false; + } + if (!player().onGround) { + return false; + } + if (!MovementHelper.canWalkOn(playerFeet().down())) { + // we're in some kind of sketchy situation, maybe parkouring + return false; + } + if (!MovementHelper.canWalkThrough(playerFeet()) || !MovementHelper.canWalkThrough(playerFeet().up())) { + // suffocating? + return false; + } + if (!path.movements().get(pathPosition).safeToCancel()) { + return false; + } + Optional currentBest = current.get().bestPathSoFar(); + if (!currentBest.isPresent()) { + return false; + } + List positions = currentBest.get().positions(); + if (positions.size() < 3) { + return false; // not long enough yet to justify pausing, its far from certain we'll actually take this route + } + // the first block of the next path will always overlap + // no need to pause our very last movement when it would have otherwise cleanly exited with MovementStatus SUCCESS + positions = positions.subList(1, positions.size()); + return positions.contains(playerFeet()); } private boolean possiblyOffPath(Tuple status, double leniency) { @@ -275,10 +326,7 @@ public class PathExecutor implements Helper { // when we're midair in the middle of a fall, we're very far from both the beginning and the end, but we aren't actually off path if (path.movements().get(pathPosition) instanceof MovementFall) { BlockPos fallDest = path.positions().get(pathPosition + 1); // .get(pathPosition) is the block we fell off of - if (Utils.playerFlatDistanceToCenter(fallDest) < leniency) { // ignore Y by using flat distance - return false; - } - return true; + return VecUtils.entityFlatDistanceToCenter(player(), fallDest) >= leniency; // ignore Y by using flat distance } else { return true; } @@ -287,10 +335,24 @@ public class PathExecutor implements Helper { } } + /** + * Regardless of current path position, snap to the current player feet if possible + */ + public boolean snipsnapifpossible() { + int index = path.positions().indexOf(playerFeet()); + if (index == -1) { + return false; + } + pathPosition = index; + clearKeys(); + return true; + } + private void sprintIfRequested() { // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint if (!new CalculationContext().canSprint()) { + Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(InputOverrideHandler.Input.SPRINT, false); player().setSprinting(false); return; } @@ -303,8 +365,11 @@ public class PathExecutor implements Helper { return; } + // we'll take it from here, no need for minecraft to see we're holding down control and sprint for us + Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(InputOverrideHandler.Input.SPRINT, false); + // however, descend doesn't request sprinting, beceause it doesn't know the context of what movement comes after it - Movement current = path.movements().get(pathPosition); + IMovement current = path.movements().get(pathPosition); if (current instanceof MovementDescend && pathPosition < path.length() - 2) { // (dest - src) + dest is offset 1 more in the same direction @@ -319,11 +384,21 @@ public class PathExecutor implements Helper { } } - Movement next = path.movements().get(pathPosition + 1); + 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 + if (!player().isSprinting()) { + player().setSprinting(true); + } + 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; + } if (canSprintInto(current, next)) { if (playerFeet().equals(current.getDest())) { pathPosition++; - clearKeys(); + onChangeInPathPosition(); } if (!player().isSprinting()) { player().setSprinting(true); @@ -332,24 +407,35 @@ public class PathExecutor implements Helper { } //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 (player().posY >= center.getY()) { // playerFeet adds 0.1251 to account for soul sand + Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(InputOverrideHandler.Input.JUMP, false); + if (!player().isSprinting()) { + player().setSprinting(true); + } + return; + } + } + } player().setSprinting(false); } - private static boolean canSprintInto(Movement current, Movement next) { - if (next instanceof MovementDescend) { - if (next.getDirection().equals(current.getDirection())) { - return true; - } - } - if (next instanceof MovementTraverse) { - if (next.getDirection().down().equals(current.getDirection()) && MovementHelper.canWalkOn(next.getDest().down())) { - return true; - } - } - if (next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.get()) { + private static boolean canSprintInto(IMovement current, IMovement next) { + if (next instanceof MovementDescend && next.getDirection().equals(current.getDirection())) { return true; } - return false; + if (next instanceof MovementTraverse && next.getDirection().down().equals(current.getDirection()) && MovementHelper.canWalkOn(next.getDest().down())) { + return true; + } + return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.get(); + } + + private void onChangeInPathPosition() { + clearKeys(); + ticksOnCurrent = 0; } private static void clearKeys() { @@ -359,6 +445,7 @@ public class PathExecutor implements Helper { private void cancel() { clearKeys(); + BlockBreakHelper.stopBreakingBlock(); pathPosition = path.length() + 3; failed = true; } @@ -367,6 +454,44 @@ public class PathExecutor implements Helper { return pathPosition; } + public PathExecutor trySplice(PathExecutor next) { + if (next == null) { + return cutIfTooLong(); + } + return SplicedPath.trySplice(path, next.path).map(path -> { + if (!path.getDest().equals(next.getPath().getDest())) { + throw new IllegalStateException(); + } + PathExecutor ret = new PathExecutor(path); + ret.pathPosition = pathPosition; + ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; + ret.costEstimateIndex = costEstimateIndex; + ret.ticksOnCurrent = ticksOnCurrent; + return ret; + }).orElse(cutIfTooLong()); + } + + private PathExecutor cutIfTooLong() { + if (pathPosition > Baritone.settings().maxPathHistoryLength.get()) { + int cutoffAmt = Baritone.settings().pathHistoryCutoffAmount.get(); + CutoffPath newPath = new CutoffPath(path, cutoffAmt, path.length() - 1); + if (!newPath.getDest().equals(path.getDest())) { + throw new IllegalStateException(); + } + logDebug("Discarding earliest segment movements, length cut from " + path.length() + " to " + newPath.length()); + PathExecutor ret = new PathExecutor(newPath); + ret.pathPosition = pathPosition - cutoffAmt; + ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; + if (costEstimateIndex != null) { + ret.costEstimateIndex = costEstimateIndex - cutoffAmt; + } + ret.ticksOnCurrent = ticksOnCurrent; + return ret; + } + return this; + } + + @Override public IPath getPath() { return path; } diff --git a/src/main/java/baritone/pathing/path/SplicedPath.java b/src/main/java/baritone/pathing/path/SplicedPath.java new file mode 100644 index 00000000..5048a84a --- /dev/null +++ b/src/main/java/baritone/pathing/path/SplicedPath.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.pathing.path; + +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.utils.pathing.PathBase; + +import java.util.*; + +public class SplicedPath extends PathBase { + private final List path; + + private final List movements; + + private final int numNodes; + + private final Goal goal; + + private SplicedPath(List path, List movements, int numNodesConsidered, Goal goal) { + this.path = path; + this.movements = movements; + this.numNodes = numNodesConsidered; + this.goal = goal; + sanityCheck(); + } + + @Override + public Goal getGoal() { + return goal; + } + + @Override + public List movements() { + return Collections.unmodifiableList(movements); + } + + @Override + public List positions() { + return Collections.unmodifiableList(path); + } + + @Override + public int getNumNodesConsidered() { + return numNodes; + } + + public static Optional trySplice(IPath first, IPath second) { + if (second == null || first == null) { + return Optional.empty(); + } + if (!Objects.equals(first.getGoal(), second.getGoal())) { + return Optional.empty(); + } + if (!first.getDest().equals(second.getSrc())) { + return Optional.empty(); + } + HashSet a = new HashSet<>(first.positions()); + for (int i = 1; i < second.length(); i++) { + if (a.contains(second.positions().get(i))) { + return Optional.empty(); + } + } + List positions = new ArrayList<>(); + List movements = new ArrayList<>(); + positions.addAll(first.positions()); + positions.addAll(second.positions().subList(1, second.length())); + movements.addAll(first.movements()); + movements.addAll(second.movements()); + return Optional.of(new SplicedPath(positions, movements, first.getNumNodesConsidered() + second.getNumNodesConsidered(), first.getGoal())); + } +} diff --git a/src/main/java/baritone/process/CustomGoalProcess.java b/src/main/java/baritone/process/CustomGoalProcess.java new file mode 100644 index 00000000..98c700e3 --- /dev/null +++ b/src/main/java/baritone/process/CustomGoalProcess.java @@ -0,0 +1,115 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.ICustomGoalProcess; +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 { + + /** + * The current goal + */ + private Goal goal; + + /** + * The current process state. + * + * @see State + */ + private State state; + + public CustomGoalProcess(Baritone baritone) { + super(baritone, 3); + } + + @Override + public void setGoal(Goal goal) { + this.goal = goal; + this.state = State.GOAL_SET; + } + + @Override + public void path() { + this.state = State.PATH_REQUESTED; + } + + @Override + public Goal getGoal() { + return this.goal; + } + + @Override + public boolean isActive() { + return this.state != State.NONE; + } + + @Override + 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); + this.state = State.EXECUTING; + return ret; + case EXECUTING: + if (calcFailed) { + onLostControl(); + } + if (this.goal == null || this.goal.isInGoal(playerFeet())) { + onLostControl(); // we're there xd + } + return new PathingCommand(this.goal, PathingCommandType.SET_GOAL_AND_PATH); + default: + throw new IllegalStateException(); + } + } + + @Override + public void onLostControl() { + this.state = State.NONE; + this.goal = null; + } + + @Override + public String displayName() { + return "Custom Goal " + this.goal; + } + + protected enum State { + NONE, + GOAL_SET, + PATH_REQUESTED, + EXECUTING + } +} diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java new file mode 100644 index 00000000..959e3a48 --- /dev/null +++ b/src/main/java/baritone/process/FollowProcess.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.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.pathing.goals.GoalNear; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.process.IFollowProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +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; +import java.util.stream.Stream; + +/** + * Follow an entity + * + * @author leijurv + */ +public final class FollowProcess extends BaritoneProcessHelper implements IFollowProcess { + + private Predicate filter; + private List cache; + + public FollowProcess(Baritone baritone) { + super(baritone, 1); + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + scanWorld(); + Goal goal = new GoalComposite(cache.stream().map(this::towards).toArray(Goal[]::new)); + return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); + } + + private Goal towards(Entity following) { + // lol this is trashy but it works + BlockPos pos; + if (Baritone.settings().followOffsetDistance.get() == 0) { + pos = new BlockPos(following); + } else { + GoalXZ g = GoalXZ.fromDirection(following.getPositionVector(), Baritone.settings().followOffsetDirection.get(), Baritone.settings().followOffsetDistance.get()); + pos = new BlockPos(g.getX(), following.posY, g.getZ()); + } + return new GoalNear(pos, Baritone.settings().followRadius.get()); + } + + + private boolean followable(Entity entity) { + if (entity == null) { + return false; + } + if (entity.isDead) { + return false; + } + if (entity.equals(player())) { + return false; + } + return world().loadedEntityList.contains(entity) || world().playerEntities.contains(entity); + } + + private void scanWorld() { + cache = Stream.of(world().loadedEntityList, world().playerEntities).flatMap(List::stream).filter(this::followable).filter(this.filter).distinct().collect(Collectors.toCollection(ArrayList::new)); + } + + @Override + public boolean isActive() { + if (filter == null) { + return false; + } + scanWorld(); + return !cache.isEmpty(); + } + + @Override + public void onLostControl() { + filter = null; + cache = null; + } + + @Override + public String displayName() { + return "Follow " + cache; + } + + @Override + public void follow(Predicate filter) { + this.filter = filter; + } + + @Override + public List following() { + return cache; + } + + @Override + public Predicate currentFilter() { + return filter; + } +} diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java new file mode 100644 index 00000000..0fb2abc0 --- /dev/null +++ b/src/main/java/baritone/process/GetToBlockProcess.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.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.process.IGetToBlockProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.utils.BaritoneProcessHelper; +import net.minecraft.block.Block; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { + private Block gettingTo; + private List knownLocations; + + private int tickCount = 0; + + public GetToBlockProcess(Baritone baritone) { + super(baritone, 2); + } + + @Override + public void getToBlock(Block block) { + gettingTo = block; + knownLocations = null; + rescan(new ArrayList<>()); + } + + @Override + public boolean isActive() { + return gettingTo != null; + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + if (knownLocations == null) { + rescan(new ArrayList<>()); + } + if (knownLocations.isEmpty()) { + logDirect("No known locations of " + gettingTo + ", canceling GetToBlock"); + if (isSafeToCancel) { + onLostControl(); + } + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + if (calcFailed) { + logDirect("Unable to find any path to " + gettingTo + ", canceling GetToBlock"); + if (isSafeToCancel) { + onLostControl(); + } + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.get(); + if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain + List current = new ArrayList<>(knownLocations); + Baritone.getExecutor().execute(() -> rescan(current)); + } + Goal goal = new GoalComposite(knownLocations.stream().map(GoalGetToBlock::new).toArray(Goal[]::new)); + if (goal.isInGoal(playerFeet())) { + onLostControl(); + } + return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); + } + + @Override + public void onLostControl() { + gettingTo = null; + knownLocations = null; + } + + @Override + public String displayName() { + return "Get To Block " + gettingTo; + } + + private void rescan(List known) { + knownLocations = MineProcess.searchWorld(Collections.singletonList(gettingTo), 64, baritone.getWorldProvider(), world(), known); + } +} \ No newline at end of file diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java new file mode 100644 index 00000000..c3a61426 --- /dev/null +++ b/src/main/java/baritone/process/MineProcess.java @@ -0,0 +1,305 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.*; +import baritone.api.process.IMineProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.RotationUtils; +import baritone.cache.CachedChunk; +import baritone.cache.ChunkPacker; +import baritone.cache.WorldProvider; +import baritone.cache.WorldScanner; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BaritoneProcessHelper; +import baritone.utils.BlockStateInterface; +import baritone.utils.Helper; +import net.minecraft.block.Block; +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; +import net.minecraft.world.chunk.EmptyChunk; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Mine blocks of a certain type + * + * @author leijurv + */ +public final class MineProcess extends BaritoneProcessHelper implements IMineProcess { + + private static final int ORE_LOCATIONS_COUNT = 64; + + private List mining; + private List knownOreLocations; + private BlockPos branchPoint; + private GoalRunAway branchPointRunaway; + private int desiredQuantity; + private int tickCount; + + public MineProcess(Baritone baritone) { + super(baritone, 0); + } + + @Override + public boolean isActive() { + return mining != 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 = player().inventory.mainInventory.stream().filter(stack -> item.equals(stack.getItem())).mapToInt(ItemStack::getCount).sum(); + System.out.println("Currently have " + curr + " " + item); + if (curr >= desiredQuantity) { + logDirect("Have " + curr + " " + item.getItemStackDisplayName(new ItemStack(item, 1))); + cancel(); + return null; + } + } + if (calcFailed) { + logDirect("Unable to find any path to " + mining + ", canceling Mine"); + cancel(); + return null; + } + int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.get(); + if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain + List curr = new ArrayList<>(knownOreLocations); + baritone.getExecutor().execute(() -> rescan(curr)); + } + if (Baritone.settings().legitMine.get()) { + addNearby(); + } + Goal goal = updateGoal(); + if (goal == null) { + // none in range + // maybe say something in chat? (ahem impact) + cancel(); + return null; + } + return new PathingCommand(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); + } + + @Override + public void onLostControl() { + mine(0, (Block[]) null); + } + + @Override + public String displayName() { + return "Mine " + mining; + } + + private Goal updateGoal() { + List locs = knownOreLocations; + if (!locs.isEmpty()) { + List locs2 = prune(new ArrayList<>(locs), mining, ORE_LOCATIONS_COUNT, world()); + // can't reassign locs, gotta make a new var locs2, because we use it in a lambda right here, and variables you use in a lambda must be effectively final + Goal goal = new GoalComposite(locs2.stream().map(loc -> coalesce(loc, locs2)).toArray(Goal[]::new)); + knownOreLocations = locs2; + return goal; + } + // we don't know any ore locations at the moment + if (!Baritone.settings().legitMine.get()) { + return null; + } + // only in non-Xray mode (aka legit mode) do we do this + int y = Baritone.settings().legitMineYLevel.get(); + if (branchPoint == null) { + /*if (!baritone.getPathingBehavior().isPathing() && playerFeet().y == y) { + // cool, path is over and we are at desired y + branchPoint = playerFeet(); + branchPointRunaway = null; + } else { + return new GoalYLevel(y); + }*/ + branchPoint = playerFeet(); + } + // TODO shaft mode, mine 1x1 shafts to either side + // TODO also, see if the GoalRunAway with maintain Y at 11 works even from the surface + if (branchPointRunaway == null) { + branchPointRunaway = new GoalRunAway(1, Optional.of(y), branchPoint) { + @Override + public boolean isInGoal(int x, int y, int z) { + return false; + } + }; + } + return branchPointRunaway; + } + + private void rescan(List already) { + if (mining == null) { + return; + } + if (Baritone.settings().legitMine.get()) { + return; + } + List locs = searchWorld(mining, ORE_LOCATIONS_COUNT, baritone.getWorldProvider(), world(), already); + locs.addAll(droppedItemsScan(mining, world())); + if (locs.isEmpty()) { + logDebug("No locations for " + mining + " known, cancelling"); + cancel(); + return; + } + knownOreLocations = locs; + } + + private static Goal coalesce(BlockPos loc, List locs) { + if (!Baritone.settings().forceInternalMining.get()) { + return new GoalTwoBlocks(loc); + } + + boolean upwardGoal = locs.contains(loc.up()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(loc.up()) == Blocks.AIR); + boolean downwardGoal = locs.contains(loc.down()) || (Baritone.settings().internalMiningAirException.get() && BlockStateInterface.getBlock(loc.up()) == Blocks.AIR); + if (upwardGoal) { + if (downwardGoal) { + return new GoalTwoBlocks(loc); + } else { + return new GoalBlock(loc); + } + } else { + if (downwardGoal) { + return new GoalBlock(loc.down()); + } else { + return new GoalTwoBlocks(loc); + } + } + } + + public static List droppedItemsScan(List mining, World world) { + if (!Baritone.settings().mineScanDroppedItems.get()) { + return new ArrayList<>(); + } + Set searchingFor = new HashSet<>(); + for (Block block : mining) { + Item drop = block.getItemDropped(block.getDefaultState(), new Random(), 0); + Item ore = Item.getItemFromBlock(block); + searchingFor.add(drop); + searchingFor.add(ore); + } + List ret = new ArrayList<>(); + for (Entity entity : world.loadedEntityList) { + if (entity instanceof EntityItem) { + EntityItem ei = (EntityItem) entity; + if (searchingFor.contains(ei.getItem().getItem())) { + ret.add(new BlockPos(entity)); + } + } + } + return ret; + } + + /*public static List searchWorld(List mining, int max, World world) { + + }*/ + public static List searchWorld(List mining, int max, WorldProvider provider, World world, List alreadyKnown) { + 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(provider.getCurrentWorld().getCachedWorld().getLocationsOf(ChunkPacker.blockToString(m), 1, 1)); + } else { + uninteresting.add(m); + } + } + //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(uninteresting, max, 10, 26)); + //System.out.println("Scan of loaded chunks took " + (System.currentTimeMillis() - before) + "ms"); + } + locs.addAll(alreadyKnown); + return prune(locs, mining, max, world); + } + + public void addNearby() { + knownOreLocations.addAll(droppedItemsScan(mining, world())); + BlockPos playerFeet = playerFeet(); + int searchDist = 4;//why four? idk + for (int x = playerFeet.getX() - searchDist; x <= playerFeet.getX() + searchDist; x++) { + for (int y = playerFeet.getY() - searchDist; y <= playerFeet.getY() + searchDist; y++) { + for (int z = playerFeet.getZ() - searchDist; z <= playerFeet.getZ() + searchDist; z++) { + BlockPos pos = new BlockPos(x, y, z); + if (mining.contains(BlockStateInterface.getBlock(pos)) && RotationUtils.reachable(player(), pos, playerController().getBlockReachDistance()).isPresent()) {//crucial to only add blocks we can see because otherwise this is an x-ray and it'll get caught + knownOreLocations.add(pos); + } + } + } + } + knownOreLocations = prune(knownOreLocations, mining, ORE_LOCATIONS_COUNT, world()); + } + + public static List prune(List locs2, List mining, int max, World world) { + List dropped = droppedItemsScan(mining, world); + List locs = locs2 + .stream() + .distinct() + + // remove any that are within loaded chunks that aren't actually what we want + .filter(pos -> world.getChunk(pos) instanceof EmptyChunk || mining.contains(BlockStateInterface.get(pos).getBlock()) || dropped.contains(pos)) + + // remove any that are implausible to mine (encased in bedrock, or touching lava) + .filter(MineProcess::plausibleToBreak) + + .sorted(Comparator.comparingDouble(Helper.HELPER.playerFeet()::distanceSq)) + .collect(Collectors.toList()); + + if (locs.size() > max) { + return locs.subList(0, max); + } + return locs; + } + + public static boolean plausibleToBreak(BlockPos pos) { + if (MovementHelper.avoidBreaking(new CalculationContext(), pos.getX(), pos.getY(), pos.getZ(), BlockStateInterface.get(pos))) { + return false; + } + // bedrock above and below makes it implausible, otherwise we're good + return !(BlockStateInterface.getBlock(pos.up()) == Blocks.BEDROCK && BlockStateInterface.getBlock(pos.down()) == 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)); + } + + @Override + public void mine(int quantity, Block... blocks) { + this.mining = blocks == null || blocks.length == 0 ? null : Arrays.asList(blocks); + this.desiredQuantity = quantity; + this.knownOreLocations = new ArrayList<>(); + this.branchPoint = null; + this.branchPointRunaway = null; + rescan(new ArrayList<>()); + } +} diff --git a/src/main/java/baritone/utils/BaritoneAutoTest.java b/src/main/java/baritone/utils/BaritoneAutoTest.java index faad6153..4aee4bd4 100644 --- a/src/main/java/baritone/utils/BaritoneAutoTest.java +++ b/src/main/java/baritone/utils/BaritoneAutoTest.java @@ -17,11 +17,11 @@ package baritone.utils; +import baritone.Baritone; 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.behavior.PathingBehavior; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.settings.GameSettings; @@ -35,8 +35,6 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { public static final BaritoneAutoTest INSTANCE = new BaritoneAutoTest(); - private BaritoneAutoTest() {} - public static final boolean ENABLE_AUTO_TEST = "true".equals(System.getenv("BARITONE_AUTO_TEST")); private static final long TEST_SEED = -928872506371745L; private static final BlockPos STARTING_POSITION = new BlockPos(0, 65, 0); @@ -107,8 +105,7 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { } // Setup Baritone's pathing goal and (if needed) begin pathing - PathingBehavior.INSTANCE.setGoal(GOAL); - PathingBehavior.INSTANCE.path(); + Baritone.INSTANCE.getCustomGoalProcess().setGoalAndPath(GOAL); // If we have reached our goal, print a message and safely close the game if (GOAL.isInGoal(playerFeet())) { @@ -123,4 +120,6 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper { } } } + + private BaritoneAutoTest() {} } diff --git a/src/main/java/baritone/utils/BaritoneProcessHelper.java b/src/main/java/baritone/utils/BaritoneProcessHelper.java new file mode 100644 index 00000000..d01e815d --- /dev/null +++ b/src/main/java/baritone/utils/BaritoneProcessHelper.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.utils; + +import baritone.Baritone; +import baritone.api.process.IBaritoneProcess; + +public abstract class BaritoneProcessHelper implements IBaritoneProcess, Helper { + public static final double DEFAULT_PRIORITY = 0; + + protected final Baritone baritone; + private final double priority; + + public BaritoneProcessHelper(Baritone baritone) { + this(baritone, DEFAULT_PRIORITY); + } + + public BaritoneProcessHelper(Baritone baritone, double priority) { + this.baritone = baritone; + this.priority = priority; + baritone.getPathingControlManager().registerProcess(this); + } + + @Override + public Baritone associatedWith() { + return baritone; + } + + @Override + 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 1bc4a44a..b1e9ae53 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -20,6 +20,7 @@ package baritone.utils; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; /** * @author Brady @@ -32,6 +33,7 @@ public final class BlockBreakHelper implements Helper { * between attempts, then we re-initialize the breaking process. */ private static BlockPos lastBlock; + private static boolean didBreakLastTick; private BlockBreakHelper() {} @@ -48,7 +50,23 @@ public final class BlockBreakHelper implements Helper { public static void stopBreakingBlock() { if (mc.playerController != null) { mc.playerController.resetBlockRemoving(); - } + } lastBlock = null; } + + public static boolean tick(boolean isLeftClick) { + RayTraceResult trace = mc.objectMouseOver; + boolean isBlockTrace = trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK; + + // If we're forcing left click, we're in a gui screen, and we're looking + // at a block, break the block without a direct game input manipulation. + if (mc.currentScreen != null && isLeftClick && isBlockTrace) { + tryBreakBlock(trace.getBlockPos(), trace.sideHit); + didBreakLastTick = true; + } else if (didBreakLastTick) { + stopBreakingBlock(); + didBreakLastTick = false; + } + return !didBreakLastTick && isLeftClick; + } } diff --git a/src/main/java/baritone/utils/BlockStateInterface.java b/src/main/java/baritone/utils/BlockStateInterface.java index 774d08e9..33529ce3 100644 --- a/src/main/java/baritone/utils/BlockStateInterface.java +++ b/src/main/java/baritone/utils/BlockStateInterface.java @@ -20,12 +20,12 @@ package baritone.utils; import baritone.Baritone; import baritone.cache.CachedRegion; import baritone.cache.WorldData; -import baritone.cache.WorldProvider; +import baritone.pathing.movement.CalculationContext; import net.minecraft.block.Block; -import net.minecraft.block.BlockLiquid; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; /** @@ -37,16 +37,29 @@ public class BlockStateInterface implements Helper { public static int numBlockStateLookups = 0; public static int numTimesChunkSucceeded = 0; - private static Chunk prev = null; - private static CachedRegion prevCached = null; + private final World world; + private final WorldData worldData; + private Chunk prev = null; + private CachedRegion prevCached = null; - private static IBlockState AIR = Blocks.AIR.getDefaultState(); + private static final IBlockState AIR = Blocks.AIR.getDefaultState(); - public static IBlockState get(BlockPos pos) { - return get(pos.getX(), pos.getY(), pos.getZ()); + public BlockStateInterface(World world, WorldData worldData) { + this.worldData = worldData; + this.world = world; } - public static IBlockState get(int x, int y, int z) { + public static Block getBlock(BlockPos pos) { // won't be called from the pathing thread because the pathing thread doesn't make a single blockpos pog + return get(pos).getBlock(); + } + + public static IBlockState get(BlockPos pos) { + return new CalculationContext().get(pos); // immense iq + // can't just do world().get because that doesn't work for out of bounds + // and toBreak and stuff fails when the movement is instantiated out of load range but it's not able to BlockStateInterface.get what it's going to walk on + } + + public IBlockState get0(int x, int y, int z) { numBlockStateLookups++; // Invalid vertical position if (y < 0 || y >= 256) { @@ -65,7 +78,7 @@ public class BlockStateInterface implements Helper { numTimesChunkSucceeded++; return cached.getBlockState(x, y, z); } - Chunk chunk = mc.world.getChunk(x >> 4, z >> 4); + Chunk chunk = world.getChunk(x >> 4, z >> 4); if (chunk.isLoaded()) { prev = chunk; return chunk.getBlockState(x, y, z); @@ -75,11 +88,10 @@ public class BlockStateInterface implements Helper { // except here, it's 512x512 tiles instead of 16x16, so even better repetition CachedRegion cached = prevCached; if (cached == null || cached.getX() != x >> 9 || cached.getZ() != z >> 9) { - WorldData world = WorldProvider.INSTANCE.getCurrentWorld(); - if (world == null) { + if (worldData == null) { return AIR; } - CachedRegion region = world.cache.getRegion(x >> 9, z >> 9); + CachedRegion region = worldData.cache.getRegion(x >> 9, z >> 9); if (region == null) { return AIR; } @@ -93,12 +105,12 @@ public class BlockStateInterface implements Helper { return type; } - public static boolean isLoaded(int x, int z) { + public boolean isLoaded(int x, int z) { Chunk prevChunk = prev; if (prevChunk != null && prevChunk.x == x >> 4 && prevChunk.z == z >> 4) { return true; } - prevChunk = mc.world.getChunk(x >> 4, z >> 4); + prevChunk = world.getChunk(x >> 4, z >> 4); if (prevChunk.isLoaded()) { prev = prevChunk; return true; @@ -107,71 +119,14 @@ public class BlockStateInterface implements Helper { if (prevRegion != null && prevRegion.getX() == x >> 9 && prevRegion.getZ() == z >> 9) { return prevRegion.isCached(x & 511, z & 511); } - WorldData world = WorldProvider.INSTANCE.getCurrentWorld(); - if (world == null) { + if (worldData == null) { return false; } - prevRegion = world.cache.getRegion(x >> 9, z >> 9); + prevRegion = worldData.cache.getRegion(x >> 9, z >> 9); if (prevRegion == null) { return false; } prevCached = prevRegion; return prevRegion.isCached(x & 511, z & 511); } - - public static void clearCachedChunk() { - prev = null; - prevCached = null; - } - - public static Block getBlock(BlockPos pos) { - return get(pos).getBlock(); - } - - public static Block getBlock(int x, int y, int z) { - return get(x, y, z).getBlock(); - } - - /** - * Returns whether or not the specified block is - * water, regardless of whether or not it is flowing. - * - * @param b The block - * @return Whether or not the block is water - */ - public static boolean isWater(Block b) { - return b == Blocks.FLOWING_WATER || b == Blocks.WATER; - } - - /** - * Returns whether or not the block at the specified pos is - * water, regardless of whether or not it is flowing. - * - * @param bp The block pos - * @return Whether or not the block is water - */ - public static boolean isWater(BlockPos bp) { - return isWater(BlockStateInterface.getBlock(bp)); - } - - public static boolean isLava(Block b) { - return b == Blocks.FLOWING_LAVA || b == Blocks.LAVA; - } - - /** - * Returns whether or not the specified pos has a liquid - * - * @param p The pos - * @return Whether or not the block is a liquid - */ - public static boolean isLiquid(BlockPos p) { - return BlockStateInterface.getBlock(p) instanceof BlockLiquid; - } - - public static boolean isFlowing(IBlockState state) { - // Will be IFluidState in 1.13 - return state.getBlock() instanceof BlockLiquid - && state.getPropertyKeys().contains(BlockLiquid.LEVEL) - && state.getValue(BlockLiquid.LEVEL) != 0; - } } diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index ee58fd60..2f6bef6c 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -23,15 +23,17 @@ 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.RayTraceUtils; +import baritone.api.utils.SettingsUtil; import baritone.behavior.Behavior; -import baritone.behavior.FollowBehavior; -import baritone.behavior.MineBehavior; import baritone.behavior.PathingBehavior; import baritone.cache.ChunkPacker; import baritone.cache.Waypoint; -import baritone.cache.WorldProvider; import baritone.pathing.calc.AbstractNodeCostSearch; -import baritone.pathing.movement.*; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.Moves; +import baritone.process.CustomGoalProcess; import net.minecraft.block.Block; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.entity.Entity; @@ -45,46 +47,80 @@ import java.util.stream.Stream; public class ExampleBaritoneControl extends Behavior implements Helper { - public static ExampleBaritoneControl INSTANCE = new ExampleBaritoneControl(); + 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 "; - private ExampleBaritoneControl() { - - } - - public void initAndRegister() { - Baritone.INSTANCE.registerBehavior(this); + public ExampleBaritoneControl(Baritone baritone) { + super(baritone); } @Override public void onSendChatMessage(ChatEvent event) { - if (!Baritone.settings().chatControl.get()) { - if (!Baritone.settings().removePrefix.get()) { - return; - } + if (!Baritone.settings().chatControl.get() && !Baritone.settings().removePrefix.get()) { + return; } - String msg = event.getMessage().toLowerCase(Locale.US); + 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; - event.cancel(); logDirect("Toggled " + setting.getName() + " to " + setting.value); - return; + SettingsUtil.save(Baritone.settings()); + return true; } } if (msg.equals("baritone") || msg.equals("settings")) { for (Settings.Setting setting : Baritone.settings().allSettings) { logDirect(setting.toString()); } - event.cancel(); - return; + 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(" "); @@ -106,24 +142,21 @@ public class ExampleBaritoneControl extends Behavior implements Helper { } } catch (NumberFormatException e) { logDirect("Unable to parse " + data[1]); - event.cancel(); - return; + return true; } + SettingsUtil.save(Baritone.settings()); logDirect(setting.toString()); - event.cancel(); - return; + return true; } } } if (Baritone.settings().byLowerName.containsKey(msg)) { Settings.Setting setting = Baritone.settings().byLowerName.get(msg); logDirect(setting.toString()); - event.cancel(); - return; + return true; } if (msg.startsWith("goal")) { - event.cancel(); String[] params = msg.substring(4).trim().split(" "); if (params[0].equals("")) { params = new String[]{}; @@ -149,30 +182,27 @@ public class ExampleBaritoneControl extends Behavior implements Helper { break; default: logDirect("unable to understand lol"); - return; + return true; } } catch (NumberFormatException ex) { logDirect("unable to parse integer " + ex); - return; + return true; } - PathingBehavior.INSTANCE.setGoal(goal); + customGoalProcess.setGoal(goal); logDirect("Goal: " + goal); - return; + return true; } if (msg.equals("path")) { - if (!PathingBehavior.INSTANCE.path()) { - if (PathingBehavior.INSTANCE.getGoal() == null) { - logDirect("No goal."); - } else { - if (PathingBehavior.INSTANCE.getGoal().isInGoal(playerFeet())) { - logDirect("Already in goal"); - } else { - logDirect("Currently executing a path. Please cancel it first."); - } - } + if (pathingBehavior.getGoal() == null) { + logDirect("No goal."); + } else if (pathingBehavior.getGoal().isInGoal(playerFeet())) { + logDirect("Already in goal"); + } else if (pathingBehavior.isPathing()) { + logDirect("Currently executing a path. Please cancel it first."); + } else { + customGoalProcess.setGoalAndPath(pathingBehavior.getGoal()); } - event.cancel(); - return; + return true; } if (msg.equals("repack") || msg.equals("rescan")) { ChunkProviderClient cli = world().getChunkProvider(); @@ -184,46 +214,36 @@ public class ExampleBaritoneControl extends Behavior implements Helper { Chunk chunk = cli.getLoadedChunk(x, z); if (chunk != null) { count++; - WorldProvider.INSTANCE.getCurrentWorld().getCachedWorld().queueForPacking(chunk); + baritone.getWorldProvider().getCurrentWorld().getCachedWorld().queueForPacking(chunk); } } } logDirect("Queued " + count + " chunks for repacking"); - event.cancel(); - return; + return true; } if (msg.equals("axis")) { - PathingBehavior.INSTANCE.setGoal(new GoalAxis()); - PathingBehavior.INSTANCE.path(); - event.cancel(); - return; + customGoalProcess.setGoalAndPath(new GoalAxis()); + return true; } - if (msg.equals("cancel")) { - MineBehavior.INSTANCE.cancel(); - FollowBehavior.INSTANCE.cancel(); - PathingBehavior.INSTANCE.cancel(); - event.cancel(); + if (msg.equals("cancel") || msg.equals("stop")) { + pathingBehavior.cancelEverything(); logDirect("ok canceled"); - return; + return true; } if (msg.equals("forcecancel")) { - MineBehavior.INSTANCE.cancel(); - FollowBehavior.INSTANCE.cancel(); - PathingBehavior.INSTANCE.cancel(); + pathingBehavior.cancelEverything(); AbstractNodeCostSearch.forceCancel(); - PathingBehavior.INSTANCE.forceCancel(); - event.cancel(); + pathingBehavior.forceCancel(); logDirect("ok force canceled"); - return; + return true; } if (msg.equals("gc")) { System.gc(); - event.cancel(); logDirect("Called System.gc();"); - return; + return true; } if (msg.equals("invert")) { - Goal goal = PathingBehavior.INSTANCE.getGoal(); + Goal goal = pathingBehavior.getGoal(); BlockPos runAwayFrom; if (goal instanceof GoalXZ) { runAwayFrom = new BlockPos(((GoalXZ) goal).getX(), 0, ((GoalXZ) goal).getZ()); @@ -234,58 +254,54 @@ public class ExampleBaritoneControl extends Behavior implements Helper { logDirect("Inverting goal of player feet"); runAwayFrom = playerFeet(); } - PathingBehavior.INSTANCE.setGoal(new GoalRunAway(1, runAwayFrom) { + customGoalProcess.setGoalAndPath(new GoalRunAway(1, runAwayFrom) { @Override - public boolean isInGoal(BlockPos pos) { + public boolean isInGoal(int x, int y, int z) { return false; } }); - if (!PathingBehavior.INSTANCE.path()) { - logDirect("Currently executing a path. Please cancel it first."); - } - event.cancel(); - return; + 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 = MovementHelper.whatEntityAmILookingAt(); + toFollow = RayTraceUtils.getSelectedEntity(); } else { for (EntityPlayer pl : world().playerEntities) { String theirName = pl.getName().trim().toLowerCase(); - if (!theirName.equals(player().getName().trim().toLowerCase())) { // don't follow ourselves lol - if (theirName.contains(name) || name.contains(theirName)) { - toFollow = Optional.of(pl); - } + if (!theirName.equals(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"); - event.cancel(); - return; + return true; } - FollowBehavior.INSTANCE.follow(toFollow.get()); + Entity effectivelyFinal = toFollow.get(); + baritone.getFollowProcess().follow(x -> effectivelyFinal.equals(x)); logDirect("Following " + toFollow.get()); - event.cancel(); - return; + return true; } if (msg.equals("reloadall")) { - WorldProvider.INSTANCE.getCurrentWorld().getCachedWorld().reloadAllFromDisk(); + baritone.getWorldProvider().getCurrentWorld().getCachedWorld().reloadAllFromDisk(); logDirect("ok"); - event.cancel(); - return; + return true; } if (msg.equals("saveall")) { - WorldProvider.INSTANCE.getCurrentWorld().getCachedWorld().save(); + baritone.getWorldProvider().getCurrentWorld().getCachedWorld().save(); logDirect("ok"); - event.cancel(); - return; + return true; } if (msg.startsWith("find")) { String blockType = msg.substring(4).trim(); - LinkedList locs = WorldProvider.INSTANCE.getCurrentWorld().getCachedWorld().getLocationsOf(blockType, 1, 4); + LinkedList locs = baritone.getWorldProvider().getCurrentWorld().getCachedWorld().getLocationsOf(blockType, 1, 4); logDirect("Have " + locs.size() + " locations"); for (BlockPos pos : locs) { Block actually = BlockStateInterface.get(pos).getBlock(); @@ -293,8 +309,7 @@ public class ExampleBaritoneControl extends Behavior implements Helper { System.out.println("Was looking for " + blockType + " but actually found " + actually + " " + ChunkPacker.blockToString(actually)); } } - event.cancel(); - return; + return true; } if (msg.startsWith("mine")) { String[] blockTypes = msg.substring(4).trim().split(" "); @@ -302,34 +317,30 @@ public class ExampleBaritoneControl extends Behavior implements Helper { int quantity = Integer.parseInt(blockTypes[1]); Block block = ChunkPacker.stringToBlock(blockTypes[0]); Objects.requireNonNull(block); - MineBehavior.INSTANCE.mine(quantity, block); + baritone.getMineProcess().mine(quantity, block); logDirect("Will mine " + quantity + " " + blockTypes[0]); - event.cancel(); - return; + return true; } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) {} for (String s : blockTypes) { if (ChunkPacker.stringToBlock(s) == null) { logDirect(s + " isn't a valid block name"); - event.cancel(); - return; + return true; } } - MineBehavior.INSTANCE.mine(0, blockTypes); + baritone.getMineProcess().mineByName(0, blockTypes); logDirect("Started mining blocks of type " + Arrays.toString(blockTypes)); - event.cancel(); - return; + return true; } if (msg.startsWith("thisway")) { try { Goal goal = GoalXZ.fromDirection(playerFeetAsVec(), player().rotationYaw, Double.parseDouble(msg.substring(7).trim())); - PathingBehavior.INSTANCE.setGoal(goal); + customGoalProcess.setGoal(goal); logDirect("Goal: " + goal); } catch (NumberFormatException ex) { logDirect("Error unable to parse '" + msg.substring(7).trim() + "' to a double."); } - event.cancel(); - return; + return true; } if (msg.startsWith("list") || msg.startsWith("get ") || msg.startsWith("show")) { String waypointType = msg.substring(4).trim(); @@ -340,10 +351,9 @@ public class ExampleBaritoneControl extends Behavior implements Helper { Waypoint.Tag tag = Waypoint.Tag.fromString(waypointType); if (tag == null) { logDirect("Not a valid tag. Tags are: " + Arrays.asList(Waypoint.Tag.values()).toString().toLowerCase()); - event.cancel(); - return; + return true; } - Set waypoints = WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().getByTag(tag); + 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)); @@ -351,11 +361,9 @@ public class ExampleBaritoneControl extends Behavior implements Helper { for (IWaypoint waypoint : sorted) { logDirect(waypoint.toString()); } - event.cancel(); - return; + return true; } if (msg.startsWith("save")) { - event.cancel(); String name = msg.substring(4).trim(); BlockPos pos = playerFeet(); if (name.contains(" ")) { @@ -363,19 +371,19 @@ public class ExampleBaritoneControl extends Behavior implements Helper { String[] parts = name.split(" "); if (parts.length != 4) { logDirect("Unable to parse, expected four things"); - return; + 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; + return true; } name = parts[0]; } - WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().addWaypoint(new Waypoint(name, Waypoint.Tag.USER, pos)); - logDirect("Saved user defined position " + pos + " under name '" + name + "'. Say 'goto user' to set goal, say 'list user' to list."); - return; + 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(); @@ -389,93 +397,78 @@ public class ExampleBaritoneControl extends Behavior implements Helper { String mining = waypointType; Block block = ChunkPacker.stringToBlock(mining); //logDirect("Not a valid tag. Tags are: " + Arrays.asList(Waypoint.Tag.values()).toString().toLowerCase()); - event.cancel(); if (block == null) { - waypoint = WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().getAllWaypoints().stream().filter(w -> w.getName().equalsIgnoreCase(mining)).max(Comparator.comparingLong(IWaypoint::getCreationTimestamp)).orElse(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; + return true; } } else { - List locs = MineBehavior.INSTANCE.scanFor(Collections.singletonList(block), 64); - if (locs.isEmpty()) { - logDirect("No locations for " + mining + " known, cancelling"); - return; - } - PathingBehavior.INSTANCE.setGoal(new GoalComposite(locs.stream().map(GoalGetToBlock::new).toArray(Goal[]::new))); - PathingBehavior.INSTANCE.path(); - return; + baritone.getGetToBlockProcess().getToBlock(block); + return true; } } else { - waypoint = WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().getMostRecentByTag(tag); + waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(tag); if (waypoint == null) { logDirect("None saved for tag " + tag); - event.cancel(); - return; + return true; } } Goal goal = new GoalBlock(waypoint.getLocation()); - PathingBehavior.INSTANCE.setGoal(goal); - if (!PathingBehavior.INSTANCE.path()) { - if (!goal.isInGoal(playerFeet())) { - logDirect("Currently executing a path. Please cancel it first."); - } - } - event.cancel(); - return; + customGoalProcess.setGoalAndPath(goal); + return true; } if (msg.equals("spawn") || msg.equals("bed")) { - IWaypoint waypoint = WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().getMostRecentByTag(Waypoint.Tag.BED); + IWaypoint waypoint = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getMostRecentByTag(Waypoint.Tag.BED); if (waypoint == null) { BlockPos spawnPoint = 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); - PathingBehavior.INSTANCE.setGoal(goal); + customGoalProcess.setGoalAndPath(goal); } else { - Goal goal = new GoalBlock(waypoint.getLocation()); - PathingBehavior.INSTANCE.setGoal(goal); + Goal goal = new GoalGetToBlock(waypoint.getLocation()); + customGoalProcess.setGoalAndPath(goal); logDirect("Set goal to most recent bed " + goal); } - event.cancel(); - return; + return true; } if (msg.equals("sethome")) { - WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("", Waypoint.Tag.HOME, playerFeet())); + baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("", Waypoint.Tag.HOME, playerFeet())); logDirect("Saved. Say home to set goal."); - event.cancel(); - return; + return true; } if (msg.equals("home")) { - IWaypoint waypoint = WorldProvider.INSTANCE.getCurrentWorld().getWaypoints().getMostRecentByTag(Waypoint.Tag.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()); - PathingBehavior.INSTANCE.setGoal(goal); - PathingBehavior.INSTANCE.path(); + customGoalProcess.setGoalAndPath(goal); logDirect("Going to saved home " + goal); } - event.cancel(); - return; + return true; } if (msg.equals("costs")) { - List moves = Stream.of(Moves.values()).map(x -> x.apply0(playerFeet())).collect(Collectors.toCollection(ArrayList::new)); + List moves = Stream.of(Moves.values()).map(x -> x.apply0(new CalculationContext(), playerFeet())).collect(Collectors.toCollection(ArrayList::new)); while (moves.contains(null)) { moves.remove(null); } - moves.sort(Comparator.comparingDouble(movement -> movement.getCost(new CalculationContext()))); + moves.sort(Comparator.comparingDouble(Movement::getCost)); for (Movement move : moves) { String[] parts = move.getClass().toString().split("\\."); - double cost = move.getCost(new CalculationContext()); + 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); } - event.cancel(); - return; + return true; } + if (msg.equals("damn")) { + logDirect("daniel"); + } + return false; } } diff --git a/src/main/java/baritone/utils/Helper.java b/src/main/java/baritone/utils/Helper.java index a0ffdb96..31b3fc10 100755 --- a/src/main/java/baritone/utils/Helper.java +++ b/src/main/java/baritone/utils/Helper.java @@ -18,11 +18,12 @@ package baritone.utils; import baritone.Baritone; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Rotation; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.BlockSlab; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.ITextComponent; @@ -51,10 +52,23 @@ public interface Helper { Minecraft mc = Minecraft.getMinecraft(); default EntityPlayerSP player() { + if (!mc.isCallingFromMinecraftThread()) { + throw new IllegalStateException("h00000000"); + } return mc.player; } + default PlayerControllerMP playerController() { // idk + if (!mc.isCallingFromMinecraftThread()) { + throw new IllegalStateException("h00000000"); + } + return mc.playerController; + } + default WorldClient world() { + if (!mc.isCallingFromMinecraftThread()) { + throw new IllegalStateException("h00000000"); + } return mc.world; } @@ -86,8 +100,8 @@ public interface Helper { */ default void logDebug(String message) { if (!Baritone.settings().chatDebug.get()) { - System.out.println("Suppressed debug message:"); - System.out.println(message); + //System.out.println("Suppressed debug message:"); + //System.out.println(message); return; } logDirect(message); diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 9cf7d1da..fc902cf1 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -17,8 +17,13 @@ package baritone.utils; +import baritone.Baritone; +import baritone.api.event.events.TickEvent; +import baritone.behavior.Behavior; import net.minecraft.client.settings.KeyBinding; +import org.lwjgl.input.Keyboard; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -30,15 +35,15 @@ import java.util.Map; * @author Brady * @since 7/31/2018 11:20 PM */ -public final class InputOverrideHandler implements Helper { +public final class InputOverrideHandler extends Behavior implements Helper { /** - * Maps keybinds to whether or not we are forcing their state down. + * Maps inputs to whether or not we are forcing their state down. */ - private final Map inputForceStateMap = new HashMap<>(); + private final Map inputForceStateMap = new HashMap<>(); - public final void clearAllKeys() { - inputForceStateMap.clear(); + public InputOverrideHandler(Baritone baritone) { + super(baritone); } /** @@ -48,7 +53,17 @@ public final class InputOverrideHandler implements Helper { * @return Whether or not it is being forced down */ public final boolean isInputForcedDown(KeyBinding key) { - return inputForceStateMap.getOrDefault(key, false); + return isInputForcedDown(Input.getInputForBind(key)); + } + + /** + * 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 + */ + public final boolean isInputForcedDown(Input input) { + return input == null ? false : this.inputForceStateMap.getOrDefault(input, false); } /** @@ -58,11 +73,47 @@ public final class InputOverrideHandler implements Helper { * @param forced Whether or not the state is being forced */ public final void setInputForceState(Input input, boolean forced) { - inputForceStateMap.put(input.getKeyBinding(), forced); + this.inputForceStateMap.put(input, forced); } /** - * An {@link Enum} representing the possible inputs that we may want to force. + * Clears the override state for all keys + */ + public final void clearAllKeys() { + this.inputForceStateMap.clear(); + } + + @Override + public final void onProcessKeyBinds() { + // Simulate the key being held down this tick + for (InputOverrideHandler.Input input : Input.values()) { + KeyBinding keyBinding = input.getKeyBinding(); + + if (isInputForcedDown(keyBinding) && !keyBinding.isKeyDown()) { + int keyCode = keyBinding.getKeyCode(); + + if (keyCode < Keyboard.KEYBOARD_SIZE) { + KeyBinding.onTick(keyCode < 0 ? keyCode + 100 : keyCode); + } + } + } + } + + @Override + public final void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.OUT) { + return; + } + if (Baritone.settings().leftClickWorkaround.get()) { + boolean stillClick = BlockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT.keyBinding)); + setInputForceState(Input.CLICK_LEFT, stillClick); + } + } + + /** + * An {@link Enum} representing the inputs that control the player's + * behavior. This includes moving, interacting with blocks, jumping, + * sneaking, and sprinting. */ public enum Input { @@ -111,6 +162,11 @@ public final class InputOverrideHandler implements Helper { */ SPRINT(mc.gameSettings.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. */ @@ -126,5 +182,15 @@ public final class InputOverrideHandler implements Helper { 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)); + } } } diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index a66bc0ae..bf73149e 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -18,13 +18,17 @@ package baritone.utils; import baritone.Baritone; +import baritone.api.event.events.RenderEvent; +import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalTwoBlocks; import baritone.api.pathing.goals.GoalXZ; -import baritone.pathing.path.IPath; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.interfaces.IGoalRenderPos; -import baritone.utils.pathing.BetterBlockPos; +import baritone.behavior.PathingBehavior; +import baritone.pathing.calc.AbstractNodeCostSearch; +import baritone.pathing.path.PathExecutor; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; @@ -40,6 +44,7 @@ import net.minecraft.util.math.MathHelper; import java.awt.*; import java.util.Collection; +import java.util.Collections; import java.util.List; import static org.lwjgl.opengl.GL11.*; @@ -49,12 +54,68 @@ import static org.lwjgl.opengl.GL11.*; * @since 8/9/2018 4:39 PM */ public final class PathRenderer implements Helper { - + private static final Tessellator TESSELLATOR = Tessellator.getInstance(); private static final BufferBuilder BUFFER = TESSELLATOR.getBuffer(); 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(); + EntityPlayerSP player = mc.player; + if (goal != null && Baritone.settings().renderGoal.value) { + drawLitDankGoalBox(player, goal, partialTicks, Baritone.settings().colorGoalBox.get()); + } + if (!Baritone.settings().renderPath.get()) { + 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 + // TODO is this enough, or do we need to acquire a lock here? + // TODO benchmark synchronized in render loop + + // 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, player, partialTicks, Baritone.settings().colorCurrentPath.get(), Baritone.settings().fadePath.get(), 10, 20); + } + if (next != null && next.getPath() != null) { + drawPath(next.getPath(), 0, player, partialTicks, Baritone.settings().colorNextPath.get(), Baritone.settings().fadePath.get(), 10, 20); + } + + //long split = System.nanoTime(); + if (current != null) { + drawManySelectionBoxes(player, current.toBreak(), partialTicks, Baritone.settings().colorBlocksToBreak.get()); + drawManySelectionBoxes(player, current.toPlace(), partialTicks, Baritone.settings().colorBlocksToPlace.get()); + drawManySelectionBoxes(player, current.toWalkInto(), partialTicks, Baritone.settings().colorBlocksToWalkInto.get()); + } + + // If there is a path calculation currently running, render the path calculation process + AbstractNodeCostSearch.getCurrentlyRunning().ifPresent(currentlyRunning -> { + currentlyRunning.bestPathSoFar().ifPresent(p -> { + drawPath(p, 0, player, partialTicks, Baritone.settings().colorBestPathSoFar.get(), Baritone.settings().fadePath.get(), 10, 20); + }); + currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { + + drawPath(mr, 0, player, partialTicks, Baritone.settings().colorMostRecentConsidered.get(), Baritone.settings().fadePath.get(), 10, 20); + drawManySelectionBoxes(player, Collections.singletonList(mr.getDest()), partialTicks, Baritone.settings().colorMostRecentConsidered.get()); + }); + }); + //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, EntityPlayerSP 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); @@ -62,28 +123,33 @@ public final class PathRenderer implements Helper { 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(); int fadeStart = fadeStart0 + startIndex; int fadeEnd = fadeEnd0 + startIndex; for (int i = startIndex; i < positions.size() - 1; i = next) { - BlockPos start = positions.get(i); + BetterBlockPos start = positions.get(i); next = i + 1; - BlockPos end = positions.get(next); + BetterBlockPos end = positions.get(next); - BlockPos direction = Utils.diff(start, end); - while (next + 1 < positions.size() && (!fadeOut || next + 1 < fadeStart) && direction.equals(Utils.diff(end, positions.get(next + 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.getX(); - double y1 = start.getY(); - double z1 = start.getZ(); - double x2 = end.getX(); - double y2 = end.getY(); - double z2 = end.getZ(); + 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) { float alpha; @@ -100,6 +166,9 @@ public final class PathRenderer implements Helper { drawLine(player, x1, y1, z1, x2, y2, z2, partialTicks); tessellator.draw(); } + if (Baritone.settings().renderPathIgnoreDepth.get()) { + GlStateManager.enableDepth(); + } //GlStateManager.color(0.0f, 0.0f, 0.0f, 0.4f); GlStateManager.depthMask(true); GlStateManager.enableTexture2D(); @@ -125,6 +194,11 @@ public final class PathRenderer implements Helper { GlStateManager.glLineWidth(Baritone.settings().pathRenderLineWidthPixels.get()); GlStateManager.disableTexture2D(); GlStateManager.depthMask(false); + + if (Baritone.settings().renderSelectionBoxesIgnoreDepth.get()) { + GlStateManager.disableDepth(); + } + float expand = 0.002F; //BlockPos blockpos = movingObjectPositionIn.getBlockPos(); @@ -166,6 +240,10 @@ public final class PathRenderer implements Helper { TESSELLATOR.draw(); }); + if (Baritone.settings().renderSelectionBoxesIgnoreDepth.get()) { + GlStateManager.enableDepth(); + } + GlStateManager.depthMask(true); GlStateManager.enableTexture2D(); GlStateManager.disableBlend(); @@ -230,26 +308,12 @@ public final class PathRenderer implements Helper { GlStateManager.glLineWidth(Baritone.settings().goalRenderLineWidthPixels.get()); GlStateManager.disableTexture2D(); GlStateManager.depthMask(false); - - if (y1 != 0) { - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, y1, minZ).endVertex(); - BUFFER.pos(maxX, y1, minZ).endVertex(); - BUFFER.pos(maxX, y1, maxZ).endVertex(); - BUFFER.pos(minX, y1, maxZ).endVertex(); - BUFFER.pos(minX, y1, minZ).endVertex(); - TESSELLATOR.draw(); + if (Baritone.settings().renderGoalIgnoreDepth.get()) { + GlStateManager.disableDepth(); } - if (y2 != 0) { - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(minX, y2, minZ).endVertex(); - BUFFER.pos(maxX, y2, minZ).endVertex(); - BUFFER.pos(maxX, y2, maxZ).endVertex(); - BUFFER.pos(minX, y2, maxZ).endVertex(); - BUFFER.pos(minX, y2, minZ).endVertex(); - TESSELLATOR.draw(); - } + renderHorizontalQuad(minX, maxX, minZ, maxZ, y1); + renderHorizontalQuad(minX, maxX, minZ, maxZ, y2); BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); BUFFER.pos(minX, minY, minZ).endVertex(); @@ -262,9 +326,22 @@ public final class PathRenderer implements Helper { BUFFER.pos(minX, maxY, maxZ).endVertex(); TESSELLATOR.draw(); - + if (Baritone.settings().renderGoalIgnoreDepth.get()) { + GlStateManager.enableDepth(); + } GlStateManager.depthMask(true); GlStateManager.enableTexture2D(); GlStateManager.disableBlend(); } + + 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(); + } + } } diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java new file mode 100644 index 00000000..9ea193a1 --- /dev/null +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -0,0 +1,194 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.event.events.TickEvent; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.IBaritoneProcess; +import baritone.api.process.PathingCommand; +import baritone.behavior.PathingBehavior; +import baritone.pathing.calc.AbstractNodeCostSearch; +import baritone.pathing.path.PathExecutor; +import net.minecraft.util.math.BlockPos; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class PathingControlManager { + private final Baritone baritone; + private final HashSet processes; // unGh + private IBaritoneProcess inControlLastTick; + private IBaritoneProcess inControlThisTick; + private PathingCommand command; + + public PathingControlManager(Baritone baritone) { + this.baritone = baritone; + this.processes = new HashSet<>(); + baritone.registerEventListener(new AbstractGameEventListener() { // needs to be after all behavior ticks + @Override + public void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.OUT) { + return; + } + postTick(); + } + }); + } + + public void registerProcess(IBaritoneProcess process) { + process.onLostControl(); // make sure it's reset + processes.add(process); + } + + public void cancelEverything() { + for (IBaritoneProcess proc : processes) { + proc.onLostControl(); + if (proc.isActive() && !proc.isTemporary()) { // it's okay for a temporary thing (like combat pause) to maintain control even if you say to cancel + // but not for a non temporary thing + throw new IllegalStateException(proc.displayName()); + } + } + } + + public IBaritoneProcess inControlThisTick() { + return inControlThisTick; + } + + public void preTick() { + inControlLastTick = inControlThisTick; + command = doTheStuff(); + if (command == null) { + return; + } + PathingBehavior p = baritone.getPathingBehavior(); + switch (command.commandType) { + case REQUEST_PAUSE: + p.requestPause(); + break; + case CANCEL_AND_SET_GOAL: + p.secretInternalSetGoal(command.goal); + p.cancelSegmentIfSafe(); + break; + case FORCE_REVALIDATE_GOAL_AND_PATH: + if (!p.isPathing() && !AbstractNodeCostSearch.getCurrentlyRunning().isPresent()) { + p.secretInternalSetGoalAndPath(command.goal); + } + break; + case REVALIDATE_GOAL_AND_PATH: + if (!p.isPathing() && !AbstractNodeCostSearch.getCurrentlyRunning().isPresent()) { + p.secretInternalSetGoalAndPath(command.goal); + } + break; + case SET_GOAL_AND_PATH: + // now this i can do + if (command.goal != null) { + baritone.getPathingBehavior().secretInternalSetGoalAndPath(command.goal); + } + break; + default: + throw new IllegalStateException(); + } + } + + public 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 + // "it would suck" means it would actually execute a path every other tick + if (command == null) { + return; + } + PathingBehavior p = baritone.getPathingBehavior(); + switch (command.commandType) { + case FORCE_REVALIDATE_GOAL_AND_PATH: + if (command.goal == null || forceRevalidate(command.goal) || revalidateGoal(command.goal)) { + // pwnage + p.softCancelIfSafe(); + } + p.secretInternalSetGoalAndPath(command.goal); + break; + case REVALIDATE_GOAL_AND_PATH: + if (Baritone.settings().cancelOnGoalInvalidation.get() && (command.goal == null || revalidateGoal(command.goal))) { + p.softCancelIfSafe(); + } + p.secretInternalSetGoalAndPath(command.goal); + break; + default: + } + } + + public boolean forceRevalidate(Goal newGoal) { + PathExecutor current = baritone.getPathingBehavior().getCurrent(); + if (current != null) { + if (newGoal.isInGoal(current.getPath().getDest())) { + return false; + } + return !newGoal.toString().equals(current.getPath().getGoal().toString()); + } + return false; + } + + public boolean revalidateGoal(Goal newGoal) { + PathExecutor current = baritone.getPathingBehavior().getCurrent(); + if (current != null) { + Goal intended = current.getPath().getGoal(); + BlockPos end = current.getPath().getDest(); + if (intended.isInGoal(end) && !newGoal.isInGoal(end)) { + // this path used to end in the goal + // but the goal has changed, so there's no reason to continue... + return true; + } + } + return false; + } + + + public PathingCommand doTheStuff() { + List inContention = processes.stream().filter(IBaritoneProcess::isActive).sorted(Comparator.comparingDouble(IBaritoneProcess::priority)).collect(Collectors.toList()); + boolean found = false; + boolean cancelOthers = false; + PathingCommand exec = null; + for (int i = inContention.size() - 1; i >= 0; i--) { // truly a gamer moment + IBaritoneProcess proc = inContention.get(i); + if (found) { + if (cancelOthers) { + proc.onLostControl(); + } + } else { + exec = proc.onTick(Objects.equals(proc, inControlLastTick) && baritone.getPathingBehavior().calcFailedLastTick(), baritone.getPathingBehavior().isSafeToCancel()); + if (exec == null) { + if (proc.isActive()) { + throw new IllegalStateException(proc.displayName()); + } + proc.onLostControl(); + continue; + } + //System.out.println("Executing command " + exec.commandType + " " + exec.goal + " from " + proc.displayName()); + inControlThisTick = proc; + found = true; + cancelOthers = !proc.isTemporary(); + } + } + return exec; + } +} diff --git a/src/main/java/baritone/utils/RayTraceUtils.java b/src/main/java/baritone/utils/RayTraceUtils.java deleted file mode 100644 index b313f1fe..00000000 --- a/src/main/java/baritone/utils/RayTraceUtils.java +++ /dev/null @@ -1,83 +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.api.utils.Rotation; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; - -import static baritone.behavior.LookBehaviorUtils.calcVec3dFromRotation; - -/** - * @author Brady - * @since 8/25/2018 - */ -public final class RayTraceUtils implements Helper { - - private RayTraceUtils() {} - - /** - * Simulates a "vanilla" raytrace. A RayTraceResult returned by this method - * will be that of the next render pass given that the local player's yaw and - * pitch match the specified yaw and pitch values. This is particularly useful - * when you would like to simulate a "legit" raytrace with certainty that the only - * thing to achieve the desired outcome (whether it is hitting and entity or placing - * a block) can be done just by modifying user input. - * - * @param yaw The yaw to raytrace with - * @param pitch The pitch to raytrace with - * @return The calculated raytrace result - */ - public static RayTraceResult simulateRayTrace(float yaw, float pitch) { - RayTraceResult oldTrace = mc.objectMouseOver; - float oldYaw = mc.player.rotationYaw; - float oldPitch = mc.player.rotationPitch; - - mc.player.rotationYaw = yaw; - mc.player.rotationPitch = pitch; - - mc.entityRenderer.getMouseOver(1.0F); - RayTraceResult result = mc.objectMouseOver; - mc.objectMouseOver = oldTrace; - - mc.player.rotationYaw = oldYaw; - mc.player.rotationPitch = oldPitch; - - return result; - } - - /** - * Performs a block raytrace with the specified rotations. This should only be used when - * any entity collisions can be ignored, because this method will not recognize if an - * entity is in the way or not. The local player's block reach distance will be used. - * - * @param rotation The rotation to raytrace towards - * @return The calculated raytrace result - */ - public static RayTraceResult rayTraceTowards(Rotation rotation) { - double blockReachDistance = mc.playerController.getBlockReachDistance(); - Vec3d start = mc.player.getPositionEyes(1.0F); - Vec3d direction = calcVec3dFromRotation(rotation); - Vec3d end = start.add( - direction.x * blockReachDistance, - direction.y * blockReachDistance, - direction.z * blockReachDistance - ); - return mc.world.rayTraceBlocks(start, end, false, false, true); - } -} diff --git a/src/main/java/baritone/utils/ToolSet.java b/src/main/java/baritone/utils/ToolSet.java index 62115f24..026ec199 100644 --- a/src/main/java/baritone/utils/ToolSet.java +++ b/src/main/java/baritone/utils/ToolSet.java @@ -20,44 +20,48 @@ package baritone.utils; import baritone.Baritone; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.init.Enchantments; import net.minecraft.init.MobEffects; +import net.minecraft.item.Item.ToolMaterial; import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemTool; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; /** * A cached list of the best tools on the hotbar for any block * - * @author avecowa, Brady, leijurv + * @author Avery, Brady, leijurv */ -public class ToolSet implements Helper { - +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. */ - private Map breakStrengthCache = new HashMap<>(); + private final Map breakStrengthCache; /** - * Calculate which tool on the hotbar is best for mining - * - * @param b the blockstate to be mined - * @return a byte indicating the index in the tools array that worked best + * My buddy leijurv owned me so we have this to not create a new lambda instance. */ - public byte getBestSlot(IBlockState b) { - byte best = 0; - double value = -1; - for (byte i = 0; i < 9; i++) { - double v = calculateStrVsBlock(i, b); - if (v > value || value == -1) { - value = v; - best = i; - } + private final Function backendCalculation; + + private final EntityPlayerSP player; + + public ToolSet(EntityPlayerSP player) { + breakStrengthCache = new HashMap<>(); + this.player = player; + + if (Baritone.settings().considerPotionEffects.get()) { + double amplifier = potionAmplifier(); + Function amplify = x -> amplifier * x; + backendCalculation = amplify.compose(this::getBestDestructionTime); + } else { + backendCalculation = this::getBestDestructionTime; } - return best; } /** @@ -67,7 +71,64 @@ public class ToolSet implements Helper { * @return how long it would take in ticks */ public double getStrVsBlock(IBlockState state) { - return this.breakStrengthCache.computeIfAbsent(state.getBlock(), b -> calculateStrVsBlock(getBestSlot(state), state)); + return breakStrengthCache.computeIfAbsent(state.getBlock(), backendCalculation); + } + + /** + * Evaluate the material cost of a possible tool. The priority matches the + * listed order in the Item.ToolMaterial enum. + * + * @param itemStack a possibly empty ItemStack + * @return values range from -1 to 4 + */ + private int getMaterialCost(ItemStack itemStack) { + if (itemStack.getItem() instanceof ItemTool) { + ItemTool tool = (ItemTool) itemStack.getItem(); + return ToolMaterial.valueOf(tool.getToolMaterialName()).ordinal(); + } else { + return -1; + } + } + + /** + * 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) { + byte best = 0; + double value = Double.NEGATIVE_INFINITY; + int materialCost = Integer.MIN_VALUE; + IBlockState blockState = b.getDefaultState(); + for (byte i = 0; i < 9; i++) { + ItemStack itemStack = player.inventory.getStackInSlot(i); + double v = calculateStrVsBlock(itemStack, blockState); + if (v > value) { + value = v; + best = i; + materialCost = getMaterialCost(itemStack); + } else if (v == value) { + int c = getMaterialCost(itemStack); + if (c < materialCost) { + value = v; + best = i; + materialCost = c; + } + } + } + return best; + } + + /** + * Calculate how effectively a block can be destroyed + * + * @param b the blockstate to be mined + * @return A double containing the destruction ticks with the best tool + */ + private double getBestDestructionTime(Block b) { + ItemStack stack = player.inventory.getStackInSlot(getBestSlot(b)); + return calculateStrVsBlock(stack, b.getDefaultState()); } /** @@ -77,49 +138,55 @@ public class ToolSet implements Helper { * @param state the blockstate to be mined * @return how long it would take in ticks */ - private double calculateStrVsBlock(byte slot, IBlockState state) { - // Calculate the slot with the best item - ItemStack contents = player().inventory.getStackInSlot(slot); - - float blockHard = state.getBlockHardness(null, null); - if (blockHard < 0) { + private double calculateStrVsBlock(ItemStack item, IBlockState state) { + float hardness = state.getBlockHardness(null, null); + if (hardness < 0) { return -1; } - float speed = contents.getDestroySpeed(state); + float speed = item.getDestroySpeed(state); if (speed > 1) { - int effLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.EFFICIENCY, contents); - if (effLevel > 0 && !contents.isEmpty()) { + int effLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.EFFICIENCY, item); + if (effLevel > 0 && !item.isEmpty()) { speed += effLevel * effLevel + 1; } } - if (Baritone.settings().considerPotionEffects.get()) { - if (player().isPotionActive(MobEffects.HASTE)) { - speed *= 1 + (player().getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2; - } - if (player().isPotionActive(MobEffects.MINING_FATIGUE)) { - switch (player().getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) { - case 0: - speed *= 0.3; - break; - case 1: - speed *= 0.09; - break; - case 2: - speed *= 0.0027; - break; - default: - speed *= 0.00081; - break; - } - } - } - speed /= blockHard; - if (state.getMaterial().isToolNotRequired() || (!contents.isEmpty() && contents.canHarvestBlock(state))) { - return speed / 30; + speed /= hardness; + if (state.getMaterial().isToolNotRequired() || (!item.isEmpty() && item.canHarvestBlock(state))) { + speed /= 30; } else { - return speed / 100; + speed /= 100; } + return speed; + } + + /** + * Calculates any modifier to breaking time based on status effects. + * + * @return a double to scale block breaking speed. + */ + private double potionAmplifier() { + double speed = 1; + if (player.isPotionActive(MobEffects.HASTE)) { + speed *= 1 + (player.getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2; + } + if (player.isPotionActive(MobEffects.MINING_FATIGUE)) { + switch (player.getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) { + case 0: + speed *= 0.3; + break; + case 1: + speed *= 0.09; + break; + case 2: + speed *= 0.0027; + break; + default: + speed *= 0.00081; + break; + } + } + return speed; } } diff --git a/src/main/java/baritone/utils/Utils.java b/src/main/java/baritone/utils/Utils.java deleted file mode 100755 index 0de61461..00000000 --- a/src/main/java/baritone/utils/Utils.java +++ /dev/null @@ -1,137 +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.api.utils.Rotation; -import net.minecraft.block.BlockFire; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; - -import static baritone.utils.Helper.HELPER; - -/** - * @author Brady - * @since 8/1/2018 12:56 AM - */ -public final class Utils { - - /** - * Constant that a degree value is multiplied by to get the equivalent radian value - */ - public static final double DEG_TO_RAD = Math.PI / 180.0; - - /** - * Constant that a radian value is multiplied by to get the equivalent degree value - */ - public static final double RAD_TO_DEG = 180.0 / Math.PI; - - public static Rotation calcRotationFromCoords(BlockPos orig, BlockPos dest) { - return calcRotationFromVec3d(vec3dFromBlockPos(orig), vec3dFromBlockPos(dest)); - } - - /** - * Calculates rotation to given Vecdest from Vecorig - * - * @param orig - * @param dest - * @return Rotation {@link Rotation} - */ - public 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]); - double pitch = MathHelper.atan2(delta[1], dist); - return new Rotation( - (float) radToDeg(yaw), - (float) radToDeg(pitch) - ); - } - - /** - * Calculates rotation to given Vecdest from Vecorig - * - * @param orig - * @param dest - * @return Rotation {@link Rotation} - */ - public static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest, Rotation current) { - return wrapAnglesToRelative(current, calcRotationFromVec3d(orig, dest)); - } - - public static Vec3d calcCenterFromCoords(BlockPos orig, World world) { - IBlockState b = BlockStateInterface.get(orig); - AxisAlignedBB bbox = b.getBoundingBox(world, orig); - double xDiff = (bbox.minX + bbox.maxX) / 2; - double yDiff = (bbox.minY + bbox.maxY) / 2; - double zDiff = (bbox.minZ + bbox.maxZ) / 2; - if (b.getBlock() instanceof BlockFire) {//look at bottom of fire when putting it out - yDiff = 0; - } - return new Vec3d( - orig.getX() + xDiff, - orig.getY() + yDiff, - orig.getZ() + zDiff - ); - } - - public static Vec3d getBlockPosCenter(BlockPos pos) { - return new Vec3d(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); - } - - public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { - return target.subtract(current).normalize().add(current); - } - - public static Vec3d vec3dFromBlockPos(BlockPos orig) { - return new Vec3d(orig.getX() + 0.0D, orig.getY() + 0.0D, orig.getZ() + 0.0D); - } - - public static double distanceToCenter(BlockPos pos, double x, double y, double z) { - double xdiff = x - (pos.getX() + 0.5D); - double ydiff = y - (pos.getY() + 0.5D); - double zdiff = z - (pos.getZ() + 0.5D); - return Math.sqrt(xdiff * xdiff + ydiff * ydiff + zdiff * zdiff); - } - - public static double playerDistanceToCenter(BlockPos pos) { - EntityPlayerSP player = HELPER.player(); - return distanceToCenter(pos, player.posX, player.posY, player.posZ); - } - - public static double playerFlatDistanceToCenter(BlockPos pos) { - EntityPlayerSP player = HELPER.player(); - return distanceToCenter(pos, player.posX, pos.getY() + 0.5, player.posZ); - } - - public static double degToRad(double deg) { - return deg * DEG_TO_RAD; - } - - public static double radToDeg(double rad) { - return rad * RAD_TO_DEG; - } - - public static BlockPos diff(BlockPos a, BlockPos b) { - return new BlockPos(a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ()); - } -} diff --git a/src/main/java/baritone/utils/accessor/IAnvilChunkLoader.java b/src/main/java/baritone/utils/accessor/IAnvilChunkLoader.java index c243ae0e..73936deb 100644 --- a/src/main/java/baritone/utils/accessor/IAnvilChunkLoader.java +++ b/src/main/java/baritone/utils/accessor/IAnvilChunkLoader.java @@ -17,10 +17,13 @@ package baritone.utils.accessor; +import baritone.cache.WorldProvider; + import java.io.File; /** * @author Brady + * @see WorldProvider * @since 8/4/2018 11:36 AM */ public interface IAnvilChunkLoader { diff --git a/src/main/java/baritone/utils/accessor/IChunkProviderServer.java b/src/main/java/baritone/utils/accessor/IChunkProviderServer.java index 78a471b2..b5512a1a 100644 --- a/src/main/java/baritone/utils/accessor/IChunkProviderServer.java +++ b/src/main/java/baritone/utils/accessor/IChunkProviderServer.java @@ -17,10 +17,12 @@ package baritone.utils.accessor; +import net.minecraft.world.WorldProvider; import net.minecraft.world.chunk.storage.IChunkLoader; /** * @author Brady + * @see WorldProvider * @since 8/4/2018 11:33 AM */ public interface IChunkProviderServer { diff --git a/src/main/java/baritone/utils/pathing/BetterWorldBorder.java b/src/main/java/baritone/utils/pathing/BetterWorldBorder.java new file mode 100644 index 00000000..9d8f3ef8 --- /dev/null +++ b/src/main/java/baritone/utils/pathing/BetterWorldBorder.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.utils.pathing; + +import net.minecraft.world.border.WorldBorder; + +/** + * Essentially, a "rule" for the path finder, prevents proposed movements from attempting to venture + * into the world border, and prevents actual movements from placing blocks in the world border. + */ +public class BetterWorldBorder { + + private final double minX; + private final double maxX; + private final double minZ; + private final double maxZ; + + public BetterWorldBorder(WorldBorder border) { + this.minX = border.minX(); + this.maxX = border.maxX(); + this.minZ = border.minZ(); + this.maxZ = border.maxZ(); + } + + public boolean entirelyContains(int x, int z) { + return x + 1 > minX && x < maxX && z + 1 > minZ && z < maxZ; + } + + public boolean canPlaceAt(int x, int z) { + // move it in 1 block on all sides + // because we can't place a block at the very edge against a block outside the border + // it won't let us right click it + return x > minX && x + 1 < maxX && z > minZ && z + 1 < maxZ; + } +} diff --git a/src/main/java/baritone/utils/pathing/MoveResult.java b/src/main/java/baritone/utils/pathing/MutableMoveResult.java similarity index 66% rename from src/main/java/baritone/utils/pathing/MoveResult.java rename to src/main/java/baritone/utils/pathing/MutableMoveResult.java index 8478a617..b6e1ba8a 100644 --- a/src/main/java/baritone/utils/pathing/MoveResult.java +++ b/src/main/java/baritone/utils/pathing/MutableMoveResult.java @@ -17,24 +17,27 @@ package baritone.utils.pathing; -import static baritone.api.pathing.movement.ActionCosts.COST_INF; +import baritone.api.pathing.movement.ActionCosts; /** * The result of a calculated movement, with destination x, y, z, and the cost of performing the movement * * @author leijurv */ -public final class MoveResult { - public static final MoveResult IMPOSSIBLE = new MoveResult(0, 0, 0, COST_INF); - public final int destX; - public final int destY; - public final int destZ; - public final double cost; +public final class MutableMoveResult { + public int x; + public int y; + public int z; + public double cost; - public MoveResult(int x, int y, int z, double cost) { - this.destX = x; - this.destY = y; - this.destZ = z; - this.cost = cost; + public MutableMoveResult() { + reset(); + } + + public final void reset() { + x = 0; + y = 0; + z = 0; + cost = ActionCosts.COST_INF; } } diff --git a/src/main/java/baritone/utils/pathing/PathBase.java b/src/main/java/baritone/utils/pathing/PathBase.java new file mode 100644 index 00000000..aaf36895 --- /dev/null +++ b/src/main/java/baritone/utils/pathing/PathBase.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.utils.pathing; + +import baritone.api.BaritoneAPI; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.goals.Goal; +import baritone.pathing.path.CutoffPath; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.EmptyChunk; + +public abstract class PathBase implements IPath { + @Override + public IPath cutoffAtLoadedChunks(World world) { + for (int i = 0; i < positions().size(); i++) { + BlockPos pos = positions().get(i); + if (world.getChunk(pos) instanceof EmptyChunk) { + return new CutoffPath(this, i); + } + } + return this; + } + + @Override + public IPath staticCutoff(Goal destination) { + if (length() < BaritoneAPI.getSettings().pathCutoffMinimumLength.get()) { + return this; + } + if (destination == null || destination.isInGoal(getDest())) { + return this; + } + double factor = BaritoneAPI.getSettings().pathCutoffFactor.get(); + int newLength = (int) ((length() - 1) * factor); + return new CutoffPath(this, newLength); + } +} diff --git a/src/main/java/baritone/utils/pathing/PathingBlockType.java b/src/main/java/baritone/utils/pathing/PathingBlockType.java index 80c92dcb..35e21fc3 100644 --- a/src/main/java/baritone/utils/pathing/PathingBlockType.java +++ b/src/main/java/baritone/utils/pathing/PathingBlockType.java @@ -42,18 +42,6 @@ public enum PathingBlockType { } public static PathingBlockType fromBits(boolean b1, boolean b2) { - if (b1) { - if (b2) { - return PathingBlockType.SOLID; - } else { - return PathingBlockType.AVOID; - } - } else { - if (b2) { - return PathingBlockType.WATER; - } else { - return PathingBlockType.AIR; - } - } + return b1 ? b2 ? SOLID : AVOID : b2 ? WATER : AIR; } } diff --git a/src/main/resources/META-INF/services/baritone.api.IBaritoneProvider b/src/main/resources/META-INF/services/baritone.api.IBaritoneProvider new file mode 100644 index 00000000..934db7d3 --- /dev/null +++ b/src/main/resources/META-INF/services/baritone.api.IBaritoneProvider @@ -0,0 +1 @@ +baritone.BaritoneProvider \ No newline at end of file diff --git a/src/test/java/baritone/utils/pathing/BetterBlockPosTest.java b/src/test/java/baritone/utils/pathing/BetterBlockPosTest.java index 13b76c73..a21f0cd4 100644 --- a/src/test/java/baritone/utils/pathing/BetterBlockPosTest.java +++ b/src/test/java/baritone/utils/pathing/BetterBlockPosTest.java @@ -17,6 +17,7 @@ package baritone.utils.pathing; +import baritone.api.utils.BetterBlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import org.junit.Test;