diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index edb250c3a..b686a589f 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -11,7 +11,7 @@ Operating system: Java version: Minecraft version: Baritone version: -Other mods (if used): +Other mods (if used): ## Exception, error or logs Please find your `latest.log` or `debug.log` in this folder and attach it to the issue @@ -29,8 +29,7 @@ Add your steps to reproduce the issue/bug experienced here. To get the modified settings run `#modified` in game ## Final checklist - -- [x] I know how to properly use check boxes +- [x] I know how to properly use check boxes - [ ] I have included the version of Minecraft I'm running, baritone's version and forge mods (if used). - [ ] I have included logs, exceptions and / or steps to reproduce the issue. - [ ] I have not used any OwO's or UwU's in this issue. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index db70a6850..b5de2dfd3 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -10,5 +10,5 @@ assignees: '' With as much detail as possible, describe your question and what you may need help with. ## Final checklist -- [x] I know how to properly use check boxes +- [x] I know how to properly use check boxes - [ ] I have not used any OwO's or UwU's in this issue. diff --git a/.github/ISSUE_TEMPLATE/suggestion.md b/.github/ISSUE_TEMPLATE/suggestion.md index 1ee32142b..9f7a30bf7 100644 --- a/.github/ISSUE_TEMPLATE/suggestion.md +++ b/.github/ISSUE_TEMPLATE/suggestion.md @@ -16,5 +16,5 @@ If applicable, what settings/customizability should be offered to tweak the func Describe how your suggestion would improve Baritone, or the reason behind it being added. ## Final checklist -- [x] I know how to properly use check boxes +- [x] I know how to properly use check boxes - [ ] I have not used any OwO's or UwU's in this issue. diff --git a/.github/workflows/gradle_build.yml b/.github/workflows/gradle_build.yml index 0d185a6d5..9d4ed379b 100644 --- a/.github/workflows/gradle_build.yml +++ b/.github/workflows/gradle_build.yml @@ -13,31 +13,31 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'adopt' - cache: gradle - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Build with Gradle - run: ./gradlew build -Pmod_version="$(git describe --always --tags | cut -c2-)" - - - name: Archive Artifacts - uses: actions/upload-artifact@v2 - with: - name: Artifacts - path: dist/ - - - name: Archive mapping.txt - uses: actions/upload-artifact@v2 - with: - name: Mappings - path: mapping/ + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build -Pmod_version="$(git describe --always --tags --first-parent | cut -c2-)" + + - name: Archive Artifacts + uses: actions/upload-artifact@v3 + with: + name: Artifacts + path: dist/ + + - name: Archive mapping.txt + uses: actions/upload-artifact@v3 + with: + name: Mappings + path: mapping/ diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 16c1126bd..34d52e54c 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -11,16 +11,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 17 - uses: actions/setup-java@v2 - with: - java-version: '17' - distribution: 'adopt' - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Executing tests - run: ./gradlew test + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Executing tests + run: ./gradlew test diff --git a/.gitignore b/.gitignore index 50a166bce..fe4b0d407 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,10 @@ baritone_Client.launch .vscode/launch.json .architectury-transformer -mapping \ No newline at end of file + +mapping + +libs/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar +libs/java-objc-bridge-1.1.jar + +mapping diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 20538e8e3..474ecf7e6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -25,13 +25,13 @@ Examples of unacceptable behavior by participants include: * Anime * The use of sexualized language or imagery and unwelcome sexual attention or - advances + advances * ~~Trolling, insulting/derogatory comments, and personal or political attacks~~ * Public or private harassment * Publishing others' private information, such as a physical or electronic - address, without explicit permission or consent + address, without explicit permission or consent * Other conduct which could reasonably be considered inappropriate in a - professional setting + professional setting ## Our Responsibilities diff --git a/Dockerfile b/Dockerfile index 9023e2020..8a00fc3da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ RUN apt update -y RUN apt install \ openjdk-17-jdk \ + git \ --assume-yes COPY . /code diff --git a/FEATURES.md b/FEATURES.md index e0f6e7d2d..372a77410 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,77 +1,33 @@ # Pathing features -- **Long distance pathing and splicing** Baritone calculates paths in segments, and precalculates the next segment when - the current one is about to end, so that it's moving towards the goal at all times. -- **Chunk caching** Baritone simplifies chunks to a compacted internal 2-bit representation (AIR, SOLID, WATER, AVOID) - and stores them in RAM for better very-long-distance pathing. There is also an option to save these cached chunks to - disk. Example -- **Block breaking** Baritone considers breaking blocks as part of its path. It also takes into account your current - tool set and hot bar. For example, if you have a Eff V diamond pick, it may choose to mine through a stone barrier, - while if you only had a wood pick it might be faster to climb over it. -- **Block placing** Baritone considers placing blocks as part of its path. This includes sneak-back-placing, pillaring, - etc. It has a configurable penalty of placing a block (set to 1 second by default), to conserve its resources. The - list of acceptable throwaway blocks is also configurable, and is cobble, dirt, or netherrack by - default. Example -- **Falling** Baritone will fall up to 3 blocks onto solid ground (configurable, if you have Feather Falling and/or - don't mind taking a little damage). If you have a water bucket on your hotbar, it will fall up to 23 blocks and place - the bucket beneath it. It will fall an unlimited distance into existing still water. -- **Vines and ladders** Baritone understands how to climb and descend vines and ladders. There is experimental support - for more advanced maneuvers, like strafing to a different ladder / vine column in midair (off by default, setting - named `allowVines`). Baritone can break its fall by grabbing ladders / vines midair, and understands when that is and - isn't possible. +- **Long distance pathing and splicing** Baritone calculates paths in segments, and precalculates the next segment when the current one is about to end, so that it's moving towards the goal at all times. +- **Chunk caching** Baritone simplifies chunks to a compacted internal 2-bit representation (AIR, SOLID, WATER, AVOID) and stores them in RAM for better very-long-distance pathing. There is also an option to save these cached chunks to disk. Example +- **Block breaking** Baritone considers breaking blocks as part of its path. It also takes into account your current tool set and hot bar. For example, if you have a Eff V diamond pick, it may choose to mine through a stone barrier, while if you only had a wood pick it might be faster to climb over it. +- **Block placing** Baritone considers placing blocks as part of its path. This includes sneak-back-placing, pillaring, etc. It has a configurable penalty of placing a block (set to 1 second by default), to conserve its resources. The list of acceptable throwaway blocks is also configurable, and is cobble, dirt, or netherrack by default. Example +- **Falling** Baritone will fall up to 3 blocks onto solid ground (configurable, if you have Feather Falling and/or don't mind taking a little damage). If you have a water bucket on your hotbar, it will fall up to 23 blocks and place the bucket beneath it. It will fall an unlimited distance into existing still water. +- **Vines and ladders** Baritone understands how to climb and descend vines and ladders. There is experimental support for more advanced maneuvers, like strafing to a different ladder / vine column in midair (off by default, setting named `allowVines`). Baritone can break its fall by grabbing ladders / vines midair, and understands when that is and isn't possible. - **Opening fence gates and doors** - **Slabs and stairs** -- **Falling blocks** Baritone understands the costs of breaking blocks with falling blocks on top, and includes all of - their break costs. Additionally, since it avoids breaking any blocks touching a liquid, it won't break the bottom of a - gravel stack below a lava lake (anymore). -- **Avoiding dangerous blocks** Obviously, it knows not to walk through fire or on magma, not to corner over lava (that - deals some damage), not to break any blocks touching a liquid (it might drown), etc. +- **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. +- **Parkour place** Sprint jumping over a 3 block gap and placing the block to land on while executing the jump. It's really cool. - **Pigs** It can sort of control pigs. I wouldn't rely on it though. # Pathing method +Baritone uses A*, with some modifications: -Baritone uses A*, with some modifications: - -- **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. 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. +- **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. 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. # Chat control - [Baritone chat control usage](USAGE.md) # Goals - The pathing goal can be set to any of these options: - - **GoalBlock** one specific block that the player should stand inside at foot level - **GoalXZ** an X and a Z coordinate, used for long distance pathing - **GoalYLevel** a Y coordinate @@ -80,13 +36,11 @@ The pathing goal can be set to any of these options: - **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. +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. -# Future features +# Future features Things it doesn't have yet - - Trapdoors - Sprint jumping in a 1x2 corridor diff --git a/README.md b/README.md index f5652e3bd..3f2923f92 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,16 @@

- Minecraft - Minecraft - Minecraft - Minecraft - Minecraft - Minecraft - Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft + Minecraft

@@ -28,12 +31,13 @@ GitHub commits Code size GitHub repo size - Lines of Code + Lines of Code + yes

Impact integration - KAMI Blue integration + Lambda integration ForgeHax integration Aristois add-on integration rootNET integration @@ -42,46 +46,37 @@

- forthebadge - forthebadge + forthebadge + forthebadge

A Minecraft pathfinder bot. -[**Baritone Discord Server**](http://discord.gg/s6fRBAUpmr) +Baritone is the pathfinding system used in [Impact](https://impactclient.net/) since 4.4. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a (very old!) video I made showing off what it can do. -Baritone is the pathfinding system used in [Impact](https://impactclient.net/) since 4.4. There's -a [showcase video](https://youtu.be/CZkLXWo4Fg4) made by @Adovin#0730 on Baritone which I -recommend. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a (very old!) video I made showing off what it can do. +[**Baritone Discord Server**](http://discord.gg/s6fRBAUpmr) -[Tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa) +**Quick download links:** -The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone. The -second easiest way (for 1.12.2 only) is to install the v1.2.* `api-forge` jar -from [releases](https://github.com/cabaletta/baritone/releases). **For 1.12.2 Forge, just -click [here](https://github.com/cabaletta/baritone/releases/download/v1.2.15/baritone-api-forge-1.2.15.jar)**. -Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on -how to use it. +| Forge | Fabric | +|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| [1.12.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.2.19/baritone-api-forge-1.2.19.jar) | | +| [1.16.5 Forge](https://github.com/cabaletta/baritone/releases/download/v1.6.5/baritone-api-forge-1.6.5.jar) | [1.16.5 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.6.5/baritone-api-fabric-1.6.5.jar) | +| [1.17.1 Forge](https://github.com/cabaletta/baritone/releases/download/v1.7.3/baritone-api-forge-1.7.3.jar) | [1.17.1 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.7.3/baritone-api-fabric-1.7.3.jar) | +| [1.18.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.8.5/baritone-api-forge-1.8.5.jar) | [1.18.2 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.8.5/baritone-api-fabric-1.8.5.jar) | +| [1.19.2 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.4/baritone-api-forge-1.9.4.jar) | [1.19.2 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.4/baritone-api-fabric-1.9.4.jar) | +| [1.19.3 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.1/baritone-api-forge-1.9.1.jar) | [1.19.3 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.1/baritone-api-fabric-1.9.1.jar) | +| [1.19.4 Forge](https://github.com/cabaletta/baritone/releases/download/v1.9.3/baritone-api-forge-1.9.3.jar) | [1.19.4 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.9.3/baritone-api-fabric-1.9.3.jar) | +| [1.20.1 Forge](https://github.com/cabaletta/baritone/releases/download/v1.10.1/baritone-api-forge-1.10.1.jar) | [1.20.1 Fabric](https://github.com/cabaletta/baritone/releases/download/v1.10.1/baritone-api-fabric-1.10.1.jar) | -For 1.16.5, [click here](https://www.youtube.com/watch?v=_4eVJ9Qz2J8) and see description. If you need Forge or Fabric -1.16.5, look [here](https://github.com/cabaletta/baritone/releases/tag/v1.6.3) and get the `api-forge` or `api-fabric` -jar. **For 1.16.5 Fabric, just -click [here](https://github.com/cabaletta/baritone/releases/download/v1.6.3/baritone-api-fabric-1.6.3.jar)**. +**Message for 2b2t players looking for 1.19/1.20 Baritone** If you like, please try the beta for Baritone Elytra for 2b2t, find it in #announcements of [the Baritone discord](http://discord.gg/s6fRBAUpmr). It supports 1.19.4 and 1.20.1, Forge or Fabric. If you have to see it to believe it, watch [this YouTube video](https://youtu.be/NnSlQi-68eQ). -If you need Forge or Fabric 1.17.1, look [here](https://github.com/cabaletta/baritone/releases/tag/v1.7.2) and get -the `api-forge` or `api-fabric` jar. **For 1.17.1 Fabric, just -click [here](https://github.com/cabaletta/baritone/releases/download/v1.7.2/baritone-api-fabric-1.7.2.jar)**. +**How to immediately get started:** Type `#goto 1000 500` in chat to go to x=1000 z=500. Type `#mine diamond_ore` to mine diamond ore. Type `#stop` to stop. For more, read [the usage page](USAGE.md) and/or watch this [tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa). Also try `#elytra` for Elytra flying in the Nether using fireworks. -If you need Forge or Fabric 1.18.2, look [here](https://github.com/cabaletta/baritone/releases/tag/v1.8.3) and get -the `api-forge` or `api-fabric` jar. **For 1.18.2 Fabric, just -click [here](https://github.com/cabaletta/baritone/releases/download/v1.8.3/baritone-api-fabric-1.8.3.jar)**. **For -1.18.2 Forge, just -click [here](https://github.com/cabaletta/baritone/releases/download/v1.8.3/baritone-api-forge-1.8.3.jar)**. +For other versions of Minecraft or more complicated situations or for development, see [Installation & setup](SETUP.md). Also consider just installing [Impact](https://impactclient.net/), which comes with Baritone and is easier to install than wrangling with version JSONs and zips. For 1.16.5, [click here](https://www.youtube.com/watch?v=_4eVJ9Qz2J8) and see description. Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. There's a [showcase video](https://youtu.be/CZkLXWo4Fg4) made by @Adovin#6313 on Baritone which I recommend. This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), -the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 onwards. Baritone focuses on reliability and -particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) -than MineBot at calculating paths). +the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 onwards. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths). Have committed at least once a day from Aug 1, 2018, to Aug 1, 2019. @@ -134,12 +129,9 @@ Magic. (Hours of [leijurv](https://github.com/leijurv/) enduring excruciating pa ![YourKit-Logo](https://www.yourkit.com/images/yklogo.png) -YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET -applications. +YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. -YourKit is the creator of -the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), -and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). +YourKit is the creator of the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). We thank them for granting Baritone an OSS license so that we can make our software the best it can be. diff --git a/SETUP.md b/SETUP.md index 6cbc967cd..1daeb9f89 100644 --- a/SETUP.md +++ b/SETUP.md @@ -2,49 +2,34 @@ The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone. -You can also use a custom version json for Minecraft, with -the [1.14.4](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1) version or -the [1.15.2](https://www.dropbox.com/s/8rx6f0kts9hvd4f/1.15.2-Baritone.zip?dl=1) version or -the [1.16.5](https://www.dropbox.com/s/i6f292o2i7o9acp/1.16.5-Baritone.zip?dl=1) version. +You can also use a custom version json for Minecraft, with the [1.14.4](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1) version or the [1.15.2](https://www.dropbox.com/s/8rx6f0kts9hvd4f/1.15.2-Baritone.zip?dl=1) version or the [1.16.5](https://www.dropbox.com/s/i6f292o2i7o9acp/1.16.5-Baritone.zip?dl=1) version. Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it. ## Prebuilt official releases - -These releases are not always completely up to date with latest features, and are only released from `master`. (so if -you want `backfill-2` branch for example, you'll have to build it yourself) +These releases are not always completely up to date with latest features, and are only released from `master`. (so if you want `backfill-2` branch for example, you'll have to build it yourself) Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases) -v1.2.* is for 1.12.2, v1.3.* is for 1.13.2, v1.4.* is for 1.14.4, v1.5.* is for 1.15.2, v1.6.* is for 1.16.5, v1.7.* is -for 1.17.1, v1.8.* is for 1.18.1 +v1.2.* is for 1.12.2, v1.3.* is for 1.13.2, v1.4.* is for 1.14.4, v1.5.* is for 1.15.2, v1.6.* is for 1.16.5, v1.7.* is for 1.17.1, v1.8.* is for 1.18.1 + +Any official release will be GPG signed by leijurv (44A3EA646EADAC6A). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by that public keys of `checksums.txt`. -Any official release will be GPG signed by leijurv (44A3EA646EADAC6A). Please verify that the hash of the file you -download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by that public keys -of `checksums.txt`. +The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too). -The build is fully deterministic and reproducible, and you can verify Travis did it properly by -running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on -Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too). ## Artifacts -Building Baritone will result in 5 artifacts created in the ``dist`` directory. These are the same as the artifacts -created in the [releases](https://github.com/cabaletta/baritone/releases). +Building Baritone will result in 5 artifacts created in the ``dist`` directory. These are the same as the artifacts created in the [releases](https://github.com/cabaletta/baritone/releases). **The Forge and Fabric releases can simply be added as a Forge/Fabric mods.** -If another one of your Forge mods has a Baritone integration, you want `baritone-api-forge-VERSION.jar`. Otherwise, you -want `baritone-standalone-forge-VERSION.jar` - -- **API**: Only the non-api packages are obfuscated. This should be used in environments where other mods would like to - use Baritone's features. -- **Forge/Fabric API**: Same as API, but packaged for Forge/Fabric. This should be used where another mod has a Baritone - integration. -- **Standalone**: Everything is obfuscated. This should be used in environments where there are no other mods present - that would like to use Baritone's features. -- **Forge/Fabric Standalone**: Same as Standalone, but packaged for Forge/Fabric. This should be used when Baritone is - your only Forge/Fabric mod, or none of your other Forge/Fabric mods integrate with Baritone. +If another one of your Forge mods has a Baritone integration, you want `baritone-api-forge-VERSION.jar`. Otherwise, you want `baritone-standalone-forge-VERSION.jar` + +- **API**: Only the non-api packages are obfuscated. This should be used in environments where other mods would like to use Baritone's features. +- **Forge/Fabric API**: Same as API, but packaged for Forge/Fabric. This should be used where another mod has a Baritone integration. +- **Standalone**: Everything is obfuscated. This should be used in environments where there are no other mods present that would like to use Baritone's features. +- **Forge/Fabric Standalone**: Same as Standalone, but packaged for Forge/Fabric. This should be used when Baritone is your only Forge/Fabric mod, or none of your other Forge/Fabric mods integrate with Baritone. - **Unoptimized**: Nothing is obfuscated. This shouldn't be used ever in production. - **Forge/Fabric Unoptimized**: Same as Unoptimized, but packaged for Forge/Fabric. @@ -56,99 +41,38 @@ want `baritone-standalone-forge-VERSION.jar` - Follow one of the instruction sets below, based on your preference ## Command Line - On Mac OSX and Linux, use `./gradlew` instead of `gradlew`. -If you have errors with a package missing please make sure you have setup your environment, and are using Oracle JDK 8 -for 1.12.2-1.16.5, JDK 16 for 1.17.1, and JDK 17 for 1.18.1. +If you have errors with a package missing please make sure you have setup your environment, and are using Oracle JDK 8 for 1.12.2-1.16.5, JDK 16+ for 1.17.1, and JDK 17+ for 1.18.1. -To check which java you are using do +To check which java you are using do `java -version` in a command prompt or terminal. -If you are using anything above OpenJDK 8 for 1.12.2-1.16.5, it might not work because the Java distributions above JDK -8 using may not have the needed javax classes. +If you are using anything above OpenJDK 8 for 1.12.2-1.16.5, it might not work because the Java distributions above JDK 8 using may not have the needed javax classes. -Open JDK download: https://openjdk.java.net/install/ +Download java: https://adoptium.net/ #### macOS guide - In order to get JDK 8, Try running the following command: `% /usr/libexec/java_home -V` -If it doesn't work try this -guide: https://stackoverflow.com/questions/46513639/how-to-downgrade-java-from-9-to-8-on-a-macos-eclipse-is-not-running-with-java-9 +If it doesn't work try this guide: https://stackoverflow.com/questions/46513639/how-to-downgrade-java-from-9-to-8-on-a-macos-eclipse-is-not-running-with-java-9 If you see something like -`% 1.8.0_VERSION, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_VERSION.jdk/Contents/Home` +`% 1.8.0_VERSION, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_VERSION.jdk/Contents/Home` -in the list then you've got JDK 8 installed. -In order to get JDK 8 running in the **current terminal window** you will have to run this command: +in the list then you've got JDK 8 installed. +In order to get JDK 8 running in the **current terminal window** you will have to run this command: `% export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)` -To add OpenJDK 8 to your PATH add the export line to the end of your `.zshrc / .bashrc` if you want it to apply to each -new terminal. If you're using bash change the .bachrc and if you're using zsh change the .zshrc - -Setting up the Environment: - -``` -$ gradlew setupDecompWorkspace -$ gradlew --refresh-dependencies -``` - -Building Baritone: +To add OpenJDK 8 to your PATH add the export line to the end of your `.zshrc / .bashrc` if you want it to apply to each new terminal. If you're using bash change the .bachrc and if you're using zsh change the .zshrc -``` -$ gradlew build -``` +### Building Baritone -For minecraft 1.15.2+, run the following instead to include the Forge jars: +These tasks depend on the minecraft version, but are (for the most part) standard for building mods. -``` -$ gradlew build -Pbaritone.forge_build -``` - -Do this instead for Fabric jars: - -``` -$ gradlew build -Pbaritone.fabric_build -``` - -Running Baritone: - -``` -$ gradlew runClient -``` - -For information on how to build baritone, see [Building Baritone](#building-baritone) +for more details, see [the build ci action](/.github/workflows/gradle_build.yml) ## IntelliJ - Open the project in IntelliJ as a Gradle project - - ![Image](https://i.imgur.com/jw7Q6vY.png) - -- Run the Gradle tasks `setupDecompWorkspace` then `genIntellijRuns` - - ![Image](https://i.imgur.com/QEfVvWP.png) - - Refresh the Gradle project (or, to be safe, just restart IntelliJ) - - ![Image](https://i.imgur.com/3V7EdWr.png) - -- Select the "Minecraft Client" launch config - - ![Image](https://i.imgur.com/1qz2QGV.png) - -- Click on ``Edit Configurations...`` from the same dropdown and select the "Minecraft Client" config - - ![Image](https://i.imgur.com/s4ly0ZF.png) - -- In `Edit Configurations...` you need to select `baritone_launch` for `Use classpath of module:`. - - ![Image](https://i.imgur.com/hrLhG9u.png) - -## IntelliJ - -- Navigate to the gradle tasks on the right tab as follows - - ![Image](https://i.imgur.com/PE6r9iN.png) - -- Double click on **build** to run it +- depending on the minecraft version, you may need to run `setupDecompWorkspace` or `genIntellijRuns` in order to get everything working \ No newline at end of file diff --git a/USAGE.md b/USAGE.md index 485b295e7..46241e3fe 100644 --- a/USAGE.md +++ b/USAGE.md @@ -2,16 +2,11 @@ # Prefix -Baritone's chat control prefix is `#` by default. In Impact, you can also use `.b` as a prefix. (for example, `.b click` -instead of `#click`) +Baritone's chat control prefix is `#` by default. In Impact, you can also use `.b` as a prefix. (for example, `.b click` instead of `#click`) -Baritone commands can also by default be typed in the chatbox. However if you make a typo, like typing "gola 10000 -10000" instead of "goal" it goes into public chat, which is bad, so using `#` is suggested. +Baritone commands can also by default be typed in the chatbox. However if you make a typo, like typing "gola 10000 10000" instead of "goal" it goes into public chat, which is bad, so using `#` is suggested. -To disable direct chat control (with no prefix), turn off the `chatControl` setting. To disable chat control with -the `#` prefix, turn off the `prefixControl` setting. In Impact, `.b` cannot be disabled. Be careful that you don't -leave yourself with all control methods disabled (if you do, reset your settings by deleting the -file `minecraft/baritone/settings.txt` and relaunching). +To disable direct chat control (with no prefix), turn off the `chatControl` setting. To disable chat control with the `#` prefix, turn off the `prefixControl` setting. In Impact, `.b` cannot be disabled. Be careful that you don't leave yourself with all control methods disabled (if you do, reset your settings by deleting the file `minecraft/baritone/settings.txt` and relaunching). # For Baritone 1.2.10+, 1.3.5+, 1.4.2+ @@ -21,7 +16,7 @@ Try `#help` I promise it won't just send you back here =) "wtf where is cleararea" -> look at `#help sel` -"wtf where is goto death, goto waypoint" -> look at `#help wp` +"wtf where is goto death, goto waypoint" -> look at `#help wp` just look at `#help` lmao @@ -35,70 +30,43 @@ Watch this [showcase video](https://youtu.be/CZkLXWo4Fg4)! `help` -To toggle a boolean setting, just say its name in chat (for example saying `allowBreak` toggles whether Baritone will -consider breaking blocks). For a numeric setting, say its name then the new value (like `primaryTimeoutMS 250`). It's -case insensitive. To reset a setting to its default value, say `acceptableThrowawayItems reset`. To reset all settings, -say `reset`. To see all settings that have been modified from their default values, say `modified`. +To toggle a boolean setting, just say its name in chat (for example saying `allowBreak` toggles whether Baritone will consider breaking blocks). For a numeric setting, say its name then the new value (like `primaryTimeoutMS 250`). It's case insensitive. To reset a setting to its default value, say `acceptableThrowawayItems reset`. To reset all settings, say `reset`. To see all settings that have been modified from their default values, say `modified`. Commands in Baritone: - - `thisway 1000` then `path` to go in the direction you're facing for a thousand blocks - `goal x y z` or `goal x z` or `goal y`, then `path` to set a goal to a certain coordinate then path to it - `goto x y z` or `goto x z` or `goto y` to go to a certain coordinate (in a single step, starts going immediately) - `goal` to set the goal to your player's feet - `goal clear` to clear the goal - `cancel` or `stop` to stop everything, `forcecancel` is also an option -- `goto portal` or `goto ender_chest` or `goto block_type` to go to a block. (in Impact, `.goto` is an alias - for `.b goto` for the most part) -- `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it - can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be - specified, for example, `mine 64 diamond_ore`. -- `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it ( - either at foot level or eye level), and left click and drag to select an area (`#help sel` to see what you can do with - that selection). -- `follow player playerName` to follow a player. `follow players` to follow any players in range (combine with Kill Aura - for a fun time). `follow entities` to follow any entities. `follow entity pig` to follow entities of a specific type. -- `wp` for waypoints. A "tag" is like "home" (created automatically on right clicking a bed) or "death" (created - automatically on death) or "user" (has to be created manually). So you might want `#wp save user coolbiome`, then to - set the goal `#wp goal coolbiome` then `#path` to path to it. For death, `#wp goal death` will list waypoints under - the "death" tag (remember stuff is clickable!) -- `build` to build a schematic. `build blah.schematic` will load `schematics/blah.schematic` and build it with the - origin being your player feet. `build blah.schematic x y z` to set the origin. Any of those can be relative to your - player (`~ 69 ~-420` would build at x=player x, y=69, z=player z-420). +- `goto portal` or `goto ender_chest` or `goto block_type` to go to a block. (in Impact, `.goto` is an alias for `.b goto` for the most part) +- `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine 64 diamond_ore`. +- `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it (either at foot level or eye level), and left click and drag to select an area (`#help sel` to see what you can do with that selection). +- `follow player playerName` to follow a player. `follow players` to follow any players in range (combine with Kill Aura for a fun time). `follow entities` to follow any entities. `follow entity pig` to follow entities of a specific type. +- `wp` for waypoints. A "tag" is like "home" (created automatically on right clicking a bed) or "death" (created automatically on death) or "user" (has to be created manually). So you might want `#wp save user coolbiome`, then to set the goal `#wp goal coolbiome` then `#path` to path to it. For death, `#wp goal death` will list waypoints under the "death" tag (remember stuff is clickable!) +- `build` to build a schematic. `build blah.schematic` will load `schematics/blah.schematic` and build it with the origin being your player feet. `build blah.schematic x y z` to set the origin. Any of those can be relative to your player (`~ 69 ~-420` would build at x=player x, y=69, z=player z-420). - `schematica` to build the schematic that is currently open in schematica -- `tunnel` to dig and make a tunnel, 1x2. It will only deviate from the straight line if necessary such as to avoid - lava. For a dumber tunnel that is really just cleararea, you can `tunnel 3 2 100`, to clear an area 3 high, 2 wide, - and 100 deep. -- `farm` to automatically harvest, replant, or bone meal crops. Use `farm ` or `farm ` to limit - the max distance from the starting point or a waypoint. +- `tunnel` to dig and make a tunnel, 1x2. It will only deviate from the straight line if necessary such as to avoid lava. For a dumber tunnel that is really just cleararea, you can `tunnel 3 2 100`, to clear an area 3 high, 2 wide, and 100 deep. +- `farm` to automatically harvest, replant, or bone meal crops. Use `farm ` or `farm ` to limit the max distance from the starting point or a waypoint. - `axis` to go to an axis or diagonal axis at y=120 (`axisHeight` is a configurable setting, defaults to 120). -- `explore x z` to explore the world from the origin of x,z. Leave out x and z to default to player feet. This will - continually path towards the closest chunk to the origin that it's never seen before. `explorefilter filter.json` with - optional invert can be used to load in a list of chunks to load. -- `invert` to invert the current goal and path. This gets as far away from it as possible, instead of as close as - possible. For example, do `goal` then `invert` to run as far as possible from where you're standing at the start. +- `explore x z` to explore the world from the origin of x,z. Leave out x and z to default to player feet. This will continually path towards the closest chunk to the origin that it's never seen before. `explorefilter filter.json` with optional invert can be used to load in a list of chunks to load. +- `invert` to invert the current goal and path. This gets as far away from it as possible, instead of as close as possible. For example, do `goal` then `invert` to run as far as possible from where you're standing at the start. - `come` tells Baritone to head towards your camera, useful when freecam doesn't move your player position. - `blacklist` will stop baritone from going to the closest block so it won't attempt to get to it. -- `eta` to get information about the estimated time until the next segment and the goal, be aware that the ETA to your - goal is really unprecise. +- `eta` to get information about the estimated time until the next segment and the goal, be aware that the ETA to your goal is really unprecise. - `proc` to view miscellaneous information about the process currently controlling Baritone. - `repack` to re-cache the chunks around you. - `gc` to call `System.gc()` which may free up some memory. - `render` to fix glitched chunk rendering without having to reload all of them. - `reloadall` to reload Baritone's world cache or `saveall` to save Baritone's world cache. - `find` to search through Baritone's cache and attempt to find the location of the block. -- `surface` or `top` to tell Baritone to head towards the closest surface-like area, this can be the surface or highest - available air space. +- `surface` or `top` to tell Baritone to head towards the closest surface-like area, this can be the surface or highest available air space. - `version` to get the version of Baritone you're running - `damn` daniel -All the settings and documentation -are here. If you -find HTML easier to read than Javadoc, you can -look here. +All the settings and documentation are here. If you find HTML easier to read than Javadoc, you can look here. -There are about a hundred settings, but here are some fun / interesting / important ones that you might want to look at -changing in normal usage of Baritone. The documentation for each can be found at the above links. +There are about a hundred settings, but here are some fun / interesting / important ones that you might want to look at changing in normal usage of Baritone. The documentation for each can be found at the above links. - `allowBreak` - `allowSprint` - `allowPlace` @@ -118,24 +86,22 @@ changing in normal usage of Baritone. The documentation for each can be found at - `mineScanDroppedItems` - `allowDiagonalAscend` + + + # Troubleshooting / common issues ## Why doesn't Baritone respond to any of my chat commands? - This could be one of many things. -First, make sure it's actually installed. An easy way to check is seeing if it created the folder `baritone` in your -Minecraft folder. +First, make sure it's actually installed. An easy way to check is seeing if it created the folder `baritone` in your Minecraft folder. Second, make sure that you're using the prefix properly, and that chat control is enabled in the way you expect. -For example, Impact disables direct chat control. (i.e. anything typed in chat without a prefix will be ignored and sent -publicly). **This is a saved setting**, so if you run Impact once, `chatControl` will be off from then on, **even in -other clients**. -So you'll need to use the `#` prefix or edit `baritone/settings.txt` in your Minecraft folder to undo that ( -specifically, remove the line `chatControl false` then restart your client). +For example, Impact disables direct chat control. (i.e. anything typed in chat without a prefix will be ignored and sent publicly). **This is a saved setting**, so if you run Impact once, `chatControl` will be off from then on, **even in other clients**. +So you'll need to use the `#` prefix or edit `baritone/settings.txt` in your Minecraft folder to undo that (specifically, remove the line `chatControl false` then restart your client). -## Why can I do `.goto x z` in Impact but nowhere else? Why can I do `-path to x z` in KAMI but nowhere else? +## Why can I do `.goto x z` in Impact but nowhere else? Why can I do `-path to x z` in KAMI but nowhere else? These are custom commands that they added; those aren't from Baritone. The equivalent you're looking for is `goto x z`. diff --git a/build.gradle b/build.gradle index 377386a76..18fc7ce25 100755 --- a/build.gradle +++ b/build.gradle @@ -21,14 +21,21 @@ allprojects { apply plugin: "maven-publish" archivesBaseName = rootProject.archives_base_name - /*def vers = 'git describe --always --tags --dirty'.execute().text.trim() + + /*def vers = "" + try { + vers = 'git describe --always --tags --first-parent --dirty'.execute().text.trim() + } catch (Exception e) { + println "Version detection failed: " + e + } if (!vers.startsWith("v")) { - println "git doesn't appear to be installed!" println "using version number: " + rootProject.mod_version version = rootProject.mod_version } else { version = vers.substring(1) + println "Detected version " + version }*/ + version = rootProject.mod_version group = rootProject.maven_group @@ -60,19 +67,31 @@ allprojects { } } mavenCentral() + maven { + name = 'babbaj-repo' + url = 'https://babbaj.github.io/maven/' + } } dependencies { - minecraft "net.minecraft:minecraft:${rootProject.minecraft_version}" - // The following line declares the mojmap mappings, you may use other mappings as well - mappings "net.fabricmc:intermediary:${rootProject.minecraft_version}:v2" - mappings "net.minecraft:minecraft:${rootProject.minecraft_version}:client-mappings" - mappings "org.parchmentmc.data:parchment-1.19.2:2022.11.27@zip" - implementation "org.spongepowered:mixin:0.8.5" implementation "org.ow2.asm:asm:9.3" // The following line declares the yarn mappings you may select this one as well. // mappings "net.fabricmc:yarn:1.17.1+build.32:v2" + //launchImplementation('dev.babbaj:nether-pathfinder:1.3.0') + implementation "dev.babbaj:nether-pathfinder:${nether_pathfinder_version}" + + implementation 'com.google.code.findbugs:jsr305:3.0.2' + } + + unimined.minecraft(sourceSets.main, true) { + version rootProject.minecraft_version + + mappings { + intermediary() + mojmap() + parchment("1.20.2", "2023.10.22") + } } tasks.withType(JavaCompile).configureEach { @@ -85,8 +104,9 @@ allprojects { } } -minecraft { +unimined.minecraft { runs.off = true + defaultRemapJar = false } archivesBaseName = archivesBaseName + "-common" @@ -119,7 +139,6 @@ sourceSets { } dependencies { - implementation 'org.jetbrains:annotations:23.0.0' testImplementation 'junit:junit:4.13.2' } @@ -127,15 +146,10 @@ jar { from sourceSets.main.output, sourceSets.launch.output, sourceSets.api.output } -remapJar { - targetNamespace = "named" - fallbackTargetNamespace = "intermediary" -} - javadoc { options.addStringOption('Xwerror', '-quiet') // makes the build fail on travis when there is a javadoc error options.linkSource true options.encoding "UTF-8" // allow emoji in comments :^) source = sourceSets.api.allJava classpath += sourceSets.api.compileClasspath -} \ No newline at end of file +} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 14b7dc3bb..a58fe6089 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -29,12 +29,16 @@ repositories { name = 'FabricMaven' url = 'https://maven.fabricmc.net/' } + maven { + name = 'NeoForgedMaven' + url = 'https://maven.neoforged.net/' + } mavenCentral() } dependencies { implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0' - implementation group: 'commons-io', name: 'commons-io', version: '2.6' + implementation group: 'commons-io', name: 'commons-io', version: '2.7' - implementation group: 'xyz.wagyourtail.unimined', name: 'xyz.wagyourtail.unimined.gradle.plugin', version: '0.3.4' + implementation group: 'xyz.wagyourtail.unimined', name: 'xyz.wagyourtail.unimined.gradle.plugin', version: '1.1.0' } \ 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 index be8c98aed..2dad551f2 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/BaritoneGradleTask.java @@ -18,6 +18,9 @@ package baritone.gradle.task; import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskAction; import java.io.File; import java.io.IOException; @@ -33,35 +36,56 @@ class BaritoneGradleTask extends DefaultTask { 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", - PROGUARD_MAPPING_DIR = "mapping", - - ARTIFACT_STANDARD = "%s-%s.jar", - ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar", - ARTIFACT_API = "%s-api-%s.jar", - ARTIFACT_STANDALONE = "%s-standalone-%s.jar"; + 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", + PROGUARD_MAPPING_DIR = "mapping", + + 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 final Path - artifactPath, - artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, // these are different for forge builds - proguardOut; + protected Path + artifactPath, + artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, // these are different for forge builds + proguardOut; + + + @Input + @Optional + protected String compType = null; + + public String getCompType() { + return compType; + } + + public void setCompType(String compType) { + this.compType = compType; + } + public BaritoneGradleTask() { - this.artifactName = getProject().getProperties().get("archivesBaseName").toString(); - this.artifactVersion = getProject().getVersion().toString(); + this.artifactName = getProject().getRootProject().getProperties().get("archives_base_name").toString(); + } + + public void doFirst() { + if (compType != null) { + this.artifactVersion = compType + "-" + getProject().getVersion(); + } else { + 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.artifactApiPath = this.getBuildFile(formatVersion(ARTIFACT_API)); + this.artifactStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_STANDALONE)); this.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH); } @@ -98,4 +122,8 @@ protected Path getTemporaryFile(String file) { protected Path getBuildFile(String file) { return getRelativeFile("libs/" + file); } + + protected String addCompTypeFirst(String string) { + return compType == null ? string : compType + "-" + string; + } } diff --git a/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java b/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java index e201512dd..48cd0a72a 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/CreateDistTask.java @@ -21,8 +21,11 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.OpenOption; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.security.MessageDigest; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -38,6 +41,7 @@ public class CreateDistTask extends BaritoneGradleTask { @TaskAction protected void exec() throws Exception { + super.doFirst(); super.verifyArtifacts(); // Define the distribution file paths diff --git a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java index 5dbdd2191..a9c7f94ea 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java @@ -20,27 +20,26 @@ import baritone.gradle.util.Determinizer; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskCollection; import org.gradle.api.tasks.compile.ForkOptions; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.internal.jvm.Jvm; -import xyz.wagyourtail.unimined.api.Constants; -import xyz.wagyourtail.unimined.api.minecraft.EnvType; -import xyz.wagyourtail.unimined.api.minecraft.MinecraftProvider; +import xyz.wagyourtail.unimined.api.UniminedExtension; +import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig; import java.io.*; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - /** * @author Brady * @since 10/11/2018 @@ -50,7 +49,9 @@ public class ProguardTask extends BaritoneGradleTask { @Input private String url; - MinecraftProvider provider = this.getProject().getExtensions().getByType(MinecraftProvider.class); + public String getUrl() { + return url; + } @Input private String extract; @@ -59,41 +60,42 @@ public String getExtract() { return extract; } - @Input - private String compType; - - public String getCompType() { - return compType; - } + private List requiredLibraries; @TaskAction protected void exec() throws Exception { + super.doFirst(); super.verifyArtifacts(); // "Haha brady why don't you make separate tasks" - processArtifact(); downloadProguard(); extractProguard(); generateConfigs(); + processArtifact(); proguardApi(); proguardStandalone(); cleanup(); } - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } + UniminedExtension ext = getProject().getExtensions().getByType(UniminedExtension.class); + SourceSetContainer sourceSets = getProject().getExtensions().getByType(SourceSetContainer.class); private File getMcJar() { - return provider.getMinecraftWithMapping(EnvType.COMBINED, provider.getMcPatcher().getProdNamespace(), provider.getMcPatcher().getProdFallbackNamespace()).toFile(); + MinecraftConfig mcc = ext.getMinecrafts().get(sourceSets.getByName("main")); + return mcc.getMinecraft(mcc.getMcPatcher().getProdNamespace(), mcc.getMcPatcher().getProdNamespace()).toFile(); } private boolean isMcJar(File f) { - return this.getProject().getConfigurations().getByName(Constants.MINECRAFT_COMBINED_PROVIDER).getFiles().contains(f); + MinecraftConfig mcc = ext.getMinecrafts().get(sourceSets.getByName("main")); + return mcc.isMinecraftJar(f.toPath()); + } + + private void processArtifact() throws Exception { + if (Files.exists(this.artifactUnoptimizedPath)) { + Files.delete(this.artifactUnoptimizedPath); + } + + Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString(), List.of(), false); } private void downloadProguard() throws Exception { @@ -123,6 +125,9 @@ private String getJavaBinPathForProguard() throws Exception { ex.printStackTrace(); } + path = findJavaByGradleCurrentRuntime(); + if (path != null) return path; + try { path = findJavaByJavaHome(); if (path != null) return path; @@ -131,33 +136,22 @@ private String getJavaBinPathForProguard() throws Exception { ex.printStackTrace(); } - - path = findJavaByGradleCurrentRuntime(); - if (path != null) return path; - throw new Exception("Unable to find java to determine ProGuard libraryjars. Please specify forkOptions.executable in javaCompile," + " JAVA_HOME environment variable, or make sure to run Gradle with the correct JDK (a v1.8 only)"); } private String findJavaByGradleCurrentRuntime() { String path = Jvm.current().getJavaExecutable().getAbsolutePath(); - - if (this.validateJavaVersion(path)) { - System.out.println("Using Gradle's runtime Java for ProGuard"); - return path; - } - return null; + System.out.println("Using Gradle's runtime Java for ProGuard"); + return path; } private String findJavaByJavaHome() { final String javaHomeEnv = System.getenv("JAVA_HOME"); if (javaHomeEnv != null) { - String path = Jvm.forHome(new File(javaHomeEnv)).getJavaExecutable().getAbsolutePath(); - if (this.validateJavaVersion(path)) { - System.out.println("Detected Java path by JAVA_HOME"); - return path; - } + System.out.println("Detected Java path by JAVA_HOME"); + return path; } return null; } @@ -173,19 +167,11 @@ private String findJavaPathByGradleConfig() { if (javacPath != null) { File javacFile = new File(javacPath); if (javacFile.exists()) { - File[] maybeJava = javacFile.getParentFile().listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.equals("java"); - } - }); - + File[] maybeJava = javacFile.getParentFile().listFiles((dir, name) -> name.equals("java")); if (maybeJava != null && maybeJava.length > 0) { String path = maybeJava[0].getAbsolutePath(); - if (this.validateJavaVersion(path)) { - System.out.println("Detected Java path by forkOptions"); - return path; - } + System.out.println("Detected Java path by forkOptions"); + return path; } } } @@ -207,32 +193,13 @@ private boolean validateJavaVersion(String java) { return true; } - private void processArtifact() throws Exception { - if (Files.exists(this.artifactUnoptimizedPath)) { - Files.delete(this.artifactUnoptimizedPath); - } - - Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString()); - } - - private Stream acquireDependencies() { - return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().findByName("main").getCompileClasspath().getFiles() - .stream() - .filter(File::isFile); - } - - private void proguardApi() throws Exception { - runProguard(getTemporaryFile(compType + PROGUARD_API_CONFIG)); - Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString()); - } - private void generateConfigs() throws Exception { - Files.copy(getRootRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), REPLACE_EXISTING); + Files.copy(getRootRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), StandardCopyOption.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)); + template.add(0, "-injars '" + this.artifactPath.toString() + "'"); + template.add(1, "-outjars '" + this.getTemporaryFile(PROGUARD_EXPORT_PATH) + "'"); template.add(2, "-libraryjars /jmods/java.base.jmod(!**.jar;!module-info.class)"); template.add(3, "-libraryjars /jmods/java.desktop.jmod(!**.jar;!module-info.class)"); @@ -264,7 +231,7 @@ private void generateConfigs() throws Exception { Files.createDirectories(this.getRootRelativeFile(PROGUARD_MAPPING_DIR)); List api = new ArrayList<>(template); - api.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + compType + "-api.txt")); + api.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + addCompTypeFirst("api.txt"))); // API config doesn't require any changes from the changes that we made to the template Files.write(getTemporaryFile(compType + PROGUARD_API_CONFIG), api); @@ -272,39 +239,75 @@ private void generateConfigs() throws Exception { // 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")); - standalone.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + compType + "-standalone.txt")); + standalone.add(2, "-printmapping " + new File(this.getRootRelativeFile(PROGUARD_MAPPING_DIR).toFile(), "mappings-" + addCompTypeFirst("standalone.txt"))); Files.write(getTemporaryFile(compType + PROGUARD_STANDALONE_CONFIG), standalone); } + private Stream acquireDependencies() { + return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().findByName("main").getCompileClasspath().getFiles() + .stream() + .filter(File::isFile); + } + + private void proguardApi() throws Exception { + runProguard(getTemporaryFile(compType + PROGUARD_API_CONFIG)); + Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString(), List.of(), false); + } + + private void proguardStandalone() throws Exception { + runProguard(getTemporaryFile(compType + PROGUARD_STANDALONE_CONFIG)); + Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString(), List.of(), false); + } + + private static final class Pair { + public final A a; + public final B b; + + private Pair(final A a, final B b) { + this.a = a; + this.b = b; + } + + @Override + public String toString() { + return "Pair{" + + "a=" + this.a + + ", " + + "b=" + this.b + + '}'; + } + } + private void cleanup() { try { Files.delete(this.proguardOut); - } catch (IOException ignored) { - } + } catch (IOException ignored) {} } - private void proguardStandalone() throws Exception { - runProguard(getTemporaryFile(compType + PROGUARD_STANDALONE_CONFIG)); - Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString()); + public void setUrl(String url) { + this.url = url; } + public void setExtract(String extract) { this.extract = extract; } - public void setCompType(String compType) { - this.compType = compType; - } - 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); + // Make paths relative to work directory; fixes spaces in path to config, @"" doesn't work + Path workingDirectory = getTemporaryFile(""); + Path proguardJar = workingDirectory.relativize(getTemporaryFile(PROGUARD_JAR)); + config = workingDirectory.relativize(config); + + // Honestly, if you still have spaces in your path at this point, you're SOL. + Process p = new ProcessBuilder("java", "-jar", proguardJar.toString(), "@" + config.toString()) - .directory(getTemporaryFile("").toFile()) // Set the working directory to the temporary folder] + .directory(workingDirectory.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 @@ -314,6 +317,7 @@ private void runProguard(Path config) throws Exception { // 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) { + Thread.sleep(1000); throw new IllegalStateException("Proguard exited with code " + exitCode); } } diff --git a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java index fc268cd35..781aaec24 100644 --- a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java +++ b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java @@ -39,10 +39,11 @@ */ public class Determinizer { - public static void determinize(String inputPath, String outputPath) throws IOException { + public static void determinize(String inputPath, String outputPath, List toInclude, boolean doForgeReplacementOfMetaInf) throws IOException { System.out.println("Running Determinizer"); System.out.println(" Input path: " + inputPath); System.out.println(" Output path: " + outputPath); + System.out.println(" Shade: " + toInclude); try ( JarFile jarFile = new JarFile(new File(inputPath)); @@ -64,14 +65,35 @@ public static void determinize(String inputPath, String outputPath) throws IOExc 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()); + JsonElement json = new JsonParser().parse(new InputStreamReader(jarFile.getInputStream(entry))); + jos.write(writeSorted(json).getBytes()); + } else if (entry.getName().equals("META-INF/MANIFEST.MF") && doForgeReplacementOfMetaInf) { // only replace for forge jar + ByteArrayOutputStream cancer = new ByteArrayOutputStream(); + copy(jarFile.getInputStream(entry), cancer); + String manifest = new String(cancer.toByteArray()); + if (!manifest.contains("baritone.launch.BaritoneTweaker")) { + throw new IllegalStateException("unable to replace"); + } + manifest = manifest.replace("baritone.launch.BaritoneTweaker", "org.spongepowered.asm.launch.MixinTweaker"); + jos.write(manifest.getBytes()); } else { copy(jarFile.getInputStream(entry), jos); } } + for (File file : toInclude) { + try (JarFile mixin = new JarFile(file)) { + for (JarEntry entry : mixin.stream().sorted(Comparator.comparing(JarEntry::getName)).collect(Collectors.toList())) { + if (entry.getName().startsWith("META-INF") && !entry.getName().startsWith("META-INF/services")) { + continue; + } + jos.putNextEntry(entry); + copy(mixin.getInputStream(entry), jos); + } + } + } jos.finish(); } + System.out.println("Done with determinizer"); } private static void copy(InputStream is, OutputStream os) throws IOException { @@ -82,7 +104,7 @@ private static void copy(InputStream is, OutputStream os) throws IOException { } } - private static String writeSorted(JsonObject in) throws IOException { + private static String writeSorted(JsonElement in) throws IOException { StringWriter writer = new StringWriter(); JsonWriter jw = new JsonWriter(writer); ORDERED_JSON_WRITER.write(jw, in); diff --git a/fabric/build.gradle b/fabric/build.gradle index b2b42ed03..93f7ec403 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -19,13 +19,15 @@ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask plugins { - id "com.github.johnrengelman.shadow" version "7.0.0" + id "com.github.johnrengelman.shadow" version "8.0.0" } -archivesBaseName = archivesBaseName + "-" + project.name +archivesBaseName = archivesBaseName + "-fabric" -minecraft { - fabric() +unimined.minecraft { + fabric { + loader project.fabric_version + } } configurations { @@ -36,8 +38,6 @@ configurations { } dependencies { - fabric "net.fabricmc:fabric-loader:${project.fabric_version}" - // because of multiple sourcesets `common project(":")` doesn't work for (sourceSet in rootProject.sourceSets) { if (sourceSet == rootProject.sourceSets.test) continue @@ -45,6 +45,7 @@ dependencies { common sourceSet.output shadowCommon sourceSet.output } + include "dev.babbaj:nether-pathfinder:${nether_pathfinder_version}" } processResources { @@ -57,17 +58,17 @@ processResources { shadowJar { configurations = [project.configurations.shadowCommon] - classifier "dev-shadow" + archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar - classifier null + archiveClassifier.set null } jar { - classifier "dev" + archiveClassifier.set "dev" } components.java { @@ -79,20 +80,26 @@ components.java { task proguard(type: ProguardTask) { url 'https://github.com/Guardsquare/proguard/releases/download/v7.2.1/proguard-7.2.1.zip' extract 'proguard-7.2.1/lib/proguard.jar' - compType "FABRIC" + compType "fabric" } -task createDist(type: CreateDistTask, dependsOn: proguard) +task createDist(type: CreateDistTask, dependsOn: proguard) { + compType "fabric" +} build.finalizedBy(createDist) publishing { publications { maven(MavenPublication) { - artifact "../dist/baritone-fabric-api-" + version + ".jar" + groupId "meteordevelopment" + artifactId "baritone" + + artifact "../dist/baritone-api-fabric-" + version + ".jar" } } + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { maven { name = "meteor-maven" @@ -108,4 +115,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/fabric/src/main/java/baritone/launch/FabricMixinPlugin.java b/fabric/src/main/java/baritone/launch/FabricMixinPlugin.java new file mode 100644 index 000000000..655de50d4 --- /dev/null +++ b/fabric/src/main/java/baritone/launch/FabricMixinPlugin.java @@ -0,0 +1,71 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch; + +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class FabricMixinPlugin implements IMixinConfigPlugin { + private static final String mixinPackage = "baritone.launch.mixins"; + + private static boolean loaded; + + private static boolean isBaritonePresent; + + @Override + public void onLoad(String mixinPackage) { + if (loaded) return; + + isBaritonePresent = FabricLoader.getInstance().isModLoaded("baritone"); + + loaded = true; + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (!mixinClassName.startsWith(mixinPackage)) { + throw new RuntimeException("Mixin " + mixinClassName + " is not in the mixin package"); + } else { + return !isBaritonePresent; + } + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) {} + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 3e69814e4..39475b65b 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,4 +1,3 @@ - { "schemaVersion": 1, "id": "baritone-meteor", @@ -7,23 +6,25 @@ "name": "Baritone", "description": "Google Maps for Blockgame", "authors": [ - "leijurv", - "Brady" + "leijurv", "Brady" ], "contact": { "homepage": "https://github.com/cabaletta/baritone", "sources": "https://github.com/cabaletta/baritone" }, + "license": "LGPL-3.0", "icon": "assets/baritone/icon.png", + "environment": "*", "entrypoints": { }, "mixins": [ - "mixins.baritone-meteor.json" + "mixins.baritone-meteor.json" ], + "depends": { - "fabricloader": ">=0.11.0", - "minecraft": "1.19.3" + "fabricloader": ">=0.14.22", + "minecraft": ["1.20.3", "1.20.4"] } -} \ No newline at end of file +} diff --git a/forge/build.gradle b/forge/build.gradle index da2c4e570..6ec55bc12 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -19,15 +19,19 @@ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask plugins { - id "com.github.johnrengelman.shadow" version "7.0.0" + id "com.github.johnrengelman.shadow" version "8.0.0" } -archivesBaseName = archivesBaseName + "-" + project.name +archivesBaseName = archivesBaseName + "-forge" + +unimined.minecraft { + mappings { + devFallbackNamespace "intermediary" + } -minecraft { forge { - devFallbackNamespace = "intermediary" - mixinConfig = ["mixins.baritone.json"] + loader project.forge_version + mixinConfig ["mixins.baritone.json"] } } @@ -45,8 +49,6 @@ configurations { } dependencies { - forge "net.minecraftforge:forge:${rootProject.forge_version}" - // because of multiple sourcesets `common project(":")` doesn't work for (sourceSet in rootProject.sourceSets) { if (sourceSet == rootProject.sourceSets.test) continue @@ -54,6 +56,7 @@ dependencies { common sourceSet.output shadowCommon sourceSet.output } + shadowCommon "dev.babbaj:nether-pathfinder:${nether_pathfinder_version}" } processResources { @@ -66,17 +69,17 @@ processResources { shadowJar { configurations = [project.configurations.shadowCommon] - classifier "dev-shadow" + archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar - classifier null + archiveClassifier.set null } jar { - classifier "dev" + archiveClassifier.set "dev" manifest { attributes( @@ -98,10 +101,12 @@ components.java { task proguard(type: ProguardTask) { url 'https://github.com/Guardsquare/proguard/releases/download/v7.2.1/proguard-7.2.1.zip' extract 'proguard-7.2.1/lib/proguard.jar' - compType "FORGE" + compType "forge" } -task createDist(type: CreateDistTask, dependsOn: proguard) +task createDist(type: CreateDistTask, dependsOn: proguard) { + compType "forge" +} build.finalizedBy(createDist) diff --git a/forge/gradle.properties b/forge/gradle.properties index bda35b14a..24befc474 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -14,4 +14,5 @@ # You should have received a copy of the GNU Lesser General Public License # along with Baritone. If not, see . # + loom.platform=forge \ No newline at end of file diff --git a/forge/src/main/java/baritone/launch/BaritoneForgeModXD.java b/forge/src/main/java/baritone/launch/BaritoneForgeModXD.java index 401a0e664..db84d62e1 100644 --- a/forge/src/main/java/baritone/launch/BaritoneForgeModXD.java +++ b/forge/src/main/java/baritone/launch/BaritoneForgeModXD.java @@ -15,9 +15,7 @@ * along with Baritone. If not, see . */ -package baritone.launch; - -import net.minecraftforge.fml.common.Mod; +package baritone.launch;import net.minecraftforge.fml.common.Mod; @Mod("baritoe") public class BaritoneForgeModXD { diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 58d9594a1..dd309314c 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -4,37 +4,37 @@ # Note that there are a couple of TOML lists in this file. # Find more information on toml format here: https://github.com/toml-lang/toml # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml -modLoader = "javafml" #mandatory +modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the forge version -loaderVersion = "[33,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. -license = "https://raw.githubusercontent.com/cabaletta/baritone/1.16.2/LICENSE" +loaderVersion="[48,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. +license="https://raw.githubusercontent.com/cabaletta/baritone/1.16.2/LICENSE" # A URL to refer people to when problems occur with this mod -issueTrackerURL = "https://github.com/cabaletta/baritone/issues" #optional +issueTrackerURL="https://github.com/cabaletta/baritone/issues" #optional # A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory # The modid of the mod -modId = "baritoe" #mandatory +modId="baritoe" #mandatory # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it -version = "${version}" #mandatory -# A display name for the mod -displayName = "Baritone" #mandatory +version="${version}" #mandatory + # A display name for the mod +displayName="Baritone" #mandatory # A URL for the "homepage" for this mod, displayed in the mod UI -displayURL = "https://github.com/cabaletta/baritone" #optional +displayURL="https://github.com/cabaletta/baritone" #optional # A file name (in the root of the mod JAR) containing a logo for display #logoFile="examplemod.png" #optional # A text field displayed in the mod UI -credits = "Hat Gamers" #optional +credits="Hat Gamers" #optional # A text field displayed in the mod UI -authors = "leijurv, Brady" #optional +authors="leijurv, Brady" #optional # The description text for the mod (multi line!) (#mandatory) -description = ''' +description=''' A Minecraft pathfinder bot. ''' [[dependencies.baritoe]] -modId = "minecraft" -mandatory = true +modId="minecraft" +mandatory=true # This version range declares a minimum of the current minecraft version up to but not including the next major version -versionRange = "[1.19.3]" -ordering = "NONE" -side = "BOTH" \ No newline at end of file +versionRange="[1.20.3, 1.20.4]" +ordering="NONE" +side="BOTH" diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..663d1787a --- /dev/null +++ b/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "null", + "pack_format": 8 + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c1cc61494..c00489491 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,15 @@ org.gradle.jvmargs=-Xmx4G -mod_version=1.19.3-beta1 + +available_loaders=fabric +mod_version=1.20.4-beta1 maven_group=baritone archives_base_name=baritone -minecraft_version=1.19.3 -forge_version=1.19.3-44.0.1 -fabric_version=0.14.11 \ No newline at end of file + +minecraft_version=1.20.4 + +forge_version=49.0.3 +neoforge_version=0-beta + +fabric_version=0.14.22 + +nether_pathfinder_version=1.4.1 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e5fb3413d..17655d0ef 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Feb 06 02:33:37 CST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c8..744e882ed 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) diff --git a/jitpack.yml b/jitpack.yml index 8a6523f7e..577b01763 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,3 +1,4 @@ before_install: - - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh - - source install-jdk.sh --feature 17 \ No newline at end of file + - curl -s "https://get.sdkman.io" | bash + - sdk install java 17.0.5-tem + - sdk use java 17.0.5-tem diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 000000000..ac102dace --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,134 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +import baritone.gradle.task.CreateDistTask +import baritone.gradle.task.ProguardTask + +plugins { + id "com.github.johnrengelman.shadow" version "8.0.0" +} + +archivesBaseName = archivesBaseName + "-neoforge" + +unimined.minecraft { + mappings { + devFallbackNamespace "intermediary" + } + + neoForged { + loader project.neoforge_version + mixinConfig ["mixins.baritone.json"] + } + + minecraftRemapper.config { + // neoforge adds 1 conflict, where 2 interfaces have a method with the same name on yarn/mojmap, + // but the method has different names in the intermediary mappings. + // this is a conflict because they have a class that extends both interfaces. + // this shouldn't be a problem as long as named mappings don't make the name of those 2 methods different. + ignoreConflicts(true) + } + +} + +//loom { +// forge { +// mixinConfig 'mixins.baritone.json' +// } +//} + +configurations { + common + shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common +} + +dependencies { + // because of multiple sourcesets `common project(":")` doesn't work + for (sourceSet in rootProject.sourceSets) { + if (sourceSet == rootProject.sourceSets.test) continue + if (sourceSet == rootProject.sourceSets.schematica_api) continue + common sourceSet.output + shadowCommon sourceSet.output + } + shadowCommon "dev.babbaj:nether-pathfinder:${nether_pathfinder_version}" +} + +processResources { + inputs.property "version", project.version + + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} + +shadowJar { + configurations = [project.configurations.shadowCommon] + archiveClassifier.set "dev-shadow" +} + +remapJar { + inputFile.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier.set null +} + +jar { + archiveClassifier.set "dev" + + manifest { + attributes( + 'MixinConfigs': 'mixins.baritone.json', + "MixinConnector": "baritone.launch.BaritoneMixinConnector", + + 'Implementation-Title': 'Baritone', + 'Implementation-Version': version, + ) + } +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} + +task proguard(type: ProguardTask) { + url 'https://github.com/Guardsquare/proguard/releases/download/v7.2.1/proguard-7.2.1.zip' + extract 'proguard-7.2.1/lib/proguard.jar' + compType "neoforge" +} + +task createDist(type: CreateDistTask, dependsOn: proguard) { + compType "neoforge" +} + +build.finalizedBy(createDist) + +publishing { + publications { + mavenFabric(MavenPublication) { + artifactId = rootProject.archives_base_name + "-" + project.name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} \ No newline at end of file diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 000000000..24befc474 --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1,18 @@ +# +# This file is part of Baritone. +# +# Baritone is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Baritone is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Baritone. If not, see . +# + +loom.platform=forge \ No newline at end of file diff --git a/neoforge/src/main/java/baritone/launch/BaritoneForgeModXD.java b/neoforge/src/main/java/baritone/launch/BaritoneForgeModXD.java new file mode 100644 index 000000000..de097320d --- /dev/null +++ b/neoforge/src/main/java/baritone/launch/BaritoneForgeModXD.java @@ -0,0 +1,24 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch; + +import net.neoforged.fml.common.Mod; + +@Mod("baritoe") +public class BaritoneForgeModXD { +} diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neoforge/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..4ad41a286 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,40 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory +# A version range to match for said mod loader - for regular FML @Mod it will be the forge version +loaderVersion="[1,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. +license="https://raw.githubusercontent.com/cabaletta/baritone/1.16.2/LICENSE" +# A URL to refer people to when problems occur with this mod +issueTrackerURL="https://github.com/cabaletta/baritone/issues" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId="baritoe" #mandatory +# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it +version="${version}" #mandatory + # A display name for the mod +displayName="Baritone" #mandatory +# A URL for the "homepage" for this mod, displayed in the mod UI +displayURL="https://github.com/cabaletta/baritone" #optional +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional +# A text field displayed in the mod UI +credits="Hat Gamers" #optional +# A text field displayed in the mod UI +authors="leijurv, Brady" #optional +# The description text for the mod (multi line!) (#mandatory) +description=''' +A Minecraft pathfinder bot. +''' + +[[dependencies.baritoe]] +modId="minecraft" +mandatory=true +# This version range declares a minimum of the current minecraft version up to but not including the next major version +versionRange="[1.20.3, 1.20.4]" +ordering="NONE" +side="BOTH" diff --git a/neoforge/src/main/resources/pack.mcmeta b/neoforge/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..663d1787a --- /dev/null +++ b/neoforge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "null", + "pack_format": 8 + } +} \ No newline at end of file diff --git a/scripts/proguard.pro b/scripts/proguard.pro index 1ea261ea1..13c10650c 100644 --- a/scripts/proguard.pro +++ b/scripts/proguard.pro @@ -1,5 +1,8 @@ +# Meteor -keep class * + + -keepattributes Signature -keepattributes *Annotation* -keepattributes InnerClasses @@ -14,13 +17,15 @@ # instead of renaming to a, b, c, rename to baritone.a, baritone.b, baritone.c so as to not conflict with minecraft's obfd classes -flattenpackagehierarchy -repackageclasses 'baritone' --ignorewarnings + # lwjgl is weird -dontwarn org.lwjgl.** # also lwjgl lol -dontwarn module-info # we dont have forge -dontwarn baritone.launch.BaritoneForgeModXD +# progard doesn't like signature polymorphism +-dontwarn java.lang.invoke.MethodHandle # please do not change the comment below -keep class baritone.api.** { *; } # this is the keep api diff --git a/settings.gradle b/settings.gradle index 7247a2843..89e47805a 100755 --- a/settings.gradle +++ b/settings.gradle @@ -42,10 +42,6 @@ pluginManagement { rootProject.name = 'baritone' include("tweaker") -if (System.getProperty("Baritone.enabled_platforms") == null) { - //System.setProperty("Baritone.enabled_platforms", "fabric,forge") - System.setProperty("Baritone.enabled_platforms", "fabric") -} -for (platform in System.getProperty("Baritone.enabled_platforms").split(",")) { +for (platform in available_loaders.split(",")) { include(platform) } diff --git a/src/api/java/baritone/api/BaritoneAPI.java b/src/api/java/baritone/api/BaritoneAPI.java index 4a87449cd..fa98525bf 100644 --- a/src/api/java/baritone/api/BaritoneAPI.java +++ b/src/api/java/baritone/api/BaritoneAPI.java @@ -32,7 +32,7 @@ public final class BaritoneAPI { static { settings = new Settings(); - SettingsUtil.readAndApply(settings); + SettingsUtil.readAndApply(settings, SettingsUtil.SETTINGS_DEFAULT_NAME); try { provider = (IBaritoneProvider) Class.forName("baritone.BaritoneProvider").newInstance(); diff --git a/src/api/java/baritone/api/IBaritone.java b/src/api/java/baritone/api/IBaritone.java index 0cc73b0d1..3c9681532 100644 --- a/src/api/java/baritone/api/IBaritone.java +++ b/src/api/java/baritone/api/IBaritone.java @@ -88,6 +88,12 @@ public interface IBaritone { */ IGetToBlockProcess getGetToBlockProcess(); + /** + * @return The {@link IElytraProcess} instance + * @see IElytraProcess + */ + IElytraProcess getElytraProcess(); + /** * @return The {@link IWorldProvider} instance * @see IWorldProvider diff --git a/src/api/java/baritone/api/IBaritoneProvider.java b/src/api/java/baritone/api/IBaritoneProvider.java index 3fdd8de8b..1daec2ef5 100644 --- a/src/api/java/baritone/api/IBaritoneProvider.java +++ b/src/api/java/baritone/api/IBaritoneProvider.java @@ -21,6 +21,8 @@ import baritone.api.command.ICommand; import baritone.api.command.ICommandSystem; import baritone.api.schematic.ISchematicSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.player.LocalPlayer; import java.util.List; @@ -52,15 +54,13 @@ public interface IBaritoneProvider { List getAllBaritones(); /** - * Provides the {@link IBaritone} instance for a given {@link LocalPlayer}. This will likely be - * replaced with or be overloaded in addition to {@code #getBaritoneForUser(IBaritoneUser)} when - * {@code bot-system} is merged into {@code master}. + * Provides the {@link IBaritone} instance for a given {@link LocalPlayer}. * * @param player The player * @return The {@link IBaritone} instance. */ default IBaritone getBaritoneForPlayer(LocalPlayer player) { - for (IBaritone baritone : getAllBaritones()) { + for (IBaritone baritone : this.getAllBaritones()) { if (Objects.equals(player, baritone.getPlayerContext().player())) { return baritone; } @@ -68,6 +68,55 @@ default IBaritone getBaritoneForPlayer(LocalPlayer player) { return null; } + /** + * Provides the {@link IBaritone} instance for a given {@link Minecraft}. + * + * @param minecraft The minecraft + * @return The {@link IBaritone} instance. + */ + default IBaritone getBaritoneForMinecraft(Minecraft minecraft) { + for (IBaritone baritone : this.getAllBaritones()) { + if (Objects.equals(minecraft, baritone.getPlayerContext().minecraft())) { + return baritone; + } + } + return null; + } + + /** + * Provides the {@link IBaritone} instance for the player with the specified connection. + * + * @param connection The connection + * @return The {@link IBaritone} instance. + */ + default IBaritone getBaritoneForConnection(ClientPacketListener connection) { + for (IBaritone baritone : this.getAllBaritones()) { + final LocalPlayer player = baritone.getPlayerContext().player(); + if (player != null && player.connection == connection) { + return baritone; + } + } + return null; + } + + /** + * Creates and registers a new {@link IBaritone} instance using the specified {@link Minecraft}. The existing + * instance is returned if already registered. + * + * @param minecraft The minecraft + * @return The {@link IBaritone} instance + */ + IBaritone createBaritone(Minecraft minecraft); + + /** + * Destroys and removes the specified {@link IBaritone} instance. If the specified instance is the + * {@link #getPrimaryBaritone() primary baritone}, this operation has no effect and will return {@code false}. + * + * @param baritone The baritone instance to remove + * @return Whether the baritone instance was removed + */ + boolean destroyBaritone(IBaritone baritone); + /** * Returns the {@link IWorldScanner} instance. This is not a type returned by * {@link IBaritone} implementation, because it is not linked with {@link IBaritone}. diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 393820e13..be9830ae6 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -17,10 +17,12 @@ package baritone.api; +import baritone.api.utils.Helper; import baritone.api.utils.NotificationHelper; import baritone.api.utils.SettingsUtil; import baritone.api.utils.TypeUtils; import baritone.api.utils.gui.BaritoneToast; +import net.minecraft.client.GuiMessageTag; import net.minecraft.client.Minecraft; import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; @@ -29,11 +31,16 @@ import net.minecraft.world.level.block.Blocks; import java.awt.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -69,6 +76,16 @@ public final class Settings { */ public final Setting allowInventory = new Setting<>(false); + /** + * Wait this many ticks between InventoryBehavior moving inventory items + */ + public final Setting ticksBetweenInventoryMoves = new Setting<>(1); + + /** + * Come to a halt before doing any inventory moves. Intended for anticheat such as 2b2t + */ + public final Setting inventoryMoveOnlyIfStationary = new Setting<>(false); + /** * Disable baritone's auto-tool at runtime, but still assume that another mod will provide auto tool functionality *

@@ -107,6 +124,13 @@ public final class Settings { */ public final Setting walkOnWaterOnePenalty = new Setting<>(3D); + /** + * Don't allow breaking blocks next to liquids. + *

+ * Enable if you have mods adding custom fluid physics. + */ + public final Setting strictLiquidCheck = new Setting<>(false); + /** * Allow Baritone to fall arbitrary distances and place a water bucket beneath it. * Reliability: questionable. @@ -116,6 +140,8 @@ public final class Settings { /** * Allow Baritone to assume it can walk on still water just like any other block. * This functionality is assumed to be provided by a separate library that might have imported Baritone. + *

+ * Note: This will prevent some usage of the frostwalker enchantment, like pillaring up from water. */ public final Setting assumeWalkOnWater = new Setting<>(false); @@ -276,6 +302,12 @@ public final class Settings { */ public final Setting buildIgnoreDirection = new Setting<>(false); + /** + * A list of names of block properties the builder will ignore. + */ + public final Setting> buildIgnoreProperties = new Setting<>(new ArrayList<>(Arrays.asList( + ))); + /** * If this setting is true, Baritone will never break a block that is adjacent to an unsupported falling block. *

@@ -595,6 +627,13 @@ public final class Settings { */ public final Setting pruneRegionsFromRAM = new Setting<>(true); + /** + * The chunk packer queue can never grow to larger than this, if it does, the oldest chunks are discarded + *

+ * The newest chunks are kept, so that if you're moving in a straight line quickly then stop, your immediate render distance is still included + */ + public final Setting chunkPackerQueueMaxSize = new Setting<>(2000); + /** * Fill in blocks behind you */ @@ -696,6 +735,37 @@ public final class Settings { */ public final Setting freeLook = new Setting<>(true); + /** + * Break and place blocks without having to force the client-sided rotations. Requires {@link #freeLook}. + */ + public final Setting blockFreeLook = new Setting<>(false); + + /** + * Automatically elytra fly without having to force the client-sided rotations. + */ + public final Setting elytraFreeLook = new Setting<>(true); + + /** + * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. + */ + public final Setting smoothLook = new Setting<>(false); + + /** + * Same as {@link #smoothLook} but for elytra flying. + */ + public final Setting elytraSmoothLook = new Setting<>(false); + + /** + * The number of ticks to average across for {@link #smoothLook}; + */ + public final Setting smoothLookTicks = new Setting<>(5); + + /** + * When true, the player will remain with its existing look direction as often as possible. + * Although, in some cases this can get it stuck, hence this setting to disable that behavior. + */ + public final Setting remainWithExistingLookDirection = new Setting<>(true); + /** * Will cause some minor behavioral differences to ensure that Baritone works on anticheats. *

@@ -758,6 +828,11 @@ public final class Settings { */ public final Setting shortBaritonePrefix = new Setting<>(false); + /** + * Use a modern message tag instead of a prefix when logging to chat + */ + public final Setting useMessageTag = new Setting<>(false); + /** * Echo commands to chat when they are run */ @@ -831,6 +906,11 @@ public final class Settings { */ public final Setting minYLevelWhileMining = new Setting<>(0); + /** + * Sets the maximum y level to mine ores at. + */ + public final Setting maxYLevelWhileMining = new Setting<>(2031); + /** * This will only allow baritone to mine exposed ores, can be used to stop ore obfuscators on servers that use them. */ @@ -1127,13 +1207,18 @@ public final class Settings { * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ - public final Setting> logger = new Setting<>(msg -> Minecraft.getInstance().gui.getChat().addMessage(msg)); + @JavaOnly + public final Setting> logger = new Setting<>((msg) -> { + final GuiMessageTag tag = useMessageTag.value ? Helper.MESSAGE_TAG : null; + Minecraft.getInstance().gui.getChat().addMessage(msg, null, tag); + }); /** * The function that is called when Baritone will send a desktop notification. This function can be added to * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ + @JavaOnly public final Setting> notifier = new Setting<>(NotificationHelper::notify); /** @@ -1141,6 +1226,7 @@ public final class Settings { * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting * {@link Setting#value}; */ + @JavaOnly public final Setting> toaster = new Setting<>(BaritoneToast::addOrUpdate); /** @@ -1273,6 +1359,120 @@ public final class Settings { */ public final Setting notificationOnMineFail = new Setting<>(true); + /** + * The number of ticks of elytra movement to simulate while firework boost is not active. Higher values are + * computationally more expensive. + */ + public final Setting elytraSimulationTicks = new Setting<>(20); + + /** + * The maximum allowed deviation in pitch from a direct line-of-sight to the flight target. Higher values are + * computationally more expensive. + */ + public final Setting elytraPitchRange = new Setting<>(25); + + /** + * The minimum speed that the player can drop to (in blocks/tick) before a firework is automatically deployed. + */ + public final Setting elytraFireworkSpeed = new Setting<>(1.2); + + /** + * The delay after the player's position is set-back by the server that a firework may be automatically deployed. + * Value is in ticks. + */ + public final Setting elytraFireworkSetbackUseDelay = new Setting<>(15); + + /** + * The minimum padding value that is added to the player's hitbox when considering which point to fly to on the + * path. High values can result in points not being considered which are otherwise safe to fly to. Low values can + * result in flight paths which are extremely tight, and there's the possibility of crashing due to getting too low + * to the ground. + */ + public final Setting elytraMinimumAvoidance = new Setting<>(0.2); + + /** + * If enabled, avoids using fireworks when descending along the flight path. + */ + public final Setting elytraConserveFireworks = new Setting<>(false); + + /** + * Renders the raytraces that are performed by the elytra fly calculation. + */ + public final Setting elytraRenderRaytraces = new Setting<>(false); + + /** + * Renders the raytraces that are used in the hitbox part of the elytra fly calculation. + * Requires {@link #elytraRenderRaytraces}. + */ + public final Setting elytraRenderHitboxRaytraces = new Setting<>(false); + + /** + * Renders the best elytra flight path that was simulated each tick. + */ + public final Setting elytraRenderSimulation = new Setting<>(true); + + /** + * Automatically path to and jump off of ledges to initiate elytra flight when grounded. + */ + public final Setting elytraAutoJump = new Setting<>(false); + + /** + * The seed used to generate chunks for long distance elytra path-finding in the nether. + * Defaults to 2b2t's nether seed. + */ + public final Setting elytraNetherSeed = new Setting<>(146008555100680L); + + /** + * Whether nether-pathfinder should generate terrain based on {@link #elytraNetherSeed}. + * If false all chunks that haven't been loaded are assumed to be air. + */ + public final Setting elytraPredictTerrain = new Setting<>(false); + + /** + * Automatically swap the current elytra with a new one when the durability gets too low + */ + public final Setting elytraAutoSwap = new Setting<>(true); + + /** + * The minimum durability an elytra can have before being swapped + */ + public final Setting elytraMinimumDurability = new Setting<>(5); + + /** + * The minimum fireworks before landing early for safety + */ + public final Setting elytraMinFireworksBeforeLanding = new Setting<>(5); + + /** + * Automatically land when elytra is almost out of durability, or almost out of fireworks + */ + public final Setting elytraAllowEmergencyLand = new Setting<>(true); + + /** + * Time between culling far away chunks from the nether pathfinder chunk cache + */ + public final Setting elytraTimeBetweenCacheCullSecs = new Setting<>(TimeUnit.MINUTES.toSeconds(3)); + + /** + * Maximum distance chunks can be before being culled from the nether pathfinder chunk cache + */ + public final Setting elytraCacheCullDistance = new Setting<>(5000); + + /** + * Should elytra consider nether brick a valid landing block + */ + public final Setting elytraAllowLandOnNetherFortress = new Setting<>(false); + + /** + * Has the user read and understood the elytra terms and conditions + */ + public final Setting elytraTermsAccepted = new Setting<>(false); + + /** + * Verbose chat logging in elytra mode + */ + public final Setting elytraChatSpam = new Setting<>(false); + /** * A map of lowercase setting field names to their respective setting */ @@ -1290,6 +1490,7 @@ public final class Setting { public T value; public final T defaultValue; private String name; + private boolean javaOnly; @SuppressWarnings("unchecked") private Setting(T value) { @@ -1298,6 +1499,7 @@ private Setting(T value) { } this.value = value; this.defaultValue = value; + this.javaOnly = false; } /** @@ -1334,8 +1536,25 @@ public void reset() { public final Type getType() { return settingTypes.get(this); } + + /** + * This should always be the same as whether the setting can be parsed from or serialized to a string; in other + * words, the only way to modify it is by writing to {@link #value} programatically. + * + * @return {@code true} if the setting can not be set or read by the user + */ + public boolean isJavaOnly() { + return javaOnly; + } } + /** + * Marks a {@link Setting} field as being {@link Setting#isJavaOnly() Java-only} + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + private @interface JavaOnly {} + // here be dragons Settings() { @@ -1351,6 +1570,7 @@ public final Type getType() { Setting setting = (Setting) field.get(this); String name = field.getName(); setting.name = name; + setting.javaOnly = field.isAnnotationPresent(JavaOnly.class); name = name.toLowerCase(); if (tmpByName.containsKey(name)) { throw new IllegalStateException("Duplicate setting name"); diff --git a/src/api/java/baritone/api/behavior/IBehavior.java b/src/api/java/baritone/api/behavior/IBehavior.java index 95fb22344..811563b93 100644 --- a/src/api/java/baritone/api/behavior/IBehavior.java +++ b/src/api/java/baritone/api/behavior/IBehavior.java @@ -27,5 +27,4 @@ * @see IGameEventListener * @since 9/23/2018 */ -public interface IBehavior extends AbstractGameEventListener { -} +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 058a5dd88..d78e7f8b3 100644 --- a/src/api/java/baritone/api/behavior/ILookBehavior.java +++ b/src/api/java/baritone/api/behavior/ILookBehavior.java @@ -17,6 +17,8 @@ package baritone.api.behavior; +import baritone.api.Settings; +import baritone.api.behavior.look.IAimProcessor; import baritone.api.utils.Rotation; /** @@ -26,14 +28,23 @@ public interface ILookBehavior extends IBehavior { /** - * Updates the current {@link ILookBehavior} target to target - * the specified rotations on the next tick. If force is {@code true}, - * then freeLook will be overriden and angles will be set regardless. - * If any sort of block interaction is required, force should be {@code true}, - * otherwise, it should be {@code false}; + * Updates the current {@link ILookBehavior} target to target the specified rotations on the next tick. If any sort + * of block interaction is required, {@code blockInteract} should be {@code true}. It is not guaranteed that the + * rotations set by the caller will be the exact rotations expressed by the client (This is due to settings like + * {@link Settings#randomLooking}). If the rotations produced by this behavior are required, then the + * {@link #getAimProcessor() aim processor} should be used. * - * @param rotation The target rotations - * @param force Whether or not to "force" the rotations + * @param rotation The target rotations + * @param blockInteract Whether the target rotations are needed for a block interaction */ - void updateTarget(Rotation rotation, boolean force); + void updateTarget(Rotation rotation, boolean blockInteract); + + /** + * The aim processor instance for this {@link ILookBehavior}, which is responsible for applying additional, + * deterministic transformations to the target rotation set by {@link #updateTarget}. + * + * @return The aim processor + * @see IAimProcessor#fork + */ + IAimProcessor getAimProcessor(); } diff --git a/src/api/java/baritone/api/behavior/look/IAimProcessor.java b/src/api/java/baritone/api/behavior/look/IAimProcessor.java new file mode 100644 index 000000000..c7c60f413 --- /dev/null +++ b/src/api/java/baritone/api/behavior/look/IAimProcessor.java @@ -0,0 +1,45 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.behavior.look; + +import baritone.api.utils.Rotation; + +/** + * @author Brady + */ +public interface IAimProcessor { + + /** + * Returns the actual rotation that will be used when the desired rotation is requested. The returned rotation + * always reflects what would happen in the upcoming tick. In other words, it is a pure function, and no internal + * state changes. If simulation of the rotation states beyond the next tick is required, then a + * {@link IAimProcessor#fork fork} should be created. + * + * @param desired The desired rotation to set + * @return The actual rotation + */ + Rotation peekRotation(Rotation desired); + + /** + * Returns a copy of this {@link IAimProcessor} which has its own internal state and is manually tickable. + * + * @return The forked processor + * @see ITickableAimProcessor + */ + ITickableAimProcessor fork(); +} diff --git a/src/api/java/baritone/api/behavior/look/ITickableAimProcessor.java b/src/api/java/baritone/api/behavior/look/ITickableAimProcessor.java new file mode 100644 index 000000000..e0a07ae57 --- /dev/null +++ b/src/api/java/baritone/api/behavior/look/ITickableAimProcessor.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.behavior.look; + +import baritone.api.utils.Rotation; + +/** + * @author Brady + */ +public interface ITickableAimProcessor extends IAimProcessor { + + /** + * Advances the internal state of this aim processor by a single tick. + */ + void tick(); + + /** + * Calls {@link #tick()} the specified number of times. + * + * @param ticks The number of calls + */ + void advance(int ticks); + + /** + * Returns the actual rotation as provided by {@link #peekRotation(Rotation)}, and then automatically advances the + * internal state by one {@link #tick() tick}. + * + * @param rotation The desired rotation to set + * @return The actual rotation + */ + Rotation nextRotation(Rotation rotation); +} diff --git a/src/api/java/baritone/api/cache/ICachedWorld.java b/src/api/java/baritone/api/cache/ICachedWorld.java index 120ca8da4..6e74fa55a 100644 --- a/src/api/java/baritone/api/cache/ICachedWorld.java +++ b/src/api/java/baritone/api/cache/ICachedWorld.java @@ -17,11 +17,10 @@ package baritone.api.cache; +import java.util.ArrayList; import net.minecraft.core.BlockPos; import net.minecraft.world.level.chunk.LevelChunk; -import java.util.ArrayList; - /** * @author Brady * @since 9/24/2018 diff --git a/src/api/java/baritone/api/cache/IWorldProvider.java b/src/api/java/baritone/api/cache/IWorldProvider.java index 0e54ef469..b9ca149c7 100644 --- a/src/api/java/baritone/api/cache/IWorldProvider.java +++ b/src/api/java/baritone/api/cache/IWorldProvider.java @@ -17,6 +17,8 @@ package baritone.api.cache; +import java.util.function.Consumer; + /** * @author Brady * @since 9/24/2018 @@ -29,4 +31,11 @@ public interface IWorldProvider { * @return The current world data */ IWorldData getCurrentWorld(); + + default void ifWorldLoaded(Consumer callback) { + final IWorldData currentWorld = this.getCurrentWorld(); + if (currentWorld != null) { + callback.accept(currentWorld); + } + } } diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java index d8cf2bd38..ea27dd161 100644 --- a/src/api/java/baritone/api/cache/IWorldScanner.java +++ b/src/api/java/baritone/api/cache/IWorldScanner.java @@ -19,12 +19,11 @@ import baritone.api.utils.BlockOptionalMetaLookup; import baritone.api.utils.IPlayerContext; +import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Block; -import java.util.List; - /** * @author Brady * @since 10/6/2018 diff --git a/src/api/java/baritone/api/command/argument/IArgConsumer.java b/src/api/java/baritone/api/command/argument/IArgConsumer.java index 833d67736..894e4a71b 100644 --- a/src/api/java/baritone/api/command/argument/IArgConsumer.java +++ b/src/api/java/baritone/api/command/argument/IArgConsumer.java @@ -27,11 +27,10 @@ import baritone.api.command.exception.CommandNotEnoughArgumentsException; import baritone.api.command.exception.CommandTooManyArgumentsException; import baritone.api.utils.Helper; -import net.minecraft.core.Direction; - import java.util.Deque; import java.util.LinkedList; import java.util.stream.Stream; +import net.minecraft.core.Direction; /** * The {@link IArgConsumer} is how {@link ICommand}s read the arguments passed to them. This class has many benefits: diff --git a/src/api/java/baritone/api/command/datatypes/BlockById.java b/src/api/java/baritone/api/command/datatypes/BlockById.java index 21562e188..f92c968a2 100644 --- a/src/api/java/baritone/api/command/datatypes/BlockById.java +++ b/src/api/java/baritone/api/command/datatypes/BlockById.java @@ -23,11 +23,17 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; +import java.util.regex.Pattern; import java.util.stream.Stream; public enum BlockById implements IDatatypeFor { INSTANCE; + /** + * Matches (domain:)?name? where domain and name are [a-z0-9_.-]+ and [a-z0-9/_.-]+ respectively. + */ + private static Pattern PATTERN = Pattern.compile("(?:[a-z0-9_.-]+:)?[a-z0-9/_.-]*"); + @Override public Block get(IDatatypeContext ctx) throws CommandException { ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString()); @@ -40,13 +46,19 @@ public Block get(IDatatypeContext ctx) throws CommandException { @Override public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + String arg = ctx.getConsumer().getString(); + + if (!PATTERN.matcher(arg).matches()) { + return Stream.empty(); + } + return new TabCompleteHelper() .append( BuiltInRegistries.BLOCK.keySet() .stream() .map(Object::toString) ) - .filterPrefixNamespaced(ctx.getConsumer().getString()) + .filterPrefixNamespaced(arg) .sortAlphabetically() .stream(); } diff --git a/src/api/java/baritone/api/command/datatypes/ForAxis.java b/src/api/java/baritone/api/command/datatypes/ForAxis.java new file mode 100644 index 000000000..369697dcb --- /dev/null +++ b/src/api/java/baritone/api/command/datatypes/ForAxis.java @@ -0,0 +1,43 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.command.datatypes; + +import baritone.api.command.exception.CommandException; +import baritone.api.command.helpers.TabCompleteHelper; +import net.minecraft.core.Direction; + +import java.util.Locale; +import java.util.stream.Stream; + +public enum ForAxis implements IDatatypeFor { + INSTANCE; + + @Override + public Direction.Axis get(IDatatypeContext ctx) throws CommandException { + return Direction.Axis.valueOf(ctx.getConsumer().getString().toUpperCase(Locale.US)); + } + + @Override + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + return new TabCompleteHelper() + .append(Stream.of(Direction.Axis.values()) + .map(Direction.Axis::getName).map(String::toLowerCase)) + .filterPrefix(ctx.getConsumer().getString()) + .stream(); + } +} diff --git a/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java b/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java index 978450a23..5b949cd63 100644 --- a/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java +++ b/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java @@ -18,20 +18,137 @@ package baritone.api.command.datatypes; import baritone.api.command.exception.CommandException; +import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.BlockOptionalMeta; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.properties.Property; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; public enum ForBlockOptionalMeta implements IDatatypeFor { INSTANCE; + /** + * Matches (domain:)?name([(property=value)*])? but the input can be truncated at any position. + * domain and name are [a-z0-9_.-]+ and [a-z0-9/_.-]+ because that's what mc 1.13+ accepts. + * property and value use the same format as domain. + */ + // Good luck reading this. + private static Pattern PATTERN = Pattern.compile("(?:[a-z0-9_.-]+:)?(?:[a-z0-9/_.-]+(?:\\[(?:(?:[a-z0-9_.-]+=[a-z0-9_.-]+,)*(?:[a-z0-9_.-]+(?:=(?:[a-z0-9_.-]+(?:\\])?)?)?)?|\\])?)?)?"); + @Override public BlockOptionalMeta get(IDatatypeContext ctx) throws CommandException { return new BlockOptionalMeta(ctx.getConsumer().getString()); } @Override - public Stream tabComplete(IDatatypeContext ctx) { - return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE); + public Stream tabComplete(IDatatypeContext ctx) throws CommandException { + String arg = ctx.getConsumer().peekString(); + + if (!PATTERN.matcher(arg).matches()) { + // Invalid format; we can't complete this. + ctx.getConsumer().getString(); + return Stream.empty(); + } + + if (arg.endsWith("]")) { + // We are already done. + ctx.getConsumer().getString(); + return Stream.empty(); + } + + if (!arg.contains("[")) { + // no properties so we are completing the block id + return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE); + } + + ctx.getConsumer().getString(); + + // destructuring assignment? Please? + String blockId, properties; + { + String[] parts = splitLast(arg, '['); + blockId = parts[0]; + properties = parts[1]; + } + + Block block = BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(blockId)).orElse(null); + if (block == null) { + // This block doesn't exist so there's no properties to complete. + return Stream.empty(); + } + + String leadingProperties, lastProperty; + { + String[] parts = splitLast(properties, ','); + leadingProperties = parts[0]; + lastProperty = parts[1]; + } + + if (!lastProperty.contains("=")) { + // The last property-value pair doesn't have a value yet so we are completing its name + Set usedProps = Stream.of(leadingProperties.split(",")) + .map(pair -> pair.split("=")[0]) + .collect(Collectors.toSet()); + + String prefix = arg.substring(0, arg.length() - lastProperty.length()); + return new TabCompleteHelper() + .append( + block.getStateDefinition() + .getProperties() + .stream() + .map(Property::getName) + ) + .filter(prop -> !usedProps.contains(prop)) + .filterPrefix(lastProperty) + .sortAlphabetically() + .map(prop -> prefix + prop) + .stream(); + } + + String lastName, lastValue; + { + String[] parts = splitLast(lastProperty, '='); + lastName = parts[0]; + lastValue = parts[1]; + } + + // We are completing the value of a property + String prefix = arg.substring(0, arg.length() - lastValue.length()); + + Property property = block.getStateDefinition().getProperty(lastName); + if (property == null) { + // The property does not exist so there's no values to complete + return Stream.empty(); + } + + return new TabCompleteHelper() + .append(getValues(property)) + .filterPrefix(lastValue) + .sortAlphabetically() + .map(val -> prefix + val) + .stream(); + } + + /** + * Always returns exactly two strings. + * If the separator is not found the FIRST returned string is empty. + */ + private static String[] splitLast(String string, char chr) { + int idx = string.lastIndexOf(chr); + if (idx == -1) { + return new String[]{"", string}; + } + return new String[]{string.substring(0, idx), string.substring(idx + 1)}; + } + + // this shouldn't need to be a separate method? + private static > Stream getValues(Property property) { + return property.getPossibleValues().stream().map(property::getName); } } diff --git a/src/api/java/baritone/api/command/datatypes/ForDirection.java b/src/api/java/baritone/api/command/datatypes/ForDirection.java index 5dd355b41..cbfbc2243 100644 --- a/src/api/java/baritone/api/command/datatypes/ForDirection.java +++ b/src/api/java/baritone/api/command/datatypes/ForDirection.java @@ -19,10 +19,9 @@ import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; -import net.minecraft.core.Direction; - import java.util.Locale; import java.util.stream.Stream; +import net.minecraft.core.Direction; public enum ForDirection implements IDatatypeFor { INSTANCE; diff --git a/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java b/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java index d1016e99e..b0d72bed1 100644 --- a/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java +++ b/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java @@ -20,11 +20,10 @@ import baritone.api.IBaritone; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Player; - import java.util.List; import java.util.stream.Stream; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; /** * An {@link IDatatype} used to resolve nearby players, those within diff --git a/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java index 7d77a96c7..3d0f2613f 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java @@ -26,7 +26,8 @@ public enum RelativeCoordinate implements IDatatypePost { INSTANCE; - private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)([k-k]?)|)$"); + private static String ScalesAliasRegex = "[kKmM]"; + private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(" + ScalesAliasRegex + "?)|)$"); @Override public Double apply(IDatatypeContext ctx, Double origin) throws CommandException { @@ -41,11 +42,15 @@ public Double apply(IDatatypeContext ctx, Double origin) throws CommandException boolean isRelative = !matcher.group(1).isEmpty(); - double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll("k", "")); + double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll(ScalesAliasRegex, "")); - if (matcher.group(2).contains("k")) { + if (matcher.group(2).toLowerCase().contains("k")) { offset *= 1000; } + if (matcher.group(2).toLowerCase().contains("m")) { + offset *= 1000000; + } + if (isRelative) { return origin + offset; diff --git a/src/api/java/baritone/api/command/datatypes/RelativeFile.java b/src/api/java/baritone/api/command/datatypes/RelativeFile.java index 7d7e6899b..e85f12971 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeFile.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeFile.java @@ -19,6 +19,8 @@ import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; +import baritone.api.utils.Helper; +import net.minecraft.client.Minecraft; import java.io.File; import java.io.IOException; @@ -30,8 +32,6 @@ import java.util.Objects; import java.util.stream.Stream; -import static baritone.api.utils.Helper.HELPER; - public enum RelativeFile implements IDatatypePost { INSTANCE; @@ -93,8 +93,13 @@ public static Stream tabComplete(IArgConsumer consumer, File base0) thro .filter(s -> !s.contains(" ")); } + @Deprecated public static File gameDir() { - File gameDir = HELPER.mc.gameDirectory.getAbsoluteFile(); + return gameDir(Helper.mc); + } + + public static File gameDir(Minecraft mc) { + File gameDir = mc.gameDirectory.getAbsoluteFile(); if (gameDir.getName().equals(".")) { return gameDir.getParentFile(); } diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java index 738ca019a..d97635eb2 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java @@ -21,9 +21,8 @@ import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; -import net.minecraft.util.Mth; - import java.util.stream.Stream; +import net.minecraft.util.Mth; public enum RelativeGoalBlock implements IDatatypePost { INSTANCE; diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java index c4dd045f3..8682bbbaf 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java @@ -21,9 +21,8 @@ import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalXZ; import baritone.api.utils.BetterBlockPos; -import net.minecraft.util.Mth; - import java.util.stream.Stream; +import net.minecraft.util.Mth; public enum RelativeGoalXZ implements IDatatypePost { INSTANCE; diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java index 9a13e4bbd..34c8c0018 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java @@ -21,9 +21,8 @@ import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.GoalYLevel; import baritone.api.utils.BetterBlockPos; -import net.minecraft.util.Mth; - import java.util.stream.Stream; +import net.minecraft.util.Mth; public enum RelativeGoalYLevel implements IDatatypePost { INSTANCE; diff --git a/src/api/java/baritone/api/command/exception/CommandUnhandledException.java b/src/api/java/baritone/api/command/exception/CommandUnhandledException.java index 51840a117..a1f826262 100644 --- a/src/api/java/baritone/api/command/exception/CommandUnhandledException.java +++ b/src/api/java/baritone/api/command/exception/CommandUnhandledException.java @@ -19,7 +19,6 @@ import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; -import net.minecraft.ChatFormatting; import java.util.List; @@ -37,10 +36,6 @@ public CommandUnhandledException(Throwable cause) { @Override public void handle(ICommand command, List args) { - HELPER.logDirect("An unhandled exception occurred. " + - "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues", - ChatFormatting.RED); - - this.printStackTrace(); + HELPER.logUnhandledException(this); } } diff --git a/src/api/java/baritone/api/command/exception/ICommandException.java b/src/api/java/baritone/api/command/exception/ICommandException.java index 389129761..0a1529d69 100644 --- a/src/api/java/baritone/api/command/exception/ICommandException.java +++ b/src/api/java/baritone/api/command/exception/ICommandException.java @@ -19,9 +19,8 @@ import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; -import net.minecraft.ChatFormatting; - import java.util.List; +import net.minecraft.ChatFormatting; import static baritone.api.utils.Helper.HELPER; diff --git a/src/api/java/baritone/api/command/helpers/Paginator.java b/src/api/java/baritone/api/command/helpers/Paginator.java index e119a9a23..77628a796 100644 --- a/src/api/java/baritone/api/command/helpers/Paginator.java +++ b/src/api/java/baritone/api/command/helpers/Paginator.java @@ -21,16 +21,17 @@ import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.utils.Helper; + +import java.awt.*; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - public class Paginator implements Helper { public final List entries; diff --git a/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java b/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java index 0722ce16d..4f822352a 100644 --- a/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java +++ b/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java @@ -23,14 +23,13 @@ import baritone.api.command.manager.ICommandManager; import baritone.api.event.events.TabCompleteEvent; import baritone.api.utils.SettingsUtil; -import net.minecraft.resources.ResourceLocation; - import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; +import net.minecraft.resources.ResourceLocation; /** * The {@link TabCompleteHelper} is a single-use object that helps you handle tab completion. It includes helper @@ -253,7 +252,7 @@ public TabCompleteHelper addCommands(ICommandManager manager) { public TabCompleteHelper addSettings() { return append( BaritoneAPI.getSettings().allSettings.stream() - .filter(s -> !SettingsUtil.javaOnlySetting(s)) + .filter(s -> !s.isJavaOnly()) .map(Settings.Setting::getName) .sorted(String.CASE_INSENSITIVE_ORDER) ); diff --git a/src/api/java/baritone/api/command/registry/Registry.java b/src/api/java/baritone/api/command/registry/Registry.java index 067791690..b571484b7 100644 --- a/src/api/java/baritone/api/command/registry/Registry.java +++ b/src/api/java/baritone/api/command/registry/Registry.java @@ -84,7 +84,7 @@ public boolean register(V entry) { * @param entry The entry to unregister. */ public void unregister(V entry) { - if (registered(entry)) { + if (!registered(entry)) { return; } _entries.remove(entry); diff --git a/src/api/java/baritone/api/event/events/BlockChangeEvent.java b/src/api/java/baritone/api/event/events/BlockChangeEvent.java new file mode 100644 index 000000000..4fc496eb5 --- /dev/null +++ b/src/api/java/baritone/api/event/events/BlockChangeEvent.java @@ -0,0 +1,47 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.event.events; + +import baritone.api.utils.Pair; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.List; + +/** + * @author Brady + */ +public final class BlockChangeEvent { + + private final ChunkPos chunk; + private final List> blocks; + + public BlockChangeEvent(ChunkPos pos, List> blocks) { + this.chunk = pos; + this.blocks = blocks; + } + + public ChunkPos getChunkPos() { + return this.chunk; + } + + public List> getBlocks() { + return this.blocks; + } +} diff --git a/src/api/java/baritone/api/event/events/ChunkEvent.java b/src/api/java/baritone/api/event/events/ChunkEvent.java index f27475bce..bb22a47b1 100644 --- a/src/api/java/baritone/api/event/events/ChunkEvent.java +++ b/src/api/java/baritone/api/event/events/ChunkEvent.java @@ -57,31 +57,38 @@ public ChunkEvent(EventState state, Type type, int x, int z) { /** * @return The state of the event */ - public final EventState getState() { + public EventState getState() { return this.state; } /** * @return The type of chunk event that occurred; */ - public final Type getType() { + public Type getType() { return this.type; } /** * @return The Chunk X position. */ - public final int getX() { + public int getX() { return this.x; } /** * @return The Chunk Z position. */ - public final int getZ() { + public int getZ() { return this.z; } + /** + * @return {@code true} if the event was fired after a chunk population + */ + public boolean isPostPopulate() { + return this.state == EventState.POST && this.type.isPopulate(); + } + public enum Type { /** @@ -106,6 +113,10 @@ public enum Type { *

* And it's a partial chunk */ - POPULATE_PARTIAL + POPULATE_PARTIAL; + + public final boolean isPopulate() { + return this == POPULATE_FULL || this == POPULATE_PARTIAL; + } } } diff --git a/src/api/java/baritone/api/event/events/RotationMoveEvent.java b/src/api/java/baritone/api/event/events/RotationMoveEvent.java index bae83c0fa..c5a9ea9f9 100644 --- a/src/api/java/baritone/api/event/events/RotationMoveEvent.java +++ b/src/api/java/baritone/api/event/events/RotationMoveEvent.java @@ -17,6 +17,7 @@ package baritone.api.event.events; +import baritone.api.utils.Rotation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.phys.Vec3; @@ -32,14 +33,27 @@ public final class RotationMoveEvent { */ private final Type type; + private final Rotation original; + /** * The yaw rotation */ private float yaw; - public RotationMoveEvent(Type type, float yaw) { + /** + * The pitch rotation + */ + private float pitch; + + public RotationMoveEvent(Type type, float yaw, float pitch) { this.type = type; + this.original = new Rotation(yaw, pitch); this.yaw = yaw; + this.pitch = pitch; + } + + public Rotation getOriginal() { + return this.original; } /** @@ -47,21 +61,37 @@ public RotationMoveEvent(Type type, float yaw) { * * @param yaw Yaw rotation */ - public final void setYaw(float yaw) { + public void setYaw(float yaw) { this.yaw = yaw; } /** * @return The yaw rotation */ - public final float getYaw() { + public float getYaw() { return this.yaw; } + /** + * Set the pitch movement rotation + * + * @param pitch Pitch rotation + */ + public void setPitch(float pitch) { + this.pitch = pitch; + } + + /** + * @return The pitch rotation + */ + public float getPitch() { + return pitch; + } + /** * @return The type of the event */ - public final Type getType() { + public Type getType() { return this.type; } diff --git a/src/api/java/baritone/api/event/events/TickEvent.java b/src/api/java/baritone/api/event/events/TickEvent.java index 5c484ae49..405ad3c49 100644 --- a/src/api/java/baritone/api/event/events/TickEvent.java +++ b/src/api/java/baritone/api/event/events/TickEvent.java @@ -18,9 +18,18 @@ package baritone.api.event.events; import baritone.api.event.events.type.EventState; +import net.minecraft.client.Minecraft; import java.util.function.BiFunction; +/** + * Called on and after each game tick of the primary {@link Minecraft} instance and dispatched to all Baritone + * instances. + *

+ * When {@link #state} is {@link EventState#PRE}, the event is being called just prior to when the current in-game + * screen is ticked. When {@link #state} is {@link EventState#POST}, the event is being called at the very end + * of the {@link Minecraft#runTick()} method. + */ public final class TickEvent { private static int overallTickCount; diff --git a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java index 5a94e5100..54aabe387 100644 --- a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java @@ -31,58 +31,50 @@ public interface AbstractGameEventListener extends IGameEventListener { @Override - default void onTick(TickEvent event) { - } + default void onTick(TickEvent event) {} @Override - default void onPlayerUpdate(PlayerUpdateEvent event) { - } + default void onPostTick(TickEvent event) {} @Override - default void onSendChatMessage(ChatEvent event) { - } + default void onPlayerUpdate(PlayerUpdateEvent event) {} @Override - default void onPreTabComplete(TabCompleteEvent event) { - } + default void onSendChatMessage(ChatEvent event) {} @Override - default void onChunkEvent(ChunkEvent event) { - } + default void onPreTabComplete(TabCompleteEvent event) {} @Override - default void onRenderPass(RenderEvent event) { - } + default void onChunkEvent(ChunkEvent event) {} @Override - default void onWorldEvent(WorldEvent event) { - } + default void onBlockChange(BlockChangeEvent event) {} @Override - default void onSendPacket(PacketEvent event) { - } + default void onRenderPass(RenderEvent event) {} @Override - default void onReceivePacket(PacketEvent event) { - } + default void onWorldEvent(WorldEvent event) {} @Override - default void onPlayerRotationMove(RotationMoveEvent event) { - } + default void onSendPacket(PacketEvent event) {} @Override - default void onPlayerSprintState(SprintStateEvent event) { - } + default void onReceivePacket(PacketEvent event) {} @Override - default void onBlockInteract(BlockInteractEvent event) { - } + default void onPlayerRotationMove(RotationMoveEvent event) {} @Override - default void onPlayerDeath() { - } + default void onPlayerSprintState(SprintStateEvent event) {} @Override - default void onPathEvent(PathEvent event) { - } + default void onBlockInteract(BlockInteractEvent event) {} + + @Override + default void onPlayerDeath() {} + + @Override + default void onPathEvent(PathEvent event) {} } diff --git a/src/api/java/baritone/api/event/listener/IGameEventListener.java b/src/api/java/baritone/api/event/listener/IGameEventListener.java index a34ea4ef3..6939f8d66 100644 --- a/src/api/java/baritone/api/event/listener/IGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java @@ -40,6 +40,14 @@ public interface IGameEventListener { */ void onTick(TickEvent event); + /** + * Run once per game tick after the tick is completed + * + * @param event The event + * @see Minecraft#runTick() + */ + void onPostTick(TickEvent event); + /** * Run once per game tick from before and after the player rotation is sent to the server. * @@ -70,6 +78,13 @@ public interface IGameEventListener { */ void onChunkEvent(ChunkEvent event); + /** + * Runs after a single or multi block change packet is received and processed. + * + * @param event The event + */ + void onBlockChange(BlockChangeEvent event); + /** * Runs once per world render pass. * diff --git a/src/api/java/baritone/api/pathing/goals/GoalAxis.java b/src/api/java/baritone/api/pathing/goals/GoalAxis.java index 7c9b26705..6e2f84e7a 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalAxis.java +++ b/src/api/java/baritone/api/pathing/goals/GoalAxis.java @@ -42,6 +42,16 @@ public double heuristic(int x0, int y, int z0) { return flatAxisDistance * BaritoneAPI.getSettings().costHeuristic.value + GoalYLevel.calculate(BaritoneAPI.getSettings().axisHeight.value, y); } + @Override + public boolean equals(Object o) { + return o.getClass() == GoalAxis.class; + } + + @Override + public int hashCode() { + return 201385781; + } + @Override public String toString() { return "GoalAxis"; diff --git a/src/api/java/baritone/api/pathing/goals/GoalBlock.java b/src/api/java/baritone/api/pathing/goals/GoalBlock.java index 7cb9da14a..4bebe07a3 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalBlock.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; @@ -66,6 +67,26 @@ public double heuristic(int x, int y, int z) { return calculate(xDiff, yDiff, zDiff); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalBlock goal = (GoalBlock) o; + return x == goal.x + && y == goal.y + && z == goal.z; + } + + @Override + public int hashCode() { + return (int) BetterBlockPos.longHash(x, y, z) * 905165533; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalComposite.java b/src/api/java/baritone/api/pathing/goals/GoalComposite.java index 47522492b..8e13a86e4 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalComposite.java +++ b/src/api/java/baritone/api/pathing/goals/GoalComposite.java @@ -67,6 +67,24 @@ public double heuristic() { return min; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalComposite goal = (GoalComposite) o; + return Arrays.equals(goals, goal.goals); + } + + @Override + public int hashCode() { + return Arrays.hashCode(goals); + } + @Override public String toString() { return "GoalComposite" + Arrays.toString(goals); diff --git a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java index 8d15e4bc9..1c04f7c6d 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; @@ -60,6 +61,26 @@ public double heuristic(int x, int y, int z) { return GoalBlock.calculate(xDiff, yDiff < 0 ? yDiff + 1 : yDiff, zDiff); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalGetToBlock goal = (GoalGetToBlock) o; + return x == goal.x + && y == goal.y + && z == goal.z; + } + + @Override + public int hashCode() { + return (int) BetterBlockPos.longHash(x, y, z) * -49639096; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalInverted.java b/src/api/java/baritone/api/pathing/goals/GoalInverted.java index 354e2ce39..4a3f75315 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalInverted.java +++ b/src/api/java/baritone/api/pathing/goals/GoalInverted.java @@ -17,6 +17,8 @@ package baritone.api.pathing.goals; +import java.util.Objects; + /** * Invert any goal. *

@@ -50,6 +52,24 @@ public double heuristic() { return Double.NEGATIVE_INFINITY; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalInverted goal = (GoalInverted) o; + return Objects.equals(origin, goal.origin); + } + + @Override + public int hashCode() { + return origin.hashCode() * 495796690; + } + @Override public String toString() { return String.format("GoalInverted{%s}", origin.toString()); diff --git a/src/api/java/baritone/api/pathing/goals/GoalNear.java b/src/api/java/baritone/api/pathing/goals/GoalNear.java index 6ee35ad7b..351c08021 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalNear.java +++ b/src/api/java/baritone/api/pathing/goals/GoalNear.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import it.unimi.dsi.fastutil.doubles.DoubleIterator; @@ -86,6 +87,27 @@ public BlockPos getGoalPos() { return new BlockPos(x, y, z); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalNear goal = (GoalNear) o; + return x == goal.x + && y == goal.y + && z == goal.z + && rangeSq == goal.rangeSq; + } + + @Override + public int hashCode() { + return (int) BetterBlockPos.longHash(x, y, z) + rangeSq; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java index 3906713f2..49b6f708d 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalRunAway.java +++ b/src/api/java/baritone/api/pathing/goals/GoalRunAway.java @@ -23,6 +23,7 @@ import net.minecraft.core.BlockPos; import java.util.Arrays; +import java.util.Objects; /** * Useful for automated combat (retreating specifically) @@ -124,6 +125,29 @@ public double heuristic() {// TODO less hacky solution return maxInside; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalRunAway goal = (GoalRunAway) o; + return distanceSq == goal.distanceSq + && Arrays.equals(from, goal.from) + && Objects.equals(maintainY, goal.maintainY); + } + + @Override + public int hashCode() { + int hash = Arrays.hashCode(from); + hash = hash * 1196803141 + distanceSq; + hash = hash * -2053788840 + maintainY; + return hash; + } + @Override public String toString() { if (maintainY != null) { diff --git a/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java index c4a8a6272..6facfbd79 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java +++ b/src/api/java/baritone/api/pathing/goals/GoalStrictDirection.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -69,6 +70,31 @@ public double heuristic() { return Double.NEGATIVE_INFINITY; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalStrictDirection goal = (GoalStrictDirection) o; + return x == goal.x + && y == goal.y + && z == goal.z + && dx == goal.dx + && dz == goal.dz; + } + + @Override + public int hashCode() { + int hash = (int) BetterBlockPos.longHash(x, y, z); + hash = hash * 630627507 + dx; + hash = hash * -283028380 + dz; + return hash; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java index 1b7213471..c9325e3ad 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java +++ b/src/api/java/baritone/api/pathing/goals/GoalTwoBlocks.java @@ -17,6 +17,7 @@ package baritone.api.pathing.goals; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.SettingsUtil; import baritone.api.utils.interfaces.IGoalRenderPos; import net.minecraft.core.BlockPos; @@ -72,6 +73,26 @@ public BlockPos getGoalPos() { return new BlockPos(x, y, z); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalTwoBlocks goal = (GoalTwoBlocks) o; + return x == goal.x + && y == goal.y + && z == goal.z; + } + + @Override + public int hashCode() { + return (int) BetterBlockPos.longHash(x, y, z) * 516508351; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalXZ.java b/src/api/java/baritone/api/pathing/goals/GoalXZ.java index f70f6c4f7..b72711283 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalXZ.java +++ b/src/api/java/baritone/api/pathing/goals/GoalXZ.java @@ -64,6 +64,27 @@ public double heuristic(int x, int y, int z) {//mostly copied from GoalBlock return calculate(xDiff, zDiff); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalXZ goal = (GoalXZ) o; + return x == goal.x && z == goal.z; + } + + @Override + public int hashCode() { + int hash = 1791873246; + hash = hash * 222601791 + x; + hash = hash * -1331679453 + z; + return hash; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/pathing/goals/GoalYLevel.java b/src/api/java/baritone/api/pathing/goals/GoalYLevel.java index 603ef9bd1..442906ad1 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalYLevel.java +++ b/src/api/java/baritone/api/pathing/goals/GoalYLevel.java @@ -58,6 +58,24 @@ public static double calculate(int goalY, int currentY) { return 0; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GoalYLevel goal = (GoalYLevel) o; + return level == goal.level; + } + + @Override + public int hashCode() { + return level * 1271009915; + } + @Override public String toString() { return String.format( diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java index 34c4394aa..347303dff 100644 --- a/src/api/java/baritone/api/process/IBuilderProcess.java +++ b/src/api/java/baritone/api/process/IBuilderProcess.java @@ -25,7 +25,6 @@ import java.io.File; import java.util.List; -import java.util.Map; /** * @author Brady @@ -42,14 +41,6 @@ public interface IBuilderProcess extends IBaritoneProcess { */ void build(String name, ISchematic schematic, Vec3i origin); - Vec3i getSchemSize(); - - void popStack(); - - boolean clearState(); - - boolean isFromAltoclefFinished(); - /** * Requests a build for the specified schematic, labeled as specified, with the specified origin. * @@ -60,6 +51,7 @@ public interface IBuilderProcess extends IBaritoneProcess { */ boolean build(String name, File schematic, Vec3i origin); + @Deprecated default boolean build(String schematicFile, BlockPos origin) { File file = new File(new File(Minecraft.getInstance().gameDirectory, "schematics"), schematicFile); return build(schematicFile, file, origin); @@ -73,13 +65,13 @@ default boolean build(String schematicFile, BlockPos origin) { boolean isPaused(); - void resume(); + void popStack(); - void clearArea(BlockPos corner1, BlockPos corner2); + boolean isFromAltoclefFinished(); - void reset(); + void resume(); - Map getMissing(); + void clearArea(BlockPos corner1, BlockPos corner2); /** * @return A list of block states that are estimated to be placeable by this builder process. You can use this in @@ -87,12 +79,4 @@ default boolean build(String schematicFile, BlockPos origin) { * cause it to give up. This is updated every tick, but only while the builder process is active. */ List getApproxPlaceable(); - - boolean isFromAltoclef(); - - void build(String name, ISchematic schematic, Vec3i origin, boolean fromAltoclef); - - boolean build(String name, File schematic, Vec3i origin, boolean fromAltoclef); - - void noteInsert(BlockPos pos); } diff --git a/src/api/java/baritone/api/process/ICustomGoalProcess.java b/src/api/java/baritone/api/process/ICustomGoalProcess.java index 5084aff2f..2bae55ce3 100644 --- a/src/api/java/baritone/api/process/ICustomGoalProcess.java +++ b/src/api/java/baritone/api/process/ICustomGoalProcess.java @@ -38,6 +38,11 @@ public interface ICustomGoalProcess extends IBaritoneProcess { */ Goal getGoal(); + /** + * @return The most recent set goal, which doesn't invalidate upon {@link #onLostControl()} + */ + Goal mostRecentGoal(); + /** * Sets the goal and begins the path execution. * diff --git a/src/api/java/baritone/api/process/IElytraProcess.java b/src/api/java/baritone/api/process/IElytraProcess.java new file mode 100644 index 000000000..28328f901 --- /dev/null +++ b/src/api/java/baritone/api/process/IElytraProcess.java @@ -0,0 +1,50 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.process; + +import baritone.api.pathing.goals.Goal; +import net.minecraft.core.BlockPos; + +public interface IElytraProcess extends IBaritoneProcess { + + void repackChunks(); + + /** + * @return Where it is currently flying to, null if not active + */ + BlockPos currentDestination(); + + void pathTo(BlockPos destination); + + void pathTo(Goal destination); + + /** + * Resets the state of the process but will maintain the same destination and will try to keep flying + */ + void resetState(); + + /** + * @return {@code true} if the native library loaded and elytra is actually usable + */ + boolean isLoaded(); + + /* + * FOR INTERNAL USE ONLY. MAY BE REMOVED AT ANY TIME. + */ + boolean isSafeToCancel(); +} diff --git a/src/api/java/baritone/api/process/IFarmProcess.java b/src/api/java/baritone/api/process/IFarmProcess.java index cf63b64d3..0c07567de 100644 --- a/src/api/java/baritone/api/process/IFarmProcess.java +++ b/src/api/java/baritone/api/process/IFarmProcess.java @@ -33,9 +33,7 @@ public interface IFarmProcess extends IBaritoneProcess { /** * Begin to search for nearby crops to farm. */ - default void farm() { - farm(0, null); - } + default void farm() {farm(0, null);} /** * Begin to search for crops to farm with in specified aria @@ -43,7 +41,5 @@ default void farm() { * * @param range The distance to search for crops to farm */ - default void farm(int range) { - farm(range, null); - } + default void farm(int range) {farm(range, null);} } diff --git a/src/api/java/baritone/api/process/IFollowProcess.java b/src/api/java/baritone/api/process/IFollowProcess.java index b9eedee30..6f7f0a239 100644 --- a/src/api/java/baritone/api/process/IFollowProcess.java +++ b/src/api/java/baritone/api/process/IFollowProcess.java @@ -17,10 +17,9 @@ package baritone.api.process; -import net.minecraft.world.entity.Entity; - import java.util.List; import java.util.function.Predicate; +import net.minecraft.world.entity.Entity; /** * @author Brady diff --git a/src/api/java/baritone/api/process/IMineProcess.java b/src/api/java/baritone/api/process/IMineProcess.java index 1331e3dde..a63eb00a2 100644 --- a/src/api/java/baritone/api/process/IMineProcess.java +++ b/src/api/java/baritone/api/process/IMineProcess.java @@ -19,9 +19,8 @@ import baritone.api.utils.BlockOptionalMeta; import baritone.api.utils.BlockOptionalMetaLookup; -import net.minecraft.world.level.block.Block; - import java.util.stream.Stream; +import net.minecraft.world.level.block.Block; /** * @author Brady diff --git a/src/api/java/baritone/api/process/PathingCommandType.java b/src/api/java/baritone/api/process/PathingCommandType.java index af25591af..cde38eaf2 100644 --- a/src/api/java/baritone/api/process/PathingCommandType.java +++ b/src/api/java/baritone/api/process/PathingCommandType.java @@ -56,5 +56,10 @@ public enum PathingCommandType { /** * Go and ask the next process what to do */ - DEFER + DEFER, + + /** + * Sets the goal and calculates a path, but pauses instead of immediately starting the path. + */ + SET_GOAL_AND_PAUSE } diff --git a/src/api/java/baritone/api/schematic/CompositeSchematic.java b/src/api/java/baritone/api/schematic/CompositeSchematic.java index 62faa8bf2..0724ec018 100644 --- a/src/api/java/baritone/api/schematic/CompositeSchematic.java +++ b/src/api/java/baritone/api/schematic/CompositeSchematic.java @@ -17,10 +17,9 @@ package baritone.api.schematic; -import net.minecraft.world.level.block.state.BlockState; - import java.util.ArrayList; import java.util.List; +import net.minecraft.world.level.block.state.BlockState; public class CompositeSchematic extends AbstractSchematic { diff --git a/src/api/java/baritone/api/schematic/FillSchematic.java b/src/api/java/baritone/api/schematic/FillSchematic.java index d79374fc3..126501987 100644 --- a/src/api/java/baritone/api/schematic/FillSchematic.java +++ b/src/api/java/baritone/api/schematic/FillSchematic.java @@ -18,6 +18,7 @@ package baritone.api.schematic; import baritone.api.utils.BlockOptionalMeta; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import java.util.List; diff --git a/src/api/java/baritone/api/schematic/ISchematic.java b/src/api/java/baritone/api/schematic/ISchematic.java index 362d246f7..bc7c35a15 100644 --- a/src/api/java/baritone/api/schematic/ISchematic.java +++ b/src/api/java/baritone/api/schematic/ISchematic.java @@ -17,11 +17,10 @@ package baritone.api.schematic; +import java.util.List; import net.minecraft.core.Direction; import net.minecraft.world.level.block.state.BlockState; -import java.util.List; - /** * Basic representation of a schematic. Provides the dimensions and the desired state for a given position relative to * the origin. @@ -76,8 +75,7 @@ default int size(Direction.Axis axis) { /** * Resets possible caches to avoid wrong behavior when moving the schematic around */ - default void reset() { - } + default void reset() {} /** * @return The width (X axis length) of this schematic diff --git a/src/api/java/baritone/api/schematic/MaskSchematic.java b/src/api/java/baritone/api/schematic/MaskSchematic.java index 954d7973c..a5749ff57 100644 --- a/src/api/java/baritone/api/schematic/MaskSchematic.java +++ b/src/api/java/baritone/api/schematic/MaskSchematic.java @@ -17,6 +17,7 @@ package baritone.api.schematic; +import baritone.api.schematic.mask.Mask; import net.minecraft.world.level.block.state.BlockState; import java.util.List; @@ -41,4 +42,14 @@ public boolean inSchematic(int x, int y, int z, BlockState currentState) { public BlockState desiredState(int x, int y, int z, BlockState current, List approxPlaceable) { return schematic.desiredState(x, y, z, current, approxPlaceable); } + + public static MaskSchematic create(ISchematic schematic, Mask function) { + return new MaskSchematic(schematic) { + + @Override + protected boolean partOfMask(int x, int y, int z, BlockState currentState) { + return function.partOfMask(x, y, z, currentState); + } + }; + } } diff --git a/src/api/java/baritone/api/schematic/SubstituteSchematic.java b/src/api/java/baritone/api/schematic/SubstituteSchematic.java index 1e7d99db0..96baa38da 100644 --- a/src/api/java/baritone/api/schematic/SubstituteSchematic.java +++ b/src/api/java/baritone/api/schematic/SubstituteSchematic.java @@ -84,6 +84,7 @@ private BlockState withBlock(BlockState state, Block block) { blockStateCache.computeIfAbsent(state, s -> new HashMap()).put(block, newState); return newState; } + private > BlockState copySingleProp(BlockState fromState, BlockState toState, Property prop) { return toState.setValue(prop, fromState.getValue(prop)); } diff --git a/src/api/java/baritone/api/schematic/mask/AbstractMask.java b/src/api/java/baritone/api/schematic/mask/AbstractMask.java new file mode 100644 index 000000000..ce92af0ec --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/AbstractMask.java @@ -0,0 +1,49 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask; + +/** + * @author Brady + */ +public abstract class AbstractMask implements Mask { + + private final int widthX; + private final int heightY; + private final int lengthZ; + + public AbstractMask(int widthX, int heightY, int lengthZ) { + this.widthX = widthX; + this.heightY = heightY; + this.lengthZ = lengthZ; + } + + @Override + public int widthX() { + return this.widthX; + } + + @Override + public int heightY() { + return this.heightY; + } + + @Override + public int lengthZ() { + return this.lengthZ; + } +} diff --git a/src/api/java/baritone/api/schematic/mask/Mask.java b/src/api/java/baritone/api/schematic/mask/Mask.java new file mode 100644 index 000000000..c8b1f15a1 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/Mask.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask; + +import baritone.api.schematic.mask.operator.BinaryOperatorMask; +import baritone.api.schematic.mask.operator.NotMask; +import baritone.api.utils.BooleanBinaryOperators; +import net.minecraft.world.level.block.state.BlockState; + +/** + * @author Brady + */ +public interface Mask { + + /** + * @param x The relative x position of the block + * @param y The relative y position of the block + * @param z The relative z position of the block + * @param currentState The current state of that block in the world, may be {@code null} + * @return Whether the given position is included in this mask + */ + boolean partOfMask(int x, int y, int z, BlockState currentState); + + int widthX(); + + int heightY(); + + int lengthZ(); + + default Mask not() { + return new NotMask(this); + } + + default Mask union(Mask other) { + return new BinaryOperatorMask(this, other, BooleanBinaryOperators.OR); + } + + default Mask intersection(Mask other) { + return new BinaryOperatorMask(this, other, BooleanBinaryOperators.AND); + } + + default Mask xor(Mask other) { + return new BinaryOperatorMask(this, other, BooleanBinaryOperators.XOR); + } +} diff --git a/src/api/java/baritone/api/schematic/mask/PreComputedMask.java b/src/api/java/baritone/api/schematic/mask/PreComputedMask.java new file mode 100644 index 000000000..aed26cc94 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/PreComputedMask.java @@ -0,0 +1,44 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask; + +/** + * @author Brady + */ +final class PreComputedMask extends AbstractMask implements StaticMask { + + private final boolean[][][] mask; + + public PreComputedMask(StaticMask mask) { + super(mask.widthX(), mask.heightY(), mask.lengthZ()); + + this.mask = new boolean[this.heightY()][this.lengthZ()][this.widthX()]; + for (int y = 0; y < this.heightY(); y++) { + for (int z = 0; z < this.lengthZ(); z++) { + for (int x = 0; x < this.widthX(); x++) { + this.mask[y][z][x] = mask.partOfMask(x, y, z); + } + } + } + } + + @Override + public boolean partOfMask(int x, int y, int z) { + return this.mask[y][z][x]; + } +} diff --git a/src/api/java/baritone/api/schematic/mask/StaticMask.java b/src/api/java/baritone/api/schematic/mask/StaticMask.java new file mode 100644 index 000000000..9925ffca2 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/StaticMask.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.api.schematic.mask; + +import baritone.api.schematic.mask.operator.BinaryOperatorMask; +import baritone.api.schematic.mask.operator.NotMask; +import baritone.api.utils.BooleanBinaryOperators; +import net.minecraft.world.level.block.state.BlockState; + +/** + * A mask that is context-free. In other words, it doesn't require the current block state to determine if a relative + * position is a part of the mask. + * + * @author Brady + */ +public interface StaticMask extends Mask { + + /** + * Determines if a given relative coordinate is included in this mask, without the need for the current block state. + * + * @param x The relative x position of the block + * @param y The relative y position of the block + * @param z The relative z position of the block + * @return Whether the given position is included in this mask + */ + boolean partOfMask(int x, int y, int z); + + /** + * Implements the parent {@link Mask#partOfMask partOfMask function} by calling the static function + * provided in this functional interface without needing the {@link BlockState} argument. This {@code default} + * implementation should NOT be overriden. + * + * @param x The relative x position of the block + * @param y The relative y position of the block + * @param z The relative z position of the block + * @param currentState The current state of that block in the world, may be {@code null} + * @return Whether the given position is included in this mask + */ + @Override + default boolean partOfMask(int x, int y, int z, BlockState currentState) { + return this.partOfMask(x, y, z); + } + + @Override + default StaticMask not() { + return new NotMask.Static(this); + } + + default StaticMask union(StaticMask other) { + return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.OR); + } + + default StaticMask intersection(StaticMask other) { + return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.AND); + } + + default StaticMask xor(StaticMask other) { + return new BinaryOperatorMask.Static(this, other, BooleanBinaryOperators.XOR); + } + + /** + * Returns a pre-computed mask using {@code this} function, with the specified size parameters. + */ + default StaticMask compute() { + return new PreComputedMask(this); + } +} diff --git a/src/api/java/baritone/api/schematic/mask/operator/BinaryOperatorMask.java b/src/api/java/baritone/api/schematic/mask/operator/BinaryOperatorMask.java new file mode 100644 index 000000000..08975e521 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/operator/BinaryOperatorMask.java @@ -0,0 +1,79 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask.operator; + +import baritone.api.schematic.mask.AbstractMask; +import baritone.api.schematic.mask.Mask; +import baritone.api.schematic.mask.StaticMask; +import baritone.api.utils.BooleanBinaryOperator; +import net.minecraft.world.level.block.state.BlockState; + +/** + * @author Brady + */ +public final class BinaryOperatorMask extends AbstractMask { + + private final Mask a; + private final Mask b; + private final BooleanBinaryOperator operator; + + public BinaryOperatorMask(Mask a, Mask b, BooleanBinaryOperator operator) { + super(Math.max(a.widthX(), b.widthX()), Math.max(a.heightY(), b.heightY()), Math.max(a.lengthZ(), b.lengthZ())); + this.a = a; + this.b = b; + this.operator = operator; + } + + @Override + public boolean partOfMask(int x, int y, int z, BlockState currentState) { + return this.operator.applyAsBoolean( + partOfMask(a, x, y, z, currentState), + partOfMask(b, x, y, z, currentState) + ); + } + + private static boolean partOfMask(Mask mask, int x, int y, int z, BlockState currentState) { + return x < mask.widthX() && y < mask.heightY() && z < mask.lengthZ() && mask.partOfMask(x, y, z, currentState); + } + + public static final class Static extends AbstractMask implements StaticMask { + + private final StaticMask a; + private final StaticMask b; + private final BooleanBinaryOperator operator; + + public Static(StaticMask a, StaticMask b, BooleanBinaryOperator operator) { + super(Math.max(a.widthX(), b.widthX()), Math.max(a.heightY(), b.heightY()), Math.max(a.lengthZ(), b.lengthZ())); + this.a = a; + this.b = b; + this.operator = operator; + } + + @Override + public boolean partOfMask(int x, int y, int z) { + return this.operator.applyAsBoolean( + partOfMask(a, x, y, z), + partOfMask(b, x, y, z) + ); + } + + private static boolean partOfMask(StaticMask mask, int x, int y, int z) { + return x < mask.widthX() && y < mask.heightY() && z < mask.lengthZ() && mask.partOfMask(x, y, z); + } + } +} diff --git a/src/api/java/baritone/api/schematic/mask/operator/NotMask.java b/src/api/java/baritone/api/schematic/mask/operator/NotMask.java new file mode 100644 index 000000000..9d0dfcd6d --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/operator/NotMask.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.schematic.mask.operator; + +import baritone.api.schematic.mask.AbstractMask; +import baritone.api.schematic.mask.Mask; +import baritone.api.schematic.mask.StaticMask; +import net.minecraft.world.level.block.state.BlockState; + +/** + * @author Brady + */ +public final class NotMask extends AbstractMask { + + private final Mask source; + + public NotMask(Mask source) { + super(source.widthX(), source.heightY(), source.lengthZ()); + this.source = source; + } + + @Override + public boolean partOfMask(int x, int y, int z, BlockState currentState) { + return !this.source.partOfMask(x, y, z, currentState); + } + + public static final class Static extends AbstractMask implements StaticMask { + + private final StaticMask source; + + public Static(StaticMask source) { + super(source.widthX(), source.heightY(), source.lengthZ()); + this.source = source; + } + + @Override + public boolean partOfMask(int x, int y, int z) { + return !this.source.partOfMask(x, y, z); + } + } +} diff --git a/src/api/java/baritone/api/schematic/mask/shape/CylinderMask.java b/src/api/java/baritone/api/schematic/mask/shape/CylinderMask.java new file mode 100644 index 000000000..093a27e38 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/shape/CylinderMask.java @@ -0,0 +1,69 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask.shape; + +import baritone.api.schematic.mask.AbstractMask; +import baritone.api.schematic.mask.StaticMask; +import net.minecraft.core.Direction; + +/** + * @author Brady + */ +public final class CylinderMask extends AbstractMask implements StaticMask { + + private final double centerA; + private final double centerB; + private final double radiusSqA; + private final double radiusSqB; + private final boolean filled; + private final Direction.Axis alignment; + + public CylinderMask(int widthX, int heightY, int lengthZ, boolean filled, Direction.Axis alignment) { + super(widthX, heightY, lengthZ); + this.centerA = this.getA(widthX, heightY, alignment) / 2.0; + this.centerB = this.getB(heightY, lengthZ, alignment) / 2.0; + this.radiusSqA = (this.centerA - 1) * (this.centerA - 1); + this.radiusSqB = (this.centerB - 1) * (this.centerB - 1); + this.filled = filled; + this.alignment = alignment; + } + + @Override + public boolean partOfMask(int x, int y, int z) { + double da = Math.abs((this.getA(x, y, this.alignment) + 0.5) - this.centerA); + double db = Math.abs((this.getB(y, z, this.alignment) + 0.5) - this.centerB); + if (this.outside(da, db)) { + return false; + } + return this.filled + || this.outside(da + 1, db) + || this.outside(da, db + 1); + } + + private boolean outside(double da, double db) { + return da * da / this.radiusSqA + db * db / this.radiusSqB > 1; + } + + private static int getA(int x, int y, Direction.Axis alignment) { + return alignment == Direction.Axis.X ? y : x; + } + + private static int getB(int y, int z, Direction.Axis alignment) { + return alignment == Direction.Axis.Z ? y : z; + } +} diff --git a/src/api/java/baritone/api/schematic/mask/shape/SphereMask.java b/src/api/java/baritone/api/schematic/mask/shape/SphereMask.java new file mode 100644 index 000000000..d805c98a8 --- /dev/null +++ b/src/api/java/baritone/api/schematic/mask/shape/SphereMask.java @@ -0,0 +1,64 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic.mask.shape; + +import baritone.api.schematic.mask.AbstractMask; +import baritone.api.schematic.mask.StaticMask; + +/** + * @author Brady + */ +public final class SphereMask extends AbstractMask implements StaticMask { + + private final double centerX; + private final double centerY; + private final double centerZ; + private final double radiusSqX; + private final double radiusSqY; + private final double radiusSqZ; + private final boolean filled; + + public SphereMask(int widthX, int heightY, int lengthZ, boolean filled) { + super(widthX, heightY, lengthZ); + this.centerX = widthX / 2.0; + this.centerY = heightY / 2.0; + this.centerZ = lengthZ / 2.0; + this.radiusSqX = this.centerX * this.centerX; + this.radiusSqY = this.centerY * this.centerY; + this.radiusSqZ = this.centerZ * this.centerZ; + this.filled = filled; + } + + @Override + public boolean partOfMask(int x, int y, int z) { + double dx = Math.abs((x + 0.5) - this.centerX); + double dy = Math.abs((y + 0.5) - this.centerY); + double dz = Math.abs((z + 0.5) - this.centerZ); + if (this.outside(dx, dy, dz)) { + return false; + } + return this.filled + || this.outside(dx + 1, dy, dz) + || this.outside(dx, dy + 1, dz) + || this.outside(dx, dy, dz + 1); + } + + private boolean outside(double dx, double dy, double dz) { + return dx * dx / this.radiusSqX + dy * dy / this.radiusSqY + dz * dz / this.radiusSqZ > 1; + } +} diff --git a/src/api/java/baritone/api/utils/BetterBlockPos.java b/src/api/java/baritone/api/utils/BetterBlockPos.java index 676398887..5add76555 100644 --- a/src/api/java/baritone/api/utils/BetterBlockPos.java +++ b/src/api/java/baritone/api/utils/BetterBlockPos.java @@ -17,13 +17,12 @@ package baritone.api.utils; +import javax.annotation.Nonnull; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.util.Mth; -import javax.annotation.Nonnull; - /** * A better BlockPos that has fewer hash collisions (and slightly more performant offsets) *

@@ -35,6 +34,15 @@ */ public final class BetterBlockPos extends BlockPos { + private static final int NUM_X_BITS = 26; + private static final int NUM_Z_BITS = NUM_X_BITS; + private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS; + private static final int Y_SHIFT = NUM_Z_BITS; + private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS; + private static final long X_MASK = (1L << NUM_X_BITS) - 1L; + private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L; + private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L; + public static final BetterBlockPos ORIGIN = new BetterBlockPos(0, 0, 0); public final int x; @@ -202,6 +210,20 @@ public BetterBlockPos west(int amt) { return amt == 0 ? this : new BetterBlockPos(x - amt, y, z); } + public double distanceSq(final BetterBlockPos to) { + double dx = (double) this.x - to.x; + double dy = (double) this.y - to.y; + double dz = (double) this.z - to.z; + return dx * dx + dy * dy + dz * dz; + } + + public double distanceTo(final BetterBlockPos to) { + double dx = (double) this.x - to.x; + double dy = (double) this.y - to.y; + double dz = (double) this.z - to.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + @Override @Nonnull public String toString() { @@ -212,4 +234,15 @@ public String toString() { SettingsUtil.maybeCensor(z) ); } + + public static long serializeToLong(final int x, final int y, final int z) { + return ((long) x & X_MASK) << X_SHIFT | ((long) y & Y_MASK) << Y_SHIFT | ((long) z & Z_MASK); + } + + public static BetterBlockPos deserializeFromLong(final long serialized) { + final int x = (int) (serialized << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS); + final int y = (int) (serialized << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS); + final int z = (int) (serialized << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS); + return new BetterBlockPos(x, y, z); + } } diff --git a/src/api/java/baritone/api/utils/BlockOptionalMeta.java b/src/api/java/baritone/api/utils/BlockOptionalMeta.java index fe549375a..ac14d0187 100644 --- a/src/api/java/baritone/api/utils/BlockOptionalMeta.java +++ b/src/api/java/baritone/api/utils/BlockOptionalMeta.java @@ -18,6 +18,7 @@ package baritone.api.utils; import baritone.api.utils.accessor.IItemStack; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import io.netty.util.concurrent.ThreadPerTaskExecutor; import net.minecraft.client.Minecraft; @@ -32,8 +33,8 @@ import net.minecraft.server.packs.repository.ServerPacksSource; import net.minecraft.server.packs.resources.MultiPackResourceManager; import net.minecraft.server.packs.resources.ReloadableResourceManager; -import net.minecraft.util.RandomSource; import net.minecraft.util.Unit; +import net.minecraft.world.RandomSequences; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -41,63 +42,99 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.loot.BuiltInLootTables; import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.LootTables; -import net.minecraft.world.level.storage.loot.PredicateManager; +import net.minecraft.world.level.storage.loot.LootDataManager; +import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.phys.Vec3; import sun.misc.Unsafe; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public final class BlockOptionalMeta { + // id or id[] or id[properties] where id and properties are any text with at least one character + private static final Pattern PATTERN = Pattern.compile("^(?.+?)(?:\\[(?.+?)?\\])?$"); private final Block block; + private final String propertiesDescription; // exists so toString() can return something more useful than a list of all blockstates private final Set blockstates; private final ImmutableSet stateHashes; private final ImmutableSet stackHashes; - private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$"); - private static LootTables lootTables; - private static PredicateManager predicate = new PredicateManager(); + private static LootDataManager lootTables; private static Map> drops = new HashMap<>(); public BlockOptionalMeta(@Nonnull Block block) { this.block = block; - this.blockstates = getStates(block); + this.propertiesDescription = "{}"; + this.blockstates = getStates(block, Collections.emptyMap()); this.stateHashes = getStateHashes(blockstates); this.stackHashes = getStackHashes(blockstates); } public BlockOptionalMeta(@Nonnull String selector) { - Matcher matcher = pattern.matcher(selector); + Matcher matcher = PATTERN.matcher(selector); if (!matcher.find()) { throw new IllegalArgumentException("invalid block selector"); } - MatchResult matchResult = matcher.toMatchResult(); + block = BlockUtils.stringToBlockRequired(matcher.group("id")); - block = BlockUtils.stringToBlockRequired(matchResult.group(1)); - blockstates = getStates(block); + String props = matcher.group("properties"); + Map, ?> properties = props == null || props.equals("") ? Collections.emptyMap() : parseProperties(block, props); + + propertiesDescription = props == null ? "{}" : "{" + props.replace("=", ":") + "}"; + blockstates = getStates(block, properties); stateHashes = getStateHashes(blockstates); stackHashes = getStackHashes(blockstates); } - private static Set getStates(@Nonnull Block block) { - return new HashSet<>(block.getStateDefinition().getPossibleStates()); + private static , P extends Property> P castToIProperty(Object value) { + //noinspection unchecked + return (P) value; + } + + private static Map, ?> parseProperties(Block block, String raw) { + ImmutableMap.Builder, Object> builder = ImmutableMap.builder(); + for (String pair : raw.split(",")) { + String[] parts = pair.split("="); + if (parts.length != 2) { + throw new IllegalArgumentException(String.format("\"%s\" is not a valid property-value pair", pair)); + } + String rawKey = parts[0]; + String rawValue = parts[1]; + Property key = block.getStateDefinition().getProperty(rawKey); + Comparable value = castToIProperty(key).getValue(rawValue) + .orElseThrow(() -> new IllegalArgumentException(String.format( + "\"%s\" is not a valid value for %s on %s", + rawValue, key, block + ))); + builder.put(key, value); + } + return builder.build(); + } + + private static Set getStates(@Nonnull Block block, @Nonnull Map, ?> properties) { + return block.getStateDefinition().getPossibleStates().stream() + .filter(blockstate -> properties.entrySet().stream().allMatch(entry -> + blockstate.getValue(entry.getKey()) == entry.getValue() + )) + .collect(Collectors.toSet()); } private static ImmutableSet getStateHashes(Set blockstates) { @@ -145,7 +182,23 @@ public boolean matches(ItemStack stack) { @Override public String toString() { - return String.format("BlockOptionalMeta{block=%s}", block); + return String.format("BlockOptionalMeta{block=%s,properties=%s}", block, propertiesDescription); + } + + public BlockState getAnyBlockState() { + if (blockstates.size() > 0) { + return blockstates.iterator().next(); + } + + return null; + } + + public Set getAllBlockStates() { + return blockstates; + } + + public Set stackHashes() { + return stackHashes; } private static Method getVanillaServerPack; @@ -165,11 +218,11 @@ private static VanillaPackResources getVanillaServerPack() { return null; } - public static LootTables getManager() { + public static LootDataManager getManager() { if (lootTables == null) { MultiPackResourceManager resources = new MultiPackResourceManager(PackType.SERVER_DATA, List.of(getVanillaServerPack())); ReloadableResourceManager resourceManager = new ReloadableResourceManager(PackType.SERVER_DATA); - lootTables = new LootTables(predicate); + lootTables = new LootDataManager(); resourceManager.registerReloadListener(lootTables); try { resourceManager.createReload(new ThreadPerTaskExecutor(Thread::new), new ThreadPerTaskExecutor(Thread::new), CompletableFuture.completedFuture(Unit.INSTANCE), resources.listPacks().toList()).done().get(); @@ -189,14 +242,17 @@ private static synchronized List drops(Block b) { } else { List items = new ArrayList<>(); try { - getManager().get(lootTableLocation).getRandomItems( - new LootContext.Builder(ServerLevelStub.fastCreate()) - .withRandom(RandomSource.create()) - .withParameter(LootContextParams.ORIGIN, Vec3.atLowerCornerOf(BlockPos.ZERO)) - .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) - .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null) - .withParameter(LootContextParams.BLOCK_STATE, block.defaultBlockState()) - .create(LootContextParamSets.BLOCK), + + getManager().getLootTable(lootTableLocation).getRandomItemsRaw( + new LootContext.Builder( + new LootParams.Builder(ServerLevelStub.fastCreate()) + .withParameter(LootContextParams.ORIGIN, Vec3.atLowerCornerOf(BlockPos.ZERO)) + .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) + .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null) + .withParameter(LootContextParams.BLOCK_STATE, block.defaultBlockState()) + .create(LootContextParamSets.BLOCK) + ).withOptionalRandomSeed(1L) + .create(null), stack -> items.add(stack.getItem()) ); } catch (Exception e) { @@ -207,24 +263,18 @@ private static synchronized List drops(Block b) { }); } - public static PredicateManager getPredicateManager() { - return predicate; - } - - public BlockState getAnyBlockState() { - if (blockstates.size() > 0) { - return blockstates.iterator().next(); - } - - return null; - } - private static class ServerLevelStub extends ServerLevel { private static Minecraft client = Minecraft.getInstance(); private static Unsafe unsafe = getUnsafe(); - public ServerLevelStub(MinecraftServer $$0, Executor $$1, LevelStorageSource.LevelStorageAccess $$2, ServerLevelData $$3, ResourceKey $$4, LevelStem $$5, ChunkProgressListener $$6, boolean $$7, long $$8, List $$9, boolean $$10) { - super($$0, $$1, $$2, $$3, $$4, $$5, $$6, $$7, $$8, $$9, $$10); + public ServerLevelStub(MinecraftServer $$0, Executor $$1, LevelStorageSource.LevelStorageAccess $$2, ServerLevelData $$3, ResourceKey $$4, LevelStem $$5, ChunkProgressListener $$6, boolean $$7, long $$8, List $$9, boolean $$10, @Nullable RandomSequences $$11) { + super($$0, $$1, $$2, $$3, $$4, $$5, $$6, $$7, $$8, $$9, $$10, $$11); + } + + @Override + public FeatureFlagSet enabledFeatures() { + assert client.level != null; + return client.level.enabledFeatures(); } public static ServerLevelStub fastCreate() { @@ -245,11 +295,5 @@ public static Unsafe getUnsafe() { } } - @Override - public FeatureFlagSet enabledFeatures() { - assert client.level != null; - return client.level.enabledFeatures(); - } - } } diff --git a/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java index 86d19ff5e..7ab01cc87 100644 --- a/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java +++ b/src/api/java/baritone/api/utils/BlockOptionalMetaLookup.java @@ -17,68 +17,70 @@ package baritone.api.utils; +import baritone.api.utils.accessor.IItemStack; +import com.google.common.collect.ImmutableSet; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Stream; public class BlockOptionalMetaLookup { - + private final ImmutableSet blockSet; + private final ImmutableSet blockStateSet; + private final ImmutableSet stackHashes; private final BlockOptionalMeta[] boms; public BlockOptionalMetaLookup(BlockOptionalMeta... boms) { this.boms = boms; + Set blocks = new HashSet<>(); + Set blockStates = new HashSet<>(); + Set stacks = new HashSet<>(); + for (BlockOptionalMeta bom : boms) { + blocks.add(bom.getBlock()); + blockStates.addAll(bom.getAllBlockStates()); + stacks.addAll(bom.stackHashes()); + } + this.blockSet = ImmutableSet.copyOf(blocks); + this.blockStateSet = ImmutableSet.copyOf(blockStates); + this.stackHashes = ImmutableSet.copyOf(stacks); } public BlockOptionalMetaLookup(Block... blocks) { - this.boms = Stream.of(blocks) + this(Stream.of(blocks) .map(BlockOptionalMeta::new) - .toArray(BlockOptionalMeta[]::new); + .toArray(BlockOptionalMeta[]::new)); + } public BlockOptionalMetaLookup(List blocks) { - this.boms = blocks.stream() + this(blocks.stream() .map(BlockOptionalMeta::new) - .toArray(BlockOptionalMeta[]::new); + .toArray(BlockOptionalMeta[]::new)); } public BlockOptionalMetaLookup(String... blocks) { - this.boms = Stream.of(blocks) + this(Stream.of(blocks) .map(BlockOptionalMeta::new) - .toArray(BlockOptionalMeta[]::new); + .toArray(BlockOptionalMeta[]::new)); } public boolean has(Block block) { - for (BlockOptionalMeta bom : boms) { - if (bom.getBlock() == block) { - return true; - } - } - - return false; + return blockSet.contains(block); } public boolean has(BlockState state) { - for (BlockOptionalMeta bom : boms) { - if (bom.matches(state)) { - return true; - } - } - - return false; + return blockStateSet.contains(state); } public boolean has(ItemStack stack) { - for (BlockOptionalMeta bom : boms) { - if (bom.matches(stack)) { - return true; - } - } - - return false; + int hash = ((IItemStack) (Object) stack).getBaritoneHash(); + hash -= stack.getDamageValue(); + return stackHashes.contains(hash); } public List blocks() { diff --git a/src/api/java/baritone/api/utils/BlockUtils.java b/src/api/java/baritone/api/utils/BlockUtils.java index d2132f9a5..a43d78e87 100644 --- a/src/api/java/baritone/api/utils/BlockUtils.java +++ b/src/api/java/baritone/api/utils/BlockUtils.java @@ -17,13 +17,13 @@ package baritone.api.utils; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; -import java.util.HashMap; -import java.util.Map; - public class BlockUtils { private static transient Map resourceCache = new HashMap<>(); @@ -64,6 +64,5 @@ public static Block stringToBlockNullable(String name) { return block; } - private BlockUtils() { - } + private BlockUtils() {} } diff --git a/src/api/java/baritone/api/utils/BooleanBinaryOperator.java b/src/api/java/baritone/api/utils/BooleanBinaryOperator.java new file mode 100644 index 000000000..cfb85e644 --- /dev/null +++ b/src/api/java/baritone/api/utils/BooleanBinaryOperator.java @@ -0,0 +1,27 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +/** + * @author Brady + */ +@FunctionalInterface +public interface BooleanBinaryOperator { + + boolean applyAsBoolean(boolean a, boolean b); +} diff --git a/src/api/java/baritone/api/utils/BooleanBinaryOperators.java b/src/api/java/baritone/api/utils/BooleanBinaryOperators.java new file mode 100644 index 000000000..11605c965 --- /dev/null +++ b/src/api/java/baritone/api/utils/BooleanBinaryOperators.java @@ -0,0 +1,38 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +/** + * @author Brady + */ +public enum BooleanBinaryOperators implements BooleanBinaryOperator { + OR((a, b) -> a || b), + AND((a, b) -> a && b), + XOR((a, b) -> a ^ b); + + private final BooleanBinaryOperator op; + + BooleanBinaryOperators(BooleanBinaryOperator op) { + this.op = op; + } + + @Override + public boolean applyAsBoolean(boolean a, boolean b) { + return this.op.applyAsBoolean(a, b); + } +} diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java index 2e1830c02..507cd85a7 100755 --- a/src/api/java/baritone/api/utils/Helper.java +++ b/src/api/java/baritone/api/utils/Helper.java @@ -18,7 +18,9 @@ package baritone.api.utils; import baritone.api.BaritoneAPI; +import baritone.api.Settings; import net.minecraft.ChatFormatting; +import net.minecraft.client.GuiMessageTag; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -39,14 +41,20 @@ public interface Helper { /** * Instance of {@link Helper}. Used for static-context reference. */ - Helper HELPER = new Helper() { - }; + Helper HELPER = new Helper() {}; /** - * Instance of the game + * The main game instance returned by {@link Minecraft#getInstance()}. + * Deprecated since {@link IPlayerContext#minecraft()} should be used instead (In the majority of cases). */ + @Deprecated Minecraft mc = Minecraft.getInstance(); + /** + * The tag to assign to chat messages when {@link Settings#useMessageTag} is {@code true}. + */ + GuiMessageTag MESSAGE_TAG = new GuiMessageTag(0xFF55FF, null, Component.literal("Baritone message."), "Baritone"); + static Component getPrefix() { // Inner text component final Calendar now = Calendar.getInstance(); @@ -71,7 +79,7 @@ static Component getPrefix() { * @param message The message to display in the popup */ default void logToast(Component title, Component message) { - mc.execute(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message)); + Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message)); } /** @@ -132,7 +140,7 @@ default void logNotificationDirect(String message) { * @param error Whether to log as an error */ default void logNotificationDirect(String message, boolean error) { - mc.execute(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error)); + Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error)); } /** @@ -159,13 +167,15 @@ default void logDebug(String message) { */ default void logDirect(boolean logAsToast, Component... components) { MutableComponent component = Component.literal(""); - component.append(getPrefix()); - component.append(Component.literal(" ")); + if (!logAsToast && !BaritoneAPI.getSettings().useMessageTag.value) { + component.append(getPrefix()); + component.append(Component.literal(" ")); + } Arrays.asList(components).forEach(component::append); if (logAsToast) { logToast(getPrefix(), component); } else { - mc.execute(() -> BaritoneAPI.getSettings().logger.value.accept(component)); + Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().logger.value.accept(component)); } } @@ -225,4 +235,11 @@ default void logDirect(String message, boolean logAsToast) { default void logDirect(String message) { logDirect(message, BaritoneAPI.getSettings().logAsToast.value); } + + default void logUnhandledException(final Throwable exception) { + HELPER.logDirect("An unhandled exception occurred. " + + "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues", + ChatFormatting.RED); + exception.printStackTrace(); + } } diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java index 32e331544..a97475a5b 100644 --- a/src/api/java/baritone/api/utils/IPlayerContext.java +++ b/src/api/java/baritone/api/utils/IPlayerContext.java @@ -18,6 +18,7 @@ package baritone.api.utils; import baritone.api.cache.IWorldData; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; @@ -38,11 +39,11 @@ */ public interface IPlayerContext { + Minecraft minecraft(); + LocalPlayer player(); - static double eyeHeight(boolean ifSneaking) { - return ifSneaking ? 1.27 : 1.62; - } + IPlayerController playerController(); Level world(); @@ -54,7 +55,8 @@ default Stream entitiesStream() { return StreamSupport.stream(entities().spliterator(), false); } - IPlayerController playerController(); + + IWorldData worldData(); HitResult objectMouseOver(); @@ -74,8 +76,7 @@ default BetterBlockPos playerFeet() { if (world().getBlockState(feet).getBlock() instanceof SlabBlock) { return feet.above(); } - } catch (NullPointerException ignored) { - } + } catch (NullPointerException ignored) {} return feet; } @@ -88,11 +89,19 @@ default Vec3 playerHead() { return new Vec3(player().position().x, player().position().y + player().getEyeHeight(), player().position().z); } + default Vec3 playerMotion() { + return player().getDeltaMovement(); + } + + BetterBlockPos viewerPos(); + default Rotation playerRotations() { return new Rotation(player().getYRot(), player().getXRot()); } - IWorldData worldData(); + static double eyeHeight(boolean ifSneaking) { + return ifSneaking ? 1.27 : 1.62; + } /** * Returns the block that the crosshair is currently placed over. Updated once per tick. diff --git a/src/api/java/baritone/api/utils/Pair.java b/src/api/java/baritone/api/utils/Pair.java new file mode 100644 index 000000000..ca7259520 --- /dev/null +++ b/src/api/java/baritone/api/utils/Pair.java @@ -0,0 +1,59 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils; + +import java.util.Objects; + +/** + * @author Brady + */ +public final class Pair { + + private final A a; + private final B b; + + public Pair(A a, B b) { + this.a = a; + this.b = b; + } + + public A first() { + return this.a; + } + + public B second() { + return this.b; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || o.getClass() != Pair.class) { + return false; + } + Pair pair = (Pair) o; + return Objects.equals(this.a, pair.a) && Objects.equals(this.b, pair.b); + } + + @Override + public int hashCode() { + return 31 * Objects.hashCode(this.a) + Objects.hashCode(this.b); + } +} diff --git a/src/api/java/baritone/api/utils/RandomSpotNearby.java b/src/api/java/baritone/api/utils/RandomSpotNearby.java deleted file mode 100644 index 8dbcaa9d6..000000000 --- a/src/api/java/baritone/api/utils/RandomSpotNearby.java +++ /dev/null @@ -1,40 +0,0 @@ -package baritone.api.utils; - -import java.util.Random; - -public class RandomSpotNearby { - private final Random rand; - private BetterBlockPos end; - private double r; - private double old_r; - - public RandomSpotNearby(final double r) { - this.rand = new Random(); - this.r = r; - this.old_r = r; - } - - private final double MAX_DIST_INCREASE() { - return old_r * 2; - } - - private BetterBlockPos calc(final BetterBlockPos start) { - final double phi = rand.nextInt(360) + rand.nextDouble(); - final double radius = rand.nextInt((int) Math.round(r)) + rand.nextDouble(); //rand.nextDouble(r) + rand.nextDouble(); - final int x = (int) Math.round(radius * Math.sin(phi)); - final int z = (int) Math.round(radius * Math.cos(phi)); - - this.end = new BetterBlockPos(start.getX() + x, start.getY(), start.getZ() + z); - return this.end; - } - - public BetterBlockPos next(final BetterBlockPos start) { - this.r = (this.r - this.old_r > MAX_DIST_INCREASE()) ? this.old_r : this.r; - return next(start, 0.3d); - } - - public BetterBlockPos next(final BetterBlockPos start, final double increaseRadius) { - this.r += increaseRadius; - return calc(start); - } -} diff --git a/src/api/java/baritone/api/utils/RayTraceUtils.java b/src/api/java/baritone/api/utils/RayTraceUtils.java index 022281b64..38199d071 100644 --- a/src/api/java/baritone/api/utils/RayTraceUtils.java +++ b/src/api/java/baritone/api/utils/RayTraceUtils.java @@ -28,8 +28,7 @@ */ public final class RayTraceUtils { public static ClipContext.Fluid fluidHandling = ClipContext.Fluid.NONE; - private RayTraceUtils() { - } + private RayTraceUtils() {} /** * Performs a block raytrace with the specified rotations. This should only be used when @@ -52,13 +51,14 @@ public static HitResult rayTraceTowards(Entity entity, Rotation rotation, double } else { start = entity.getEyePosition(1.0F); // do whatever is correct } - Vec3 direction = RotationUtils.calcVector3dFromRotation(rotation); + + Vec3 direction = RotationUtils.calcLookDirectionFromRotation(rotation); Vec3 end = start.add( direction.x * blockReachDistance, direction.y * blockReachDistance, direction.z * blockReachDistance ); - return entity.level.clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, fluidHandling, entity)); + return entity.level().clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, fluidHandling, entity)); } public static Vec3 inferSneakingEyePosition(Entity entity) { diff --git a/src/api/java/baritone/api/utils/Rotation.java b/src/api/java/baritone/api/utils/Rotation.java index 36beea7bc..c75a9a559 100644 --- a/src/api/java/baritone/api/utils/Rotation.java +++ b/src/api/java/baritone/api/utils/Rotation.java @@ -26,12 +26,12 @@ public class Rotation { /** * The yaw angle of this Rotation */ - private float yaw; + private final float yaw; /** * The pitch angle of this Rotation */ - private float pitch; + private final float pitch; public Rotation(float yaw, float pitch) { this.yaw = yaw; @@ -113,6 +113,10 @@ public Rotation normalizeAndClamp() { ); } + public Rotation withPitch(float pitch) { + return new Rotation(this.yaw, pitch); + } + /** * Is really close to * diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 0f43c730b..6e310bb1c 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -44,11 +44,13 @@ 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; + public static final float DEG_TO_RAD_F = (float) DEG_TO_RAD; /** * 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 final float RAD_TO_DEG_F = (float) RAD_TO_DEG; /** * Offsets from the root block position to the center of each side. @@ -62,8 +64,7 @@ public final class RotationUtils { new Vec3(1, 0.5, 0.5) // East }; - private RotationUtils() { - } + private RotationUtils() {} /** * Calculates the rotation from BlockPosdest to BlockPosorig @@ -130,26 +131,31 @@ private static Rotation calcRotationFromVec3d(Vec3 orig, Vec3 dest) { * @param rotation The input rotation * @return Look vector for the rotation */ - public static Vec3 calcVector3dFromRotation(Rotation rotation) { - float f = Mth.cos(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); - float f1 = Mth.sin(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); - float f2 = -Mth.cos(-rotation.getPitch() * (float) DEG_TO_RAD); - float f3 = Mth.sin(-rotation.getPitch() * (float) DEG_TO_RAD); - return new Vec3((double) (f1 * f2), (double) f3, (double) (f * f2)); + public static Vec3 calcLookDirectionFromRotation(Rotation rotation) { + float flatZ = Mth.cos((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); + float flatX = Mth.sin((-rotation.getYaw() * DEG_TO_RAD_F) - (float) Math.PI); + float pitchBase = -Mth.cos(-rotation.getPitch() * DEG_TO_RAD_F); + float pitchHeight = Mth.sin(-rotation.getPitch() * DEG_TO_RAD_F); + return new Vec3(flatX * pitchBase, pitchHeight, flatZ * pitchBase); + } + + @Deprecated + public static Vec3 calcVec3dFromRotation(Rotation rotation) { + return calcLookDirectionFromRotation(rotation); } /** * @param ctx Context for the viewing entity * @param pos The target block position * @return The optional rotation - * @see #reachable(LocalPlayer, BlockPos, double) + * @see #reachable(IPlayerContext, BlockPos, double) */ public static Optional reachable(IPlayerContext ctx, BlockPos pos) { - return reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); + return reachable(ctx, pos, false); } public static Optional reachable(IPlayerContext ctx, BlockPos pos, boolean wouldSneak) { - return reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance(), wouldSneak); + return reachable(ctx, pos, ctx.playerController().getBlockReachDistance(), wouldSneak); } /** @@ -159,18 +165,17 @@ public static Optional reachable(IPlayerContext ctx, BlockPos pos, boo * 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 ctx Context for the viewing entity * @param pos The target block position * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ - public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { - return reachable(entity, pos, blockReachDistance, false); + public static Optional reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance) { + return reachable(ctx, pos, blockReachDistance, false); } - public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { - IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); - if (baritone.getPlayerContext().isLookingAt(pos)) { + public static Optional reachable(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { + if (BaritoneAPI.getSettings().remainWithExistingLookDirection.value && ctx.isLookingAt(pos)) { /* * why add 0.0001? * to indicate that we actually have a desired pitch @@ -181,10 +186,10 @@ public static Optional reachable(LocalPlayer entity, BlockPos pos, dou * * or if you're a normal person literally all this does it ensure that we don't nudge the pitch to a normal level */ - Rotation hypothetical = new Rotation(entity.getYRot(), entity.getXRot() + 0.0001F); + Rotation hypothetical = ctx.playerRotations().add(new Rotation(0, 0.0001F)); if (wouldSneak) { // the concern here is: what if we're looking at it now, but as soon as we start sneaking we no longer are - HitResult result = RayTraceUtils.rayTraceTowards(entity, hypothetical, blockReachDistance, true); + HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), hypothetical, blockReachDistance, true); if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(pos)) { return Optional.of(hypothetical); // yes, if we sneaked we would still be looking at the block } @@ -192,14 +197,14 @@ public static Optional reachable(LocalPlayer entity, BlockPos pos, dou return Optional.of(hypothetical); } } - Optional possibleRotation = reachableCenter(entity, pos, blockReachDistance, wouldSneak); + Optional possibleRotation = reachableCenter(ctx, pos, blockReachDistance, wouldSneak); //System.out.println("center: " + possibleRotation); if (possibleRotation.isPresent()) { return possibleRotation; } - BlockState state = entity.level.getBlockState(pos); - VoxelShape shape = state.getShape(entity.level, pos); + BlockState state = ctx.world().getBlockState(pos); + VoxelShape shape = state.getShape(ctx.world(), pos); if (shape.isEmpty()) { shape = Shapes.block(); } @@ -207,7 +212,7 @@ public static Optional reachable(LocalPlayer entity, BlockPos pos, dou double xDiff = shape.min(Direction.Axis.X) * sideOffset.x + shape.max(Direction.Axis.X) * (1 - sideOffset.x); double yDiff = shape.min(Direction.Axis.Y) * sideOffset.y + shape.max(Direction.Axis.Y) * (1 - sideOffset.y); double zDiff = shape.min(Direction.Axis.Z) * sideOffset.z + shape.max(Direction.Axis.Z) * (1 - sideOffset.z); - possibleRotation = reachableOffset(entity, pos, new Vec3(pos.getX(), pos.getY(), pos.getZ()).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak); + possibleRotation = reachableOffset(ctx, pos, new Vec3(pos.getX(), pos.getY(), pos.getZ()).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak); if (possibleRotation.isPresent()) { return possibleRotation; } @@ -220,22 +225,23 @@ public static Optional reachable(LocalPlayer entity, BlockPos pos, dou * 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 ctx Context for the viewing entity * @param pos The target block position * @param offsetPos The position of the block with the offset applied. * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ - public static Optional reachableOffset(Entity entity, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { - Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getEyePosition(1.0F); - Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.getYRot(), entity.getXRot())); - HitResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak); + public static Optional reachableOffset(IPlayerContext ctx, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { + Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.player().getEyePosition(1.0F); + Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, ctx.playerRotations()); + Rotation actualRotation = BaritoneAPI.getProvider().getBaritoneForPlayer(ctx.player()).getLookBehavior().getAimProcessor().peekRotation(rotation); + HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRotation, blockReachDistance, wouldSneak); //System.out.println(result); if (result != null && result.getType() == HitResult.Type.BLOCK) { if (((BlockHitResult) result).getBlockPos().equals(pos)) { return Optional.of(rotation); } - if (entity.level.getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { + if (ctx.world().getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { return Optional.of(rotation); } } @@ -246,12 +252,46 @@ public static Optional reachableOffset(Entity entity, BlockPos pos, Ve * 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 ctx Context for the viewing entity * @param pos The target block position * @param blockReachDistance The block reach distance of the entity * @return The optional rotation */ + public static Optional reachableCenter(IPlayerContext ctx, BlockPos pos, double blockReachDistance, boolean wouldSneak) { + return reachableOffset(ctx, pos, VecUtils.calculateBlockCenter(ctx.world(), pos), blockReachDistance, wouldSneak); + } + + @Deprecated + public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { + return reachable(entity, pos, blockReachDistance, false); + } + + @Deprecated + public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); + IPlayerContext ctx = baritone.getPlayerContext(); + return reachable(ctx, pos, blockReachDistance, wouldSneak); + } + + @Deprecated + public static Optional reachableOffset(Entity entity, BlockPos pos, Vec3 offsetPos, double blockReachDistance, boolean wouldSneak) { + Vec3 eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getEyePosition(1.0F); + Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.getYRot(), entity.getXRot())); + HitResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak); + //System.out.println(result); + if (result != null && result.getType() == HitResult.Type.BLOCK) { + if (((BlockHitResult) result).getBlockPos().equals(pos)) { + return Optional.of(rotation); + } + if (entity.level().getBlockState(pos).getBlock() instanceof BaseFireBlock && ((BlockHitResult) result).getBlockPos().equals(pos.below())) { + return Optional.of(rotation); + } + } + return Optional.empty(); + } + + @Deprecated public static Optional reachableCenter(Entity entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { - return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(entity.level, pos), blockReachDistance, wouldSneak); + return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(entity.level(), pos), blockReachDistance, wouldSneak); } } diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index c6e0608eb..53283cd33 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -21,12 +21,12 @@ import baritone.api.Settings; import net.minecraft.client.Minecraft; import net.minecraft.core.Direction; +import net.minecraft.core.Registry; import net.minecraft.core.Vec3i; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; - import java.awt.*; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -47,12 +47,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; - public class SettingsUtil { - private static final Path SETTINGS_PATH = Minecraft.getInstance().gameDirectory.toPath().resolve("baritone").resolve("settings.txt"); + public static final String SETTINGS_DEFAULT_NAME = "settings.txt"; private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?.+)"); // key and value split by the first space - private static final String[] JAVA_ONLY_SETTINGS = {"logger", "notifier", "toaster"}; private static boolean isComment(String line) { @@ -71,12 +69,12 @@ private static void forEachLine(Path file, Consumer consumer) throws IOE } } - public static void readAndApply(Settings settings) { + public static void readAndApply(Settings settings, String settingsName) { try { - forEachLine(SETTINGS_PATH, line -> { + forEachLine(settingsByName(settingsName), line -> { Matcher matcher = SETTING_PATTERN.matcher(line); if (!matcher.matches()) { - System.out.println("Invalid syntax in setting file: " + line); + Helper.HELPER.logDirect("Invalid syntax in setting file: " + line); return; } @@ -85,29 +83,33 @@ public static void readAndApply(Settings settings) { try { parseAndApply(settings, settingName, settingValue); } catch (Exception ex) { - System.out.println("Unable to parse line " + line); + Helper.HELPER.logDirect("Unable to parse line " + line); ex.printStackTrace(); } }); } catch (NoSuchFileException ignored) { - System.out.println("Baritone settings file not found, resetting."); + Helper.HELPER.logDirect("Baritone settings file not found, resetting."); } catch (Exception ex) { - System.out.println("Exception while reading Baritone settings, some settings may be reset to default values!"); + Helper.HELPER.logDirect("Exception while reading Baritone settings, some settings may be reset to default values!"); ex.printStackTrace(); } } public static synchronized void save(Settings settings) { - try (BufferedWriter out = Files.newBufferedWriter(SETTINGS_PATH)) { + try (BufferedWriter out = Files.newBufferedWriter(settingsByName(SETTINGS_DEFAULT_NAME))) { for (Settings.Setting setting : modifiedSettings(settings)) { out.write(settingToString(setting) + "\n"); } } catch (Exception ex) { - System.out.println("Exception thrown while saving Baritone settings!"); + Helper.HELPER.logDirect("Exception thrown while saving Baritone settings!"); ex.printStackTrace(); } } + private static Path settingsByName(String name) { + return Minecraft.getInstance().gameDirectory.toPath().resolve("baritone").resolve(name); + } + public static List modifiedSettings(Settings settings) { List modified = new ArrayList<>(); for (Settings.Setting setting : settings.allSettings) { @@ -115,7 +117,7 @@ public static List modifiedSettings(Settings settings) { System.out.println("NULL SETTING?" + setting.getName()); continue; } - if (javaOnlySetting(setting)) { + if (setting.isJavaOnly()) { continue; // NO } if (setting.value == setting.defaultValue) { @@ -169,7 +171,7 @@ public static String maybeCensor(int coord) { } public static String settingToString(Settings.Setting setting) throws IllegalStateException { - if (javaOnlySetting(setting)) { + if (setting.isJavaOnly()) { return setting.getName(); } @@ -177,18 +179,14 @@ public static String settingToString(Settings.Setting setting) throws IllegalSta } /** - * This should always be the same as whether the setting can be parsed from or serialized to a string + * Deprecated. Use {@link Settings.Setting#isJavaOnly()} instead. * - * @param the setting + * @param setting The Setting * @return true if the setting can not be set or read by the user */ + @Deprecated public static boolean javaOnlySetting(Settings.Setting setting) { - for (String name : JAVA_ONLY_SETTINGS) { // no JAVA_ONLY_SETTINGS.contains(...) because that would be case sensitive - if (setting.getName().equalsIgnoreCase(name)) { - return true; - } - } - return false; + return setting.isJavaOnly(); } public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { @@ -205,6 +203,28 @@ public static void parseAndApply(Settings settings, String settingName, String s setting.value = parsed; } + private interface ISettingParser { + + T parse(ParserContext context, String raw); + + String toString(ParserContext context, T value); + + boolean accepts(Type type); + } + + private static class ParserContext { + + private final Settings.Setting setting; + + private ParserContext(Settings.Setting setting) { + this.setting = setting; + } + + private Settings.Setting getSetting() { + return this.setting; + } + } + private enum Parser implements ISettingParser { DOUBLE(Double.class, Double::parseDouble), @@ -333,26 +353,4 @@ public static Parser getParser(Type type) { .findFirst().orElse(null); } } - - private interface ISettingParser { - - T parse(ParserContext context, String raw); - - String toString(ParserContext context, T value); - - boolean accepts(Type type); - } - - private static class ParserContext { - - private final Settings.Setting setting; - - private ParserContext(Settings.Setting setting) { - this.setting = setting; - } - - private Settings.Setting getSetting() { - return this.setting; - } - } } diff --git a/src/api/java/baritone/api/utils/TypeUtils.java b/src/api/java/baritone/api/utils/TypeUtils.java index 01fe45459..457cc449a 100644 --- a/src/api/java/baritone/api/utils/TypeUtils.java +++ b/src/api/java/baritone/api/utils/TypeUtils.java @@ -26,8 +26,7 @@ */ public final class TypeUtils { - private TypeUtils() { - } + private TypeUtils() {} /** * Resolves the "base type" for the specified type. For example, if the specified diff --git a/src/api/java/baritone/api/utils/VecUtils.java b/src/api/java/baritone/api/utils/VecUtils.java index dceab6c61..4ea94b95a 100644 --- a/src/api/java/baritone/api/utils/VecUtils.java +++ b/src/api/java/baritone/api/utils/VecUtils.java @@ -32,8 +32,7 @@ */ public final class VecUtils { - private VecUtils() { - } + private VecUtils() {} /** * Calculates the center of the block at the specified position's bounding box diff --git a/src/api/java/baritone/api/utils/gui/BaritoneToast.java b/src/api/java/baritone/api/utils/gui/BaritoneToast.java index 87f0aac6a..6cdc57cc0 100644 --- a/src/api/java/baritone/api/utils/gui/BaritoneToast.java +++ b/src/api/java/baritone/api/utils/gui/BaritoneToast.java @@ -17,8 +17,10 @@ package baritone.api.utils.gui; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.toasts.Toast; import net.minecraft.client.gui.components.toasts.ToastComponent; import net.minecraft.network.chat.Component; @@ -37,7 +39,7 @@ public BaritoneToast(Component titleComponent, Component subtitleComponent, long this.totalShowTime = totalShowTime; } - public Visibility render(PoseStack matrixStack, ToastComponent toastGui, long delta) { + public Visibility render(GuiGraphics gui, ToastComponent toastGui, long delta) { if (this.newDisplay) { this.firstDrawTime = delta; this.newDisplay = false; @@ -45,15 +47,13 @@ public Visibility render(PoseStack matrixStack, ToastComponent toastGui, long de //TODO: check - toastGui.getMinecraft().getTextureManager().bindForSetup(new ResourceLocation("textures/gui/toasts.png")); - //GlStateManager._color4f(1.0F, 1.0F, 1.0F, 255.0F); - toastGui.blit(matrixStack, 0, 0, 0, 32, 160, 32); + gui.blit(new ResourceLocation("textures/gui/toasts.png"), 0, 0, 0, 32, 160, 32); if (this.subtitle == null) { - toastGui.getMinecraft().font.draw(matrixStack, this.title, 18, 12, -11534256); + gui.drawString(toastGui.getMinecraft().font, this.title, 18, 12, -11534256); } else { - toastGui.getMinecraft().font.draw(matrixStack, this.title, 18, 7, -11534256); - toastGui.getMinecraft().font.draw(matrixStack, this.subtitle, 18, 18, -16777216); + gui.drawString(toastGui.getMinecraft().font, this.title, 18, 7, -11534256); + gui.drawString(toastGui.getMinecraft().font, this.subtitle, 18, 18, -16777216); } return delta - this.firstDrawTime < totalShowTime ? Visibility.SHOW : Visibility.HIDE; diff --git a/src/launch/java/baritone/launch/mixins/MixinChunkArray.java b/src/launch/java/baritone/launch/mixins/MixinChunkArray.java index 9f88fb1fe..711ca6554 100644 --- a/src/launch/java/baritone/launch/mixins/MixinChunkArray.java +++ b/src/launch/java/baritone/launch/mixins/MixinChunkArray.java @@ -18,13 +18,13 @@ package baritone.launch.mixins; import baritone.utils.accessor.IChunkArray; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.LevelChunk; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.util.concurrent.atomic.AtomicReferenceArray; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.LevelChunk; @Mixin(targets = "net.minecraft.client.multiplayer.ClientChunkCache$Storage") public abstract class MixinChunkArray implements IChunkArray { diff --git a/src/launch/java/baritone/launch/mixins/MixinClientChunkProvider.java b/src/launch/java/baritone/launch/mixins/MixinClientChunkProvider.java index d25837754..de465b82a 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientChunkProvider.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientChunkProvider.java @@ -19,14 +19,14 @@ import baritone.utils.accessor.IChunkArray; import baritone.utils.accessor.IClientChunkProvider; -import net.minecraft.client.multiplayer.ClientChunkCache; -import net.minecraft.client.multiplayer.ClientLevel; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import java.lang.reflect.Field; import java.util.Arrays; +import net.minecraft.client.multiplayer.ClientChunkCache; +import net.minecraft.client.multiplayer.ClientLevel; @Mixin(ClientChunkCache.class) public class MixinClientChunkProvider implements IClientChunkProvider { diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java index 1eafa8d54..d603ca632 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java @@ -20,28 +20,36 @@ import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; +import baritone.api.event.events.BlockChangeEvent; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.ChunkEvent; import baritone.api.event.events.type.EventState; +import baritone.api.utils.Pair; import baritone.cache.CachedChunk; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.client.multiplayer.CommonListenerCookie; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.network.Connection; import net.minecraft.network.protocol.game.*; import net.minecraft.world.level.ChunkPos; -import org.spongepowered.asm.mixin.Final; +import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.ArrayList; +import java.util.List; + /** * @author Brady * @since 8/3/2018 */ @Mixin(ClientPacketListener.class) -public class MixinClientPlayNetHandler { +public abstract class MixinClientPlayNetHandler extends ClientCommonPacketListenerImpl { // unused lol /*@Inject( @@ -67,9 +75,9 @@ private void preRead(SPacketChunkData packetIn, CallbackInfo ci) { } }*/ - @Shadow - @Final - private Minecraft minecraft; + protected MixinClientPlayNetHandler(final Minecraft arg, final Connection arg2, final CommonListenerCookie arg3) { + super(arg, arg2, arg3); + } @Inject( method = "sendChat(Ljava/lang/String;)V", @@ -117,7 +125,7 @@ private void preChunkUnload(ClientboundForgetLevelChunkPacket packet, CallbackIn LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( - new ChunkEvent(EventState.PRE, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) + new ChunkEvent(EventState.PRE, ChunkEvent.Type.UNLOAD, packet.pos().x, packet.pos().z) ); } } @@ -132,7 +140,7 @@ private void postChunkUnload(ClientboundForgetLevelChunkPacket packet, CallbackI LocalPlayer player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPacketListener) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( - new ChunkEvent(EventState.POST, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) + new ChunkEvent(EventState.POST, ChunkEvent.Type.UNLOAD, packet.pos().x, packet.pos().z) ); } } @@ -169,31 +177,22 @@ private void postHandleBlockChange(ClientboundBlockUpdatePacket packetIn, Callba at = @At("RETURN") ) private void postHandleMultiBlockChange(ClientboundSectionBlocksUpdatePacket packetIn, CallbackInfo ci) { - if (!Baritone.settings().repackOnAnyBlockChange.value) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((ClientPacketListener) (Object) this); + if (baritone == null) { return; } - ChunkPos[] chunkPos = new ChunkPos[1]; - packetIn.runUpdates((pos, state) -> { - if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(state.getBlock())) { - chunkPos[0] = new ChunkPos(pos); - } + + List> changes = new ArrayList<>(); + packetIn.runUpdates((mutPos, state) -> { + changes.add(new Pair<>(mutPos.immutable(), state)); }); - if (chunkPos[0] == null) { + if (changes.isEmpty()) { return; } - for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { - LocalPlayer player = ibaritone.getPlayerContext().player(); - if (player != null && player.connection == (ClientPacketListener) (Object) this) { - ibaritone.getGameEventHandler().onChunkEvent( - new ChunkEvent( - EventState.POST, - ChunkEvent.Type.POPULATE_FULL, - chunkPos[0].x, - chunkPos[0].z - ) - ); - } - } + baritone.getGameEventHandler().onBlockChange(new BlockChangeEvent( + new ChunkPos(changes.get(0).first()), + changes + )); } @Inject( @@ -211,4 +210,71 @@ private void onPlayerDeath(ClientboundPlayerCombatKillPacket packetIn, CallbackI } } } + + /* + @Inject( + method = "handleChunkData", + at = @At( + value = "INVOKE", + target = "net/minecraft/world/chunk/Chunk.read(Lnet/minecraft/network/PacketBuffer;IZ)V" + ) + ) + private void preRead(SPacketChunkData packetIn, CallbackInfo ci) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); + if (baritone == null) { + return; + } + baritone.getGameEventHandler().onChunkEvent( + new ChunkEvent( + EventState.PRE, + packetIn.isFullChunk() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, + packetIn.getChunkX(), + packetIn.getChunkZ() + ) + ); + } + + @Inject( + method = "handleChunkData", + at = @At("RETURN") + ) + private void postHandleChunkData(SPacketChunkData packetIn, CallbackInfo ci) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); + if (baritone == null) { + return; + } + baritone.getGameEventHandler().onChunkEvent( + new ChunkEvent( + EventState.POST, + packetIn.isFullChunk() ? ChunkEvent.Type.POPULATE_FULL : ChunkEvent.Type.POPULATE_PARTIAL, + packetIn.getChunkX(), + packetIn.getChunkZ() + ) + ); + } + + @Inject( + method = "handleBlockChange", + at = @At("RETURN") + ) + private void postHandleBlockChange(SPacketBlockChange packetIn, CallbackInfo ci) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForConnection((NetHandlerPlayClient) (Object) this); + if (baritone == null) { + return; + } + + final ChunkPos pos = new ChunkPos(packetIn.getBlockPosition().getX() >> 4, packetIn.getBlockPosition().getZ() >> 4); + final Pair changed = new Pair<>(packetIn.getBlockPosition(), packetIn.getBlockState()); + baritone.getGameEventHandler().onBlockChange(new BlockChangeEvent(pos, Collections.singletonList(changed))); + } + + @Inject( + method = "handleMultiBlockChange", + at = @At("RETURN") + ) + private void postHandleMultiBlockChange(SPacketMultiBlockChange packetIn, CallbackInfo ci) { + + } + + */ } diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java index 4eea7d219..24e807f62 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java @@ -43,9 +43,8 @@ public class MixinClientPlayerEntity { method = "tick", at = @At( value = "INVOKE", - target = "net/minecraft/client/player/LocalPlayer.isPassenger()Z", - shift = At.Shift.BY, - by = -3 + target = "net/minecraft/client/player/AbstractClientPlayer.tick()V", + shift = At.Shift.AFTER ) ) private void onPreUpdate(CallbackInfo ci) { @@ -55,22 +54,6 @@ private void onPreUpdate(CallbackInfo ci) { } } - @Inject( - method = "tick", - at = @At( - value = "INVOKE", - target = "net/minecraft/client/player/LocalPlayer.sendPosition()V", - shift = At.Shift.BY, - by = 2 - ) - ) - private void onPostUpdate(CallbackInfo ci) { - IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); - if (baritone != null) { - baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST)); - } - } - @Redirect( method = "aiStep", at = @At( @@ -122,4 +105,19 @@ private void updateRidden(CallbackInfo cb) { ((LookBehavior) baritone.getLookBehavior()).pig(); } } + + @Redirect( + method = "aiStep", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/player/LocalPlayer;tryToStartFallFlying()Z" + ) + ) + private boolean tryToStartFallFlying(final LocalPlayer instance) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(instance); + if (baritone != null && baritone.getPathingBehavior().isPathing()) { + return false; + } + return instance.tryToStartFallFlying(); + } } diff --git a/src/launch/java/baritone/launch/mixins/MixinCommandSuggestionHelper.java b/src/launch/java/baritone/launch/mixins/MixinCommandSuggestionHelper.java index 70d5861cb..2bcdb2d78 100644 --- a/src/launch/java/baritone/launch/mixins/MixinCommandSuggestionHelper.java +++ b/src/launch/java/baritone/launch/mixins/MixinCommandSuggestionHelper.java @@ -19,6 +19,7 @@ import baritone.api.BaritoneAPI; import baritone.api.event.events.TabCompleteEvent; +import com.mojang.brigadier.ParseResults; import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.suggestion.Suggestion; import com.mojang.brigadier.suggestion.Suggestions; @@ -51,9 +52,18 @@ public class MixinCommandSuggestionHelper { @Final private List commandUsage; + @Shadow + private ParseResults currentParse; + @Shadow private CompletableFuture pendingSuggestions; + @Shadow + private CommandSuggestions.SuggestionsList suggestions; + + @Shadow + boolean keepSuggestions; + @Inject( method = "updateCommandInfo", at = @At("HEAD"), @@ -74,27 +84,32 @@ private void preUpdateSuggestion(CallbackInfo ci) { if (event.completions != null) { ci.cancel(); + this.currentParse = null; // stop coloring + + if (this.keepSuggestions) { // Supress suggestions update when cycling suggestions. + return; + } + + this.input.setSuggestion(null); // clear old suggestions + this.suggestions = null; // TODO: Support populating the command usage this.commandUsage.clear(); if (event.completions.length == 0) { this.pendingSuggestions = Suggestions.empty(); } else { - int offset = this.input.getValue().endsWith(" ") - ? this.input.getCursorPosition() - : this.input.getValue().lastIndexOf(" ") + 1; // If there is no space this is still 0 haha yes + StringRange range = StringRange.between(prefix.lastIndexOf(" ") + 1, prefix.length()); // if there is no space this starts at 0 List suggestionList = Stream.of(event.completions) - .map(s -> new Suggestion(StringRange.between(offset, offset + s.length()), s)) + .map(s -> new Suggestion(range, s)) .collect(Collectors.toList()); - Suggestions suggestions = new Suggestions( - StringRange.between(offset, offset + suggestionList.stream().mapToInt(s -> s.getText().length()).max().orElse(0)), - suggestionList); + Suggestions suggestions = new Suggestions(range, suggestionList); this.pendingSuggestions = new CompletableFuture<>(); this.pendingSuggestions.complete(suggestions); } + ((CommandSuggestions) (Object) this).showSuggestions(true); // actually populate the suggestions list from the suggestions future } } } diff --git a/src/launch/java/baritone/launch/mixins/MixinEntity.java b/src/launch/java/baritone/launch/mixins/MixinEntity.java index b48e919f2..748095351 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntity.java @@ -23,6 +23,7 @@ import net.minecraft.world.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -33,21 +34,25 @@ public class MixinEntity { @Shadow private float yRot; - float yawRestore; + @Shadow + private float xRot; + + @Unique + private RotationMoveEvent motionUpdateRotationEvent; @Inject( method = "moveRelative", at = @At("HEAD") ) private void moveRelativeHead(CallbackInfo info) { - this.yawRestore = this.yRot; // noinspection ConstantConditions if (!LocalPlayer.class.isInstance(this) || BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this) == null) { return; } - RotationMoveEvent motionUpdateRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.yRot); + this.motionUpdateRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.yRot, this.xRot); BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this).getGameEventHandler().onPlayerRotationMove(motionUpdateRotationEvent); - this.yRot = motionUpdateRotationEvent.getYaw(); + this.yRot = this.motionUpdateRotationEvent.getYaw(); + this.xRot = this.motionUpdateRotationEvent.getPitch(); } @Inject( @@ -55,6 +60,10 @@ private void moveRelativeHead(CallbackInfo info) { at = @At("RETURN") ) private void moveRelativeReturn(CallbackInfo info) { - this.yRot = this.yawRestore; + if (this.motionUpdateRotationEvent != null) { + this.yRot = this.motionUpdateRotationEvent.getOriginal().getYaw(); + this.xRot = this.motionUpdateRotationEvent.getOriginal().getPitch(); + this.motionUpdateRotationEvent = null; + } } } diff --git a/src/launch/java/baritone/launch/mixins/MixinFireworkRocketEntity.java b/src/launch/java/baritone/launch/mixins/MixinFireworkRocketEntity.java new file mode 100644 index 000000000..b4c8e05b6 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinFireworkRocketEntity.java @@ -0,0 +1,60 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.IFireworkRocketEntity; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.OptionalInt; + +@Mixin(FireworkRocketEntity.class) +public abstract class MixinFireworkRocketEntity extends Entity implements IFireworkRocketEntity { + + @Shadow + @Final + private static EntityDataAccessor DATA_ATTACHED_TO_TARGET; + + @Shadow + private LivingEntity attachedToEntity; + + @Shadow + public abstract boolean isAttachedToEntity(); + + private MixinFireworkRocketEntity(Level level) { + super(EntityType.FIREWORK_ROCKET, level); + } + + @Override + public LivingEntity getBoostedEntity() { + if (this.isAttachedToEntity() && this.attachedToEntity == null) { // isAttachedToEntity checks if the optional is present + final Entity entity = this.level().getEntity(this.entityData.get(DATA_ATTACHED_TO_TARGET).getAsInt()); + if (entity instanceof LivingEntity) { + this.attachedToEntity = (LivingEntity) entity; + } + } + return this.attachedToEntity; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinLivingEntity.java b/src/launch/java/baritone/launch/mixins/MixinLivingEntity.java index c14f1b846..ada92f6c2 100644 --- a/src/launch/java/baritone/launch/mixins/MixinLivingEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinLivingEntity.java @@ -25,12 +25,16 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.Optional; + /** * @author Brady * @since 9/10/2018 @@ -41,9 +45,13 @@ public abstract class MixinLivingEntity extends Entity { /** * Event called to override the movement direction when jumping */ + @Unique private RotationMoveEvent jumpRotationEvent; - public MixinLivingEntity(EntityType entityTypeIn, Level worldIn) { + @Unique + private RotationMoveEvent elytraRotationEvent; + + private MixinLivingEntity(EntityType entityTypeIn, Level worldIn) { super(entityTypeIn, worldIn); } @@ -52,14 +60,10 @@ public MixinLivingEntity(EntityType entityTypeIn, Level worldIn) { at = @At("HEAD") ) private void preMoveRelative(CallbackInfo ci) { - // noinspection ConstantConditions - if (LocalPlayer.class.isInstance(this)) { - IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); - if (baritone != null) { - this.jumpRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.JUMP, this.getYRot()); - baritone.getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); - } - } + this.getBaritone().ifPresent(baritone -> { + this.jumpRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.JUMP, this.getYRot(), this.getXRot()); + baritone.getGameEventHandler().onPlayerRotationMove(this.jumpRotationEvent); + }); } @Redirect( @@ -76,5 +80,45 @@ private float overrideYaw(LivingEntity self) { return self.getYRot(); } + @Inject( + method = "travel", + at = @At( + value = "INVOKE", + target = "net/minecraft/world/entity/LivingEntity.getLookAngle()Lnet/minecraft/world/phys/Vec3;" + ) + ) + private void onPreElytraMove(Vec3 direction, CallbackInfo ci) { + this.getBaritone().ifPresent(baritone -> { + this.elytraRotationEvent = new RotationMoveEvent(RotationMoveEvent.Type.MOTION_UPDATE, this.getYRot(), this.getXRot()); + baritone.getGameEventHandler().onPlayerRotationMove(this.elytraRotationEvent); + this.setYRot(this.elytraRotationEvent.getYaw()); + this.setXRot(this.elytraRotationEvent.getPitch()); + }); + } + @Inject( + method = "travel", + at = @At( + value = "INVOKE", + target = "net/minecraft/world/entity/LivingEntity.move(Lnet/minecraft/world/entity/MoverType;Lnet/minecraft/world/phys/Vec3;)V", + shift = At.Shift.AFTER + ) + ) + private void onPostElytraMove(Vec3 direction, CallbackInfo ci) { + if (this.elytraRotationEvent != null) { + this.setYRot(this.elytraRotationEvent.getOriginal().getYaw()); + this.setXRot(this.elytraRotationEvent.getOriginal().getPitch()); + this.elytraRotationEvent = null; + } + } + + @Unique + private Optional getBaritone() { + // noinspection ConstantConditions + if (LocalPlayer.class.isInstance(this)) { + return Optional.ofNullable(BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this)); + } else { + return Optional.empty(); + } + } } diff --git a/src/launch/java/baritone/launch/mixins/MixinLootContext.java b/src/launch/java/baritone/launch/mixins/MixinLootContext.java index 7c7960cf1..223444e12 100644 --- a/src/launch/java/baritone/launch/mixins/MixinLootContext.java +++ b/src/launch/java/baritone/launch/mixins/MixinLootContext.java @@ -21,8 +21,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.LootTables; -import net.minecraft.world.level.storage.loot.PredicateManager; +import net.minecraft.world.level.storage.loot.LootDataManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @@ -48,27 +47,13 @@ private MinecraftServer getServer(ServerLevel world) { method = "create", at = @At( value = "INVOKE", - target = "Lnet/minecraft/server/MinecraftServer;getLootTables()Lnet/minecraft/world/level/storage/loot/LootTables;" + target = "Lnet/minecraft/server/MinecraftServer;getLootData()Lnet/minecraft/world/level/storage/loot/LootDataManager;" ) ) - private LootTables getLootTableManager(MinecraftServer server) { + private LootDataManager getLootTableManager(MinecraftServer server) { if (server == null) { return BlockOptionalMeta.getManager(); } - return server.getLootTables(); - } - - @Redirect( - method = "create", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/MinecraftServer;getPredicateManager()Lnet/minecraft/world/level/storage/loot/PredicateManager;" - ) - ) - private PredicateManager getLootPredicateManager(MinecraftServer server) { - if (server == null) { - return BlockOptionalMeta.getPredicateManager(); - } - return server.getPredicateManager(); + return server.getLootData(); } } diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java index 604feaa85..e615a822a 100644 --- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java +++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java @@ -19,6 +19,7 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; +import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.WorldEvent; import baritone.api.event.events.type.EventState; @@ -29,9 +30,11 @@ import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.function.BiFunction; @@ -48,6 +51,9 @@ public class MixinMinecraft { @Shadow public ClientLevel level; + @Unique + private BiFunction tickProvider; + @Inject( method = "", at = @At("RETURN") @@ -56,30 +62,68 @@ private void postInit(CallbackInfo ci) { BaritoneAPI.getProvider().getPrimaryBaritone(); } - @Inject( method = "tick", at = @At( value = "FIELD", opcode = Opcodes.GETFIELD, - target = "Lnet/minecraft/client/Minecraft;screen:Lnet/minecraft/client/gui/screens/Screen;", - ordinal = 4, - shift = At.Shift.BY, - by = -3 + target = "net/minecraft/client/Minecraft.screen:Lnet/minecraft/client/gui/screens/Screen;", + ordinal = 0, + shift = At.Shift.BEFORE + ), + slice = @Slice( + from = @At( + value = "FIELD", + opcode = Opcodes.PUTFIELD, + target = "net/minecraft/client/Minecraft.missTime:I" + ) ) ) private void runTick(CallbackInfo ci) { - final BiFunction tickProvider = TickEvent.createNextProvider(); + this.tickProvider = TickEvent.createNextProvider(); for (IBaritone baritone : BaritoneAPI.getProvider().getAllBaritones()) { - TickEvent.Type type = baritone.getPlayerContext().player() != null && baritone.getPlayerContext().world() != null ? TickEvent.Type.IN : TickEvent.Type.OUT; + baritone.getGameEventHandler().onTick(this.tickProvider.apply(EventState.PRE, type)); + } + } + + @Inject( + method = "tick", + at = @At("RETURN") + ) + private void postRunTick(CallbackInfo ci) { + if (this.tickProvider == null) { + return; + } - baritone.getGameEventHandler().onTick(tickProvider.apply(EventState.PRE, type)); + for (IBaritone baritone : BaritoneAPI.getProvider().getAllBaritones()) { + TickEvent.Type type = baritone.getPlayerContext().player() != null && baritone.getPlayerContext().world() != null + ? TickEvent.Type.IN + : TickEvent.Type.OUT; + baritone.getGameEventHandler().onPostTick(this.tickProvider.apply(EventState.POST, type)); } + this.tickProvider = null; + } + + @Inject( + method = "tick", + at = @At( + value = "INVOKE", + target = "net/minecraft/client/multiplayer/ClientLevel.tickEntities()V", + shift = At.Shift.AFTER + ) + ) + private void postUpdateEntities(CallbackInfo ci) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(this.player); + if (baritone != null) { + // Intentionally call this after all entities have been updated. That way, any modification to rotations + // can be recognized by other entity code. (Fireworks and Pigs, for example) + baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST)); + } } @Inject( @@ -123,12 +167,25 @@ private void postLoadWorld(ClientLevel world, CallbackInfo ci) { at = @At( value = "FIELD", opcode = Opcodes.GETFIELD, - target = "Lnet/minecraft/client/gui/screens/Screen;passEvents:Z" + target = "Lnet/minecraft/client/Minecraft;screen:Lnet/minecraft/client/gui/screens/Screen;" + ), + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/components/DebugScreenOverlay;showDebugScreen()Z" + ), + to = @At( + value = "CONSTANT", + args = "stringValue=Keybindings" + ) ) ) - private boolean passEvents(Screen screen) { + private Screen passEvents(Minecraft instance) { // allow user input is only the primary baritone - return (BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().isPathing() && player != null) || screen.passEvents; + if (BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().isPathing() && player != null) { + return null; + } + return instance.screen; } // TODO diff --git a/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java b/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java index 52543ed91..2d35800eb 100644 --- a/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java +++ b/src/launch/java/baritone/launch/mixins/MixinNetworkManager.java @@ -52,7 +52,7 @@ public class MixinNetworkManager { method = "sendPacket", at = @At("HEAD") ) - private void preDispatchPacket(Packet packet, PacketSendListener packetSendListener, CallbackInfo ci) { + private void preDispatchPacket(Packet packet, PacketSendListener packetSendListener, boolean flush, CallbackInfo ci) { if (this.receiving != PacketFlow.CLIENTBOUND) { return; } @@ -68,7 +68,7 @@ private void preDispatchPacket(Packet packet, PacketSendListener packetSendLi method = "sendPacket", at = @At("RETURN") ) - private void postDispatchPacket(Packet packet, PacketSendListener packetSendListener, CallbackInfo ci) { + private void postDispatchPacket(Packet packet, PacketSendListener packetSendListener, boolean flush, CallbackInfo ci) { if (this.receiving != PacketFlow.CLIENTBOUND) { return; } diff --git a/src/launch/java/baritone/launch/mixins/MixinPalettedContainer$Data.java b/src/launch/java/baritone/launch/mixins/MixinPalettedContainer$Data.java new file mode 100644 index 000000000..e4c3cc61d --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinPalettedContainer$Data.java @@ -0,0 +1,34 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.IPalettedContainer.IData; +import net.minecraft.util.BitStorage; +import net.minecraft.world.level.chunk.Palette; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(targets = "net/minecraft/world/level/chunk/PalettedContainer$Data") +public abstract class MixinPalettedContainer$Data implements IData { + + @Accessor + public abstract Palette getPalette(); + + @Accessor + public abstract BitStorage getStorage(); +} diff --git a/src/launch/java/baritone/launch/mixins/MixinPalettedContainer.java b/src/launch/java/baritone/launch/mixins/MixinPalettedContainer.java new file mode 100644 index 000000000..a1b409a09 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinPalettedContainer.java @@ -0,0 +1,100 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.IPalettedContainer; +import baritone.utils.accessor.IPalettedContainer.IData; +import net.minecraft.util.BitStorage; +import net.minecraft.world.level.chunk.Palette; +import net.minecraft.world.level.chunk.PalettedContainer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +@Mixin(PalettedContainer.class) +public abstract class MixinPalettedContainer implements IPalettedContainer { + + private static final MethodHandle DATA_GETTER; + + // Mixin has no way of referring to the data field and we can't use inheritance + // tricks to determine its name, so we use this ugly workaround instead. + // Classloading is hell here and causes accessor mixins (@Mixin interfaces with + // only @Accessor and @Invoker methods) to break on use and proguard hates method + // handles and on top of that mojang decided that error messages during world + // load are not needed so if you want to debug this you'll probably need an extra + // mixin just to display the error and hard quit the game before follow up errors + // blow up your log file. + // Mumphrey, please add the shadow classes you promised 5 years ago. + static { + Field dataField = null; + for (Field field : PalettedContainer.class.getDeclaredFields()) { + Class fieldType = field.getType(); + if (IData.class.isAssignableFrom(fieldType)) { + if ((field.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) != 0 || field.isSynthetic()) { + continue; + } + if (dataField != null) { + throw new IllegalStateException("PalettedContainer has more than one Data field."); + } + dataField = field; + } + } + if (dataField == null) { + throw new IllegalStateException("PalettedContainer has no Data field."); + } + MethodHandle rawGetter; + try { + rawGetter = MethodHandles.lookup().unreflectGetter(dataField); + } catch (IllegalAccessException impossible) { + // we literally are the owning class, wtf? + throw new IllegalStateException("PalettedContainer may not access its own field?!", impossible); + } + MethodType getterType = MethodType.methodType(IData.class, PalettedContainer.class); + DATA_GETTER = MethodHandles.explicitCastArguments(rawGetter, getterType); + } + + @Override + public Palette getPalette() { + return data().getPalette(); + } + + @Override + public BitStorage getStorage() { + return data().getStorage(); + } + + @Unique + private IData data() { + try { + // cast to Object first so the method handle doesn't hide the interface usage from proguard + return (IData) (Object) DATA_GETTER.invoke((PalettedContainer) (Object) this); + } catch (Throwable t) { + throw sneaky(t, RuntimeException.class); + } + } + + @Unique + private static T sneaky(Throwable t, Class as) throws T { + throw (T) t; + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinScreen.java b/src/launch/java/baritone/launch/mixins/MixinScreen.java index f53c7e602..c99c7776c 100644 --- a/src/launch/java/baritone/launch/mixins/MixinScreen.java +++ b/src/launch/java/baritone/launch/mixins/MixinScreen.java @@ -21,17 +21,17 @@ import baritone.api.IBaritone; import baritone.api.event.events.ChatEvent; import baritone.utils.accessor.IGuiScreen; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Style; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; + +import java.net.URI; +import net.minecraft.client.gui.screens.Screen; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.net.URI; - @Mixin(Screen.class) public abstract class MixinScreen implements IGuiScreen { @@ -43,7 +43,6 @@ public abstract class MixinScreen implements IGuiScreen { //TODO: switch to enum extention with mixin 9.0 or whenever Mumfrey gets around to it @Inject(at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;)V", remap = false, ordinal = 1), method = "handleComponentClicked", cancellable = true) public void handleCustomClickEvent(Style style, CallbackInfoReturnable cir) { - System.out.println("handleCustomClickEvent"); ClickEvent clickEvent = style.getClickEvent(); if (clickEvent == null) { return; diff --git a/src/launch/resources/mixins.baritone-meteor.json b/src/launch/resources/mixins.baritone-meteor.json index 02f905891..f686c92e3 100644 --- a/src/launch/resources/mixins.baritone-meteor.json +++ b/src/launch/resources/mixins.baritone-meteor.json @@ -15,15 +15,17 @@ "MixinCommandSuggestionHelper", "MixinEntity", "MixinEntityRenderManager", + "MixinFireworkRocketEntity", "MixinItemStack", "MixinLivingEntity", - "MixinLongArrayNBT", "MixinLootContext", "MixinMinecraft", "MixinNetworkManager", + "MixinPalettedContainer", + "MixinPalettedContainer$Data", "MixinPlayerController", "MixinScreen", "MixinWorldRenderer" ], "plugin": "baritone.launch.FabricMixinPlugin" -} \ No newline at end of file +} diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index b81e17b38..c1176a3d4 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -20,8 +20,10 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.Settings; +import baritone.api.behavior.IBehavior; import baritone.api.event.listener.IEventBus; -import baritone.api.utils.Helper; +import baritone.api.process.IBaritoneProcess; +import baritone.api.process.IElytraProcess; import baritone.api.utils.IPlayerContext; import baritone.behavior.*; import baritone.cache.WorldProvider; @@ -33,16 +35,19 @@ import baritone.utils.GuiClick; import baritone.utils.InputOverrideHandler; import baritone.utils.PathingControlManager; -import baritone.utils.player.PrimaryPlayerContext; +import baritone.utils.player.BaritonePlayerContext; import net.minecraft.client.Minecraft; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.function.Function; /** * @author Brady @@ -50,88 +55,103 @@ */ public class Baritone implements IBaritone { - private static ThreadPoolExecutor threadPool; - private static File dir; + private static final ThreadPoolExecutor threadPool; static { threadPool = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); - - dir = new File(Minecraft.getInstance().gameDirectory, "baritone"); - if (!Files.exists(dir.toPath())) { - try { - Files.createDirectories(dir.toPath()); - } catch (IOException ignored) { - } - } } - private GameEventHandler gameEventHandler; + private final Minecraft mc; + private final Path directory; + + private final GameEventHandler gameEventHandler; - private PathingBehavior pathingBehavior; - private LookBehavior lookBehavior; - private InventoryBehavior inventoryBehavior; - private WaypointBehavior waypointBehavior; - private InputOverrideHandler inputOverrideHandler; + private final PathingBehavior pathingBehavior; + private final LookBehavior lookBehavior; + private final InventoryBehavior inventoryBehavior; + private final InputOverrideHandler inputOverrideHandler; - private FollowProcess followProcess; - private MineProcess mineProcess; - private GetToBlockProcess getToBlockProcess; - private CustomGoalProcess customGoalProcess; - private BuilderProcess builderProcess; - private ExploreProcess exploreProcess; - private BackfillProcess backfillProcess; - private FarmProcess farmProcess; + private final FollowProcess followProcess; + private final MineProcess mineProcess; + private final GetToBlockProcess getToBlockProcess; + private final CustomGoalProcess customGoalProcess; + private final BuilderProcess builderProcess; + private final ExploreProcess exploreProcess; + private final FarmProcess farmProcess; + private final InventoryPauserProcess inventoryPauserProcess; + private final ElytraProcess elytraProcess; - private PathingControlManager pathingControlManager; - private SelectionManager selectionManager; - private CommandManager commandManager; + private final PathingControlManager pathingControlManager; + private final SelectionManager selectionManager; + private final CommandManager commandManager; - private IPlayerContext playerContext; - private WorldProvider worldProvider; + private final IPlayerContext playerContext; + private final WorldProvider worldProvider; public BlockStateInterface bsi; - Baritone() { + Baritone(Minecraft mc) { + this.mc = mc; this.gameEventHandler = new GameEventHandler(this); + this.directory = mc.gameDirectory.toPath().resolve("baritone"); + if (!Files.exists(this.directory)) { + try { + Files.createDirectories(this.directory); + } catch (IOException ignored) {} + } + // Define this before behaviors try and get it, or else it will be null and the builds will fail! - this.playerContext = PrimaryPlayerContext.INSTANCE; + this.playerContext = new BaritonePlayerContext(this, mc); { - // the Behavior constructor calls baritone.registerBehavior(this) so this populates the behaviors arraylist - pathingBehavior = new PathingBehavior(this); - lookBehavior = new LookBehavior(this); - inventoryBehavior = new InventoryBehavior(this); - inputOverrideHandler = new InputOverrideHandler(this); - waypointBehavior = new WaypointBehavior(this); + this.lookBehavior = this.registerBehavior(LookBehavior::new); + this.pathingBehavior = this.registerBehavior(PathingBehavior::new); + this.inventoryBehavior = this.registerBehavior(InventoryBehavior::new); + this.inputOverrideHandler = this.registerBehavior(InputOverrideHandler::new); + this.registerBehavior(WaypointBehavior::new); } this.pathingControlManager = new PathingControlManager(this); { - this.pathingControlManager.registerProcess(followProcess = new FollowProcess(this)); - this.pathingControlManager.registerProcess(mineProcess = new MineProcess(this)); - this.pathingControlManager.registerProcess(customGoalProcess = new CustomGoalProcess(this)); // very high iq - this.pathingControlManager.registerProcess(getToBlockProcess = new GetToBlockProcess(this)); - this.pathingControlManager.registerProcess(builderProcess = new BuilderProcess(this)); - this.pathingControlManager.registerProcess(exploreProcess = new ExploreProcess(this)); - this.pathingControlManager.registerProcess(backfillProcess = new BackfillProcess(this)); - this.pathingControlManager.registerProcess(farmProcess = new FarmProcess(this)); + this.followProcess = this.registerProcess(FollowProcess::new); + this.mineProcess = this.registerProcess(MineProcess::new); + this.customGoalProcess = this.registerProcess(CustomGoalProcess::new); // very high iq + this.getToBlockProcess = this.registerProcess(GetToBlockProcess::new); + this.builderProcess = this.registerProcess(BuilderProcess::new); + this.exploreProcess = this.registerProcess(ExploreProcess::new); + this.farmProcess = this.registerProcess(FarmProcess::new); + this.inventoryPauserProcess = this.registerProcess(InventoryPauserProcess::new); + this.elytraProcess = this.registerProcess(ElytraProcess::create); + this.registerProcess(BackfillProcess::new); } - this.worldProvider = new WorldProvider(); + this.worldProvider = new WorldProvider(this); this.selectionManager = new SelectionManager(this); this.commandManager = new CommandManager(this); } + public void registerBehavior(IBehavior behavior) { + this.gameEventHandler.registerEventListener(behavior); + } + + public T registerBehavior(Function constructor) { + final T behavior = constructor.apply(this); + this.registerBehavior(behavior); + return behavior; + } + + public T registerProcess(Function constructor) { + final T behavior = constructor.apply(this); + this.pathingControlManager.registerProcess(behavior); + return behavior; + } + @Override public PathingControlManager getPathingControlManager() { return this.pathingControlManager; } - public void registerBehavior(Behavior behavior) { - this.gameEventHandler.registerEventListener(behavior); - } - @Override public InputOverrideHandler getInputOverrideHandler() { return this.inputOverrideHandler; @@ -171,6 +191,7 @@ public LookBehavior getLookBehavior() { return this.lookBehavior; } + @Override public ExploreProcess getExploreProcess() { return this.exploreProcess; } @@ -180,10 +201,15 @@ public MineProcess getMineProcess() { return this.mineProcess; } + @Override public FarmProcess getFarmProcess() { return this.farmProcess; } + public InventoryPauserProcess getInventoryPauserProcess() { + return this.inventoryPauserProcess; + } + @Override public PathingBehavior getPathingBehavior() { return this.pathingBehavior; @@ -209,23 +235,27 @@ public CommandManager getCommandManager() { return this.commandManager; } + @Override + public IElytraProcess getElytraProcess() { + return this.elytraProcess; + } + @Override public void openClick() { new Thread(() -> { try { Thread.sleep(100); - Helper.mc.execute(() -> Helper.mc.setScreen(new GuiClick())); - } catch (Exception ignored) { - } + mc.execute(() -> mc.setScreen(new GuiClick())); + } catch (Exception ignored) {} }).start(); } - public static Settings settings() { - return BaritoneAPI.getSettings(); + public Path getDirectory() { + return this.directory; } - public static File getDir() { - return dir; + public static Settings settings() { + return BaritoneAPI.getSettings(); } public static Executor getExecutor() { diff --git a/src/main/java/baritone/BaritoneProvider.java b/src/main/java/baritone/BaritoneProvider.java index d5457cf85..f34d9bcb9 100644 --- a/src/main/java/baritone/BaritoneProvider.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -22,13 +22,15 @@ import baritone.api.cache.IWorldScanner; import baritone.api.command.ICommandSystem; import baritone.api.schematic.ISchematicSystem; -import baritone.cache.WorldScanner; +import baritone.cache.FasterWorldScanner; import baritone.command.CommandSystem; import baritone.command.ExampleBaritoneControl; import baritone.utils.schematic.SchematicSystem; +import net.minecraft.client.Minecraft; import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author Brady @@ -36,30 +38,45 @@ */ public final class BaritoneProvider implements IBaritoneProvider { - private final Baritone primary; private final List all; + private final List allView; - { - this.primary = new Baritone(); - this.all = Collections.singletonList(this.primary); + public BaritoneProvider() { + this.all = new CopyOnWriteArrayList<>(); + this.allView = Collections.unmodifiableList(this.all); // Setup chat control, just for the primary instance - new ExampleBaritoneControl(this.primary); + final Baritone primary = (Baritone) this.createBaritone(Minecraft.getInstance()); + primary.registerBehavior(ExampleBaritoneControl::new); } @Override public IBaritone getPrimaryBaritone() { - return primary; + return this.all.get(0); } @Override public List getAllBaritones() { - return all; + return this.allView; + } + + @Override + public synchronized IBaritone createBaritone(Minecraft minecraft) { + IBaritone baritone = this.getBaritoneForMinecraft(minecraft); + if (baritone == null) { + this.all.add(baritone = new Baritone(minecraft)); + } + return baritone; + } + + @Override + public synchronized boolean destroyBaritone(IBaritone baritone) { + return baritone != this.getPrimaryBaritone() && this.all.remove(baritone); } @Override public IWorldScanner getWorldScanner() { - return WorldScanner.INSTANCE; + return FasterWorldScanner.INSTANCE; } @Override diff --git a/src/main/java/baritone/KeepName.java b/src/main/java/baritone/KeepName.java index 8fe29cd71..7fc04cd68 100644 --- a/src/main/java/baritone/KeepName.java +++ b/src/main/java/baritone/KeepName.java @@ -18,5 +18,4 @@ package baritone; // Annotation for classes and class members that should not be renamed by proguard -public @interface KeepName { -} +public @interface KeepName {} diff --git a/src/main/java/baritone/altoclef/AltoClefSettings.java b/src/main/java/baritone/altoclef/AltoClefSettings.java index eb75a913e..cfd1e0c28 100644 --- a/src/main/java/baritone/altoclef/AltoClefSettings.java +++ b/src/main/java/baritone/altoclef/AltoClefSettings.java @@ -33,48 +33,39 @@ public class AltoClefSettings { // woo singletons private static AltoClefSettings _instance = new AltoClefSettings(); - - public static AltoClefSettings getInstance() { - return _instance; - } - private final Object breakMutex = new Object(); private final Object placeMutex = new Object(); - private final Object propertiesMutex = new Object(); - private final Object globalHeuristicMutex = new Object(); - private final HashSet _blocksToAvoidBreaking = new HashSet<>(); private final List> _breakAvoiders = new ArrayList<>(); - private final List> _placeAvoiders = new ArrayList<>(); - private final List> _forceCanWalkOn = new ArrayList<>(); - private final List> _forceAvoidWalkThrough = new ArrayList<>(); - private final List> _forceUseTool = new ArrayList<>(); - private final List> _globalHeuristics = new ArrayList<>(); - private final HashSet _protectedItems = new HashSet<>(); - private boolean _allowFlowingWaterPass; - private boolean _pauseInteractions; - private boolean _dontPlaceBucketButStillFall; - private boolean _allowSwimThroughLava = false; - private boolean _treatSoulSandAsOrdinaryBlock = false; + private boolean canWalkOnEndPortal = false; + + public static AltoClefSettings getInstance() { + return _instance; + } + + public void canWalkOnEndPortal(boolean canWalk) { + canWalkOnEndPortal = canWalk; + } public void avoidBlockBreak(BlockPos pos) { synchronized (breakMutex) { _blocksToAvoidBreaking.add(pos); } } + public void avoidBlockBreak(Predicate avoider) { synchronized (breakMutex) { _breakAvoiders.add(avoider); @@ -102,6 +93,7 @@ public void avoidBlockPlace(Predicate avoider) { public boolean shouldAvoidBreaking(int x, int y, int z) { return shouldAvoidBreaking(new BlockPos(x, y, z)); } + public boolean shouldAvoidBreaking(BlockPos pos) { synchronized (breakMutex) { if (_blocksToAvoidBreaking.contains(pos)) @@ -109,11 +101,13 @@ public boolean shouldAvoidBreaking(BlockPos pos) { return (_breakAvoiders.stream().anyMatch(pred -> pred.test(pos))); } } + public boolean shouldAvoidPlacingAt(BlockPos pos) { synchronized (placeMutex) { return _placeAvoiders.stream().anyMatch(pred -> pred.test(pos)); } } + public boolean shouldAvoidPlacingAt(int x, int y, int z) { return shouldAvoidPlacingAt(new BlockPos(x, y, z)); } @@ -129,6 +123,7 @@ public boolean shouldAvoidWalkThroughForce(BlockPos pos) { return _forceAvoidWalkThrough.stream().anyMatch(pred -> pred.test(pos)); } } + public boolean shouldAvoidWalkThroughForce(int x, int y, int z) { return shouldAvoidWalkThroughForce(new BlockPos(x, y, z)); } @@ -156,21 +151,22 @@ public boolean isInteractionPaused() { return _pauseInteractions; } } - public boolean isFlowingWaterPassAllowed() { + + public void setInteractionPaused(boolean paused) { synchronized (propertiesMutex) { - return _allowFlowingWaterPass; + _pauseInteractions = paused; } } - public boolean canSwimThroughLava() { + public boolean isFlowingWaterPassAllowed() { synchronized (propertiesMutex) { - return _allowSwimThroughLava; + return _allowFlowingWaterPass; } } - public void setInteractionPaused(boolean paused) { + public boolean canSwimThroughLava() { synchronized (propertiesMutex) { - _pauseInteractions = paused; + return _allowSwimThroughLava; } } @@ -202,15 +198,19 @@ public double applyGlobalHeuristic(double prev, int x, int y, int z) { public HashSet getBlocksToAvoidBreaking() { return _blocksToAvoidBreaking; } + public List> getBreakAvoiders() { return _breakAvoiders; } + public List> getPlaceAvoiders() { return _placeAvoiders; } + public List> getForceWalkOnPredicates() { return _forceCanWalkOn; } + public List> getForceAvoidWalkThroughPredicates() { return _forceAvoidWalkThrough; } @@ -226,9 +226,11 @@ public List> getGlobalHeuristics() { public boolean isItemProtected(Item item) { return _protectedItems.contains(item); } + public HashSet getProtectedItems() { return _protectedItems; } + public void protectItem(Item item) { _protectedItems.add(item); } @@ -252,4 +254,8 @@ public Object getPropertiesMutex() { public Object getGlobalHeuristicMutex() { return globalHeuristicMutex; } + + public boolean isCanWalkOnEndPortal() { + return canWalkOnEndPortal; + } } diff --git a/src/main/java/baritone/behavior/Behavior.java b/src/main/java/baritone/behavior/Behavior.java index 36273c026..848beb298 100644 --- a/src/main/java/baritone/behavior/Behavior.java +++ b/src/main/java/baritone/behavior/Behavior.java @@ -35,6 +35,5 @@ public class Behavior implements IBehavior { protected Behavior(Baritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); - baritone.registerBehavior(this); } } diff --git a/src/main/java/baritone/behavior/InventoryBehavior.java b/src/main/java/baritone/behavior/InventoryBehavior.java index bd61150dd..f8a7ac491 100644 --- a/src/main/java/baritone/behavior/InventoryBehavior.java +++ b/src/main/java/baritone/behavior/InventoryBehavior.java @@ -20,6 +20,7 @@ import baritone.Baritone; import baritone.altoclef.AltoClefSettings; import baritone.api.event.events.TickEvent; +import baritone.api.utils.Helper; import baritone.utils.ToolSet; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.Direction; @@ -40,7 +41,10 @@ import java.util.Random; import java.util.function.Predicate; -public final class InventoryBehavior extends Behavior { +public final class InventoryBehavior extends Behavior implements Helper { + + int ticksSinceLastInventoryMove; + int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt public InventoryBehavior(Baritone baritone) { super(baritone); @@ -58,21 +62,29 @@ public void onTick(TickEvent event) { // we have a crafting table or a chest or something open return; } + ticksSinceLastInventoryMove++; if (firstValidThrowaway() >= 9) { // aka there are none on the hotbar, but there are some in main inventory - swapWithHotBar(firstValidThrowaway(), 8); + requestSwapWithHotBar(firstValidThrowaway(), 8); } int pick = bestToolAgainst(Blocks.STONE, PickaxeItem.class); if (pick >= 9) { - swapWithHotBar(pick, 0); + requestSwapWithHotBar(pick, 0); + } + if (lastTickRequestedMove != null) { + logDebug("Remembering to move " + lastTickRequestedMove[0] + " " + lastTickRequestedMove[1] + " from a previous tick"); + requestSwapWithHotBar(lastTickRequestedMove[0], lastTickRequestedMove[1]); } } - public void attemptToPutOnHotbar(int inMainInvy, Predicate disallowedHotbar) { - if (AltoClefSettings.getInstance().isInteractionPaused()) return; + public boolean attemptToPutOnHotbar(int inMainInvy, Predicate disallowedHotbar) { + if (AltoClefSettings.getInstance().isInteractionPaused()) return false; OptionalInt destination = getTempHotbarSlot(disallowedHotbar); if (destination.isPresent()) { - swapWithHotBar(inMainInvy, destination.getAsInt()); + if (!requestSwapWithHotBar(inMainInvy, destination.getAsInt())) { + return false; + } } + return true; } public OptionalInt getTempHotbarSlot(Predicate disallowedHotbar) { @@ -96,9 +108,21 @@ public OptionalInt getTempHotbarSlot(Predicate disallowedHotbar) { return OptionalInt.of(candidates.get(new Random().nextInt(candidates.size()))); } - private void swapWithHotBar(int inInventory, int inHotbar) { - if (AltoClefSettings.getInstance().isInteractionPaused()) return; + private boolean requestSwapWithHotBar(int inInventory, int inHotbar) { + lastTickRequestedMove = new int[]{inInventory, inHotbar}; + if (ticksSinceLastInventoryMove < Baritone.settings().ticksBetweenInventoryMoves.value) { + logDebug("Inventory move requested but delaying " + ticksSinceLastInventoryMove + " " + Baritone.settings().ticksBetweenInventoryMoves.value); + return false; + } + if (Baritone.settings().inventoryMoveOnlyIfStationary.value && !baritone.getInventoryPauserProcess().stationaryForInventoryMove()) { + logDebug("Inventory move requested but delaying until stationary"); + return false; + } + if (AltoClefSettings.getInstance().isInteractionPaused()) return false; ctx.playerController().windowClick(ctx.player().inventoryMenu.containerId, inInventory < 9 ? inInventory + 36 : inInventory, inHotbar, ClickType.SWAP, ctx.player()); + ticksSinceLastInventoryMove = 0; + lastTickRequestedMove = null; + return true; } private int firstValidThrowaway() { // TODO offhand idk @@ -151,8 +175,7 @@ public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) { if (AltoClefSettings.getInstance().isInteractionPaused()) return false; if (AltoClefSettings.getInstance().shouldAvoidPlacingAt(x, y, z)) return false; BlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z)); - if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof BlockItem && maybe.equals(((BlockItem) stack.getItem()).getBlock().getStateForPlacement(new BlockPlaceContext(new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) { - }))))) { + if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof BlockItem && maybe.equals(((BlockItem) stack.getItem()).getBlock().getStateForPlacement(new BlockPlaceContext(new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) {}))))) { return true; // gotem } if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof BlockItem && ((BlockItem) stack.getItem()).getBlock().equals(maybe.getBlock()))) { @@ -172,8 +195,7 @@ public boolean throwaway(boolean select, Predicate desired) { } public boolean throwaway(boolean select, Predicate desired, boolean allowInventory) { - if (AltoClefSettings.getInstance().isInteractionPaused()) - return false; + if (AltoClefSettings.getInstance().isInteractionPaused()) return false; LocalPlayer p = ctx.player(); NonNullList inv = p.getInventory().items; for (int i = 0; i < 9; i++) { @@ -210,8 +232,8 @@ public boolean throwaway(boolean select, Predicate desired, b if (allowInventory) { for (int i = 9; i < 36; i++) { if (desired.test(inv.get(i))) { - swapWithHotBar(i, 7); if (select) { + requestSwapWithHotBar(i, 7); p.getInventory().selected = 7; } return true; diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 13b8ebdd4..98e12aeff 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -20,80 +20,112 @@ import baritone.Baritone; import baritone.api.Settings; import baritone.api.behavior.ILookBehavior; -import baritone.api.event.events.PlayerUpdateEvent; -import baritone.api.event.events.RotationMoveEvent; +import baritone.api.behavior.look.IAimProcessor; +import baritone.api.behavior.look.ITickableAimProcessor; +import baritone.api.event.events.*; +import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; +import baritone.behavior.look.ForkableRandom; +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Optional; public final class LookBehavior extends Behavior implements ILookBehavior { /** - * Target's values are as follows: + * The current look target, may be {@code null}. */ - private Rotation target; + private Target target; /** - * Whether or not rotations are currently being forced + * The rotation known to the server. Returned by {@link #getEffectiveRotation()} for use in {@link IPlayerContext}. */ - private boolean force; + private Rotation serverRotation; /** - * The last player yaw angle. Used when free looking + * The last player rotation. Used to restore the player's angle when using free look. * * @see Settings#freeLook */ - private float lastYaw; + private Rotation prevRotation; + + private final AimProcessor processor; + + private final Deque smoothYawBuffer; + private final Deque smoothPitchBuffer; public LookBehavior(Baritone baritone) { super(baritone); + this.processor = new AimProcessor(baritone.getPlayerContext()); + this.smoothYawBuffer = new ArrayDeque<>(); + this.smoothPitchBuffer = new ArrayDeque<>(); } @Override - public void updateTarget(Rotation target, boolean force) { - this.target = target; - if (!force) { - double rand = Math.random() - 0.5; - if (Math.abs(rand) < 0.1) { - rand *= 4; - } - this.target = new Rotation(this.target.getYaw() + (float) (rand * Baritone.settings().randomLooking113.value), this.target.getPitch()); + public void updateTarget(Rotation rotation, boolean blockInteract) { + this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract)); + } + + @Override + public IAimProcessor getAimProcessor() { + return this.processor; + } + + @Override + public void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.IN) { + this.processor.tick(); } - this.force = force || !Baritone.settings().freeLook.value; } @Override public void onPlayerUpdate(PlayerUpdateEvent event) { + if (this.target == null) { return; } - // Whether or not we're going to silently set our angles - boolean silent = Baritone.settings().antiCheatCompatibility.value && !this.force; - switch (event.getState()) { case PRE: { - if (this.force) { - ctx.player().setYRot(this.target.getYaw()); - float oldPitch = ctx.player().getXRot(); - float desiredPitch = this.target.getPitch(); - ctx.player().setXRot(desiredPitch); - ctx.player().setYRot((float) (ctx.player().getYRot() + (Math.random() - 0.5) * Baritone.settings().randomLooking.value)); - ctx.player().setXRot((float) (ctx.player().getXRot() + (Math.random() - 0.5) * Baritone.settings().randomLooking.value)); - if (desiredPitch == oldPitch && !Baritone.settings().freeLook.value) { - nudgeToLevel(); - } - this.target = null; - } - if (silent) { - this.lastYaw = ctx.player().getYRot(); - ctx.player().setYRot(this.target.getYaw()); + if (this.target.mode == Target.Mode.NONE) { + // Just return for PRE, we still want to set target to null on POST + return; } + + this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + ctx.player().setYRot(actual.getYaw()); + ctx.player().setXRot(actual.getPitch()); break; } case POST: { - if (silent) { - ctx.player().setYRot(this.lastYaw); - this.target = null; + // Reset the player's rotations back to their original values + if (this.prevRotation != null) { + this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); + while (this.smoothYawBuffer.size() > Baritone.settings().smoothLookTicks.value) { + this.smoothYawBuffer.removeFirst(); + } + this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); + while (this.smoothPitchBuffer.size() > Baritone.settings().smoothLookTicks.value) { + this.smoothPitchBuffer.removeFirst(); + } + if (this.target.mode == Target.Mode.SERVER) { + ctx.player().setYRot(this.prevRotation.getYaw()); + ctx.player().setXRot(this.prevRotation.getPitch()); + } else if (ctx.player().isFallFlying() ? Baritone.settings().elytraSmoothLook.value : Baritone.settings().smoothLook.value) { + ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); + if (ctx.player().isFallFlying()) { + ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); + } + } + //ctx.player().xRotO = prevRotation.getPitch(); + //ctx.player().yRotO = prevRotation.getYaw(); + this.prevRotation = null; } + // The target is done being used for this game tick, so it can be invalidated + this.target = null; break; } default: @@ -101,34 +133,227 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { } } + @Override + public void onSendPacket(PacketEvent event) { + if (!(event.getPacket() instanceof ServerboundMovePlayerPacket)) { + return; + } + + final ServerboundMovePlayerPacket packet = (ServerboundMovePlayerPacket) event.getPacket(); + if (packet instanceof ServerboundMovePlayerPacket.Rot || packet instanceof ServerboundMovePlayerPacket.PosRot) { + this.serverRotation = new Rotation(packet.getYRot(0.0f), packet.getXRot(0.0f)); + } + } + + @Override + public void onWorldEvent(WorldEvent event) { + this.serverRotation = null; + this.target = null; + } + public void pig() { if (this.target != null) { - ctx.player().setYRot(this.target.getYaw()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + ctx.player().setYRot(actual.getYaw()); } } + public Optional getEffectiveRotation() { + if (Baritone.settings().freeLook.value) { + return Optional.ofNullable(this.serverRotation); + } + // If freeLook isn't on, just defer to the player's actual rotations + return Optional.empty(); + } + @Override public void onPlayerRotationMove(RotationMoveEvent event) { if (this.target != null) { + final Rotation actual = this.processor.peekRotation(this.target.rotation); + event.setYaw(actual.getYaw()); + event.setPitch(actual.getPitch()); + } + } - event.setYaw(this.target.getYaw()); + private static final class AimProcessor extends AbstractAimProcessor { - // 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.value && event.getType() == RotationMoveEvent.Type.MOTION_UPDATE && !this.force) { - this.target = null; + public AimProcessor(final IPlayerContext ctx) { + super(ctx); + } + + @Override + protected Rotation getPrevRotation() { + // Implementation will use LookBehavior.serverRotation + return ctx.playerRotations(); + } + } + + private static abstract class AbstractAimProcessor implements ITickableAimProcessor { + + protected final IPlayerContext ctx; + private final ForkableRandom rand; + private double randomYawOffset; + private double randomPitchOffset; + + public AbstractAimProcessor(IPlayerContext ctx) { + this.ctx = ctx; + this.rand = new ForkableRandom(); + } + + private AbstractAimProcessor(final AbstractAimProcessor source) { + this.ctx = source.ctx; + this.rand = source.rand.fork(); + this.randomYawOffset = source.randomYawOffset; + this.randomPitchOffset = source.randomPitchOffset; + } + + @Override + public final Rotation peekRotation(final Rotation rotation) { + final Rotation prev = this.getPrevRotation(); + + float desiredYaw = rotation.getYaw(); + float desiredPitch = rotation.getPitch(); + + // In other words, the target doesn't care about the pitch, so it used playerRotations().getPitch() + // and it's safe to adjust it to a normal level + if (desiredPitch == prev.getPitch()) { + desiredPitch = nudgeToLevel(desiredPitch); + } + + desiredYaw += this.randomYawOffset; + desiredPitch += this.randomPitchOffset; + + return new Rotation( + this.calculateMouseMove(prev.getYaw(), desiredYaw), + this.calculateMouseMove(prev.getPitch(), desiredPitch) + ).clamp(); + } + + @Override + public final void tick() { + // randomLooking + this.randomYawOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; + this.randomPitchOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; + + // randomLooking113 + double random = this.rand.nextDouble() - 0.5; + if (Math.abs(random) < 0.1) { + random *= 4; + } + this.randomYawOffset += random * Baritone.settings().randomLooking113.value; + } + + @Override + public final void advance(int ticks) { + for (int i = 0; i < ticks; i++) { + this.tick(); } } + + @Override + public Rotation nextRotation(final Rotation rotation) { + final Rotation actual = this.peekRotation(rotation); + this.tick(); + return actual; + } + + @Override + public final ITickableAimProcessor fork() { + return new AbstractAimProcessor(this) { + + private Rotation prev = AbstractAimProcessor.this.getPrevRotation(); + + @Override + public Rotation nextRotation(final Rotation rotation) { + return (this.prev = super.nextRotation(rotation)); + } + + @Override + protected Rotation getPrevRotation() { + return this.prev; + } + }; + } + + protected abstract Rotation getPrevRotation(); + + /** + * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1}) + */ + private float nudgeToLevel(float pitch) { + if (pitch < -20) { + return pitch + 1; + } else if (pitch > 10) { + return pitch - 1; + } + return pitch; + } + + private float calculateMouseMove(float current, float target) { + final float delta = target - current; + final double deltaPx = angleToMouse(delta); // yes, even the mouse movements use double + return current + mouseToAngle(deltaPx); + } + + private double angleToMouse(float angleDelta) { + final float minAngleChange = mouseToAngle(1); + return Math.round(angleDelta / minAngleChange); + } + + private float mouseToAngle(double mouseDelta) { + // casting float literals to double gets us the precise values used by mc + final double f = ctx.minecraft().options.sensitivity().get() * (double) 0.6f + (double) 0.2f; + return (float) (mouseDelta * f * f * f * 8.0d) * 0.15f; // yes, one double and one float scaling factor + } } - /** - * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1}) - */ - private void nudgeToLevel() { - if (ctx.player().getXRot() < -20) { - ctx.player().setXRot(ctx.player().getXRot() + 1); - } else if (ctx.player().getXRot() > 10) { - ctx.player().setXRot(ctx.player().getXRot() - 1); + private static class Target { + + public final Rotation rotation; + public final Mode mode; + + public Target(Rotation rotation, Mode mode) { + this.rotation = rotation; + this.mode = mode; + } + + enum Mode { + /** + * Rotation will be set client-side and is visual to the player + */ + CLIENT, + + /** + * Rotation will be set server-side and is silent to the player + */ + SERVER, + + /** + * Rotation will remain unaffected on both the client and server + */ + NONE; + + static Mode resolve(IPlayerContext ctx, boolean blockInteract) { + final Settings settings = Baritone.settings(); + final boolean antiCheat = settings.antiCheatCompatibility.value; + final boolean blockFreeLook = settings.blockFreeLook.value; + + if (ctx.player().isFallFlying()) { + // always need to set angles while flying + return settings.elytraFreeLook.value ? SERVER : CLIENT; + } else if (settings.freeLook.value) { + // Regardless of if antiCheatCompatibility is enabled, if a blockInteract is requested then the player + // rotation needs to be set somehow, otherwise Baritone will halt since objectMouseOver() will just be + // whatever the player is mousing over visually. Let's just settle for setting it silently. + if (blockInteract) { + return blockFreeLook ? SERVER : CLIENT; + } + return antiCheat ? SERVER : NONE; + } + + // all freeLook settings are disabled so set the angles + return CLIENT; + } } } } diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index f18bd862b..b7c035598 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -33,16 +33,16 @@ import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.pathing.path.PathExecutor; +import baritone.process.ElytraProcess; import baritone.utils.PathRenderer; import baritone.utils.PathingCommandContext; import baritone.utils.pathing.Favoring; -import net.minecraft.core.BlockPos; - import java.util.ArrayList; import java.util.Comparator; import java.util.Objects; import java.util.Optional; import java.util.concurrent.LinkedBlockingQueue; +import net.minecraft.core.BlockPos; public final class PathingBehavior extends Behavior implements IPathingBehavior, Helper { @@ -99,6 +99,7 @@ public void onTick(TickEvent event) { baritone.getPathingControlManager().cancelEverything(); return; } + expectedSegmentStart = pathStart(); baritone.getPathingControlManager().preTick(); tickPath(); @@ -238,11 +239,11 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { if (current != null) { switch (event.getState()) { case PRE: - lastAutoJump = mc.options.autoJump().get(); - mc.options.autoJump().set(false); + lastAutoJump = ctx.minecraft().options.autoJump().get(); + ctx.minecraft().options.autoJump().set(false); break; case POST: - mc.options.autoJump().set(lastAutoJump); + ctx.minecraft().options.autoJump().set(lastAutoJump); break; default: break; @@ -308,7 +309,10 @@ public Optional getInProgress() { } public boolean isSafeToCancel() { - return current == null || safeToCancel; + if (current == null) { + return !baritone.getElytraProcess().isActive() || baritone.getElytraProcess().isSafeToCancel(); + } + return safeToCancel; } public void requestPause() { @@ -351,7 +355,7 @@ public void softCancelIfSafe() { } // just cancel the current path - private void secretInternalSegmentCancel() { + public void secretInternalSegmentCancel() { queuePathEvent(PathEvent.CANCELED); synchronized (pathPlanLock) { getInProgress().ifPresent(AbstractNodeCostSearch::cancel); @@ -419,7 +423,7 @@ private void resetEstimatedTicksToGoal(BetterBlockPos start) { public BetterBlockPos pathStart() { // TODO move to a helper or util class BetterBlockPos feet = ctx.playerFeet(); if (!MovementHelper.canWalkOn(ctx, feet.below())) { - if (ctx.player().isOnGround()) { + if (ctx.player().onGround()) { double playerX = ctx.player().position().x; double playerZ = ctx.player().position().z; ArrayList closest = new ArrayList<>(); diff --git a/src/main/java/baritone/behavior/look/ForkableRandom.java b/src/main/java/baritone/behavior/look/ForkableRandom.java new file mode 100644 index 000000000..5f5f942d2 --- /dev/null +++ b/src/main/java/baritone/behavior/look/ForkableRandom.java @@ -0,0 +1,85 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.behavior.look; + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.LongSupplier; + +/** + * Implementation of Xoroshiro256++ + *

+ * Extended to produce random double-precision floating point numbers, and allow copies to be spawned via {@link #fork}, + * which share the same internal state as the source object. + * + * @author Brady + */ +public final class ForkableRandom { + + private static final double DOUBLE_UNIT = 0x1.0p-53; + + private final long[] s; + + public ForkableRandom() { + this(System.nanoTime() ^ System.currentTimeMillis()); + } + + public ForkableRandom(long seedIn) { + final AtomicLong seed = new AtomicLong(seedIn); + final LongSupplier splitmix64 = () -> { + long z = seed.addAndGet(0x9e3779b97f4a7c15L); + z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; + z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL; + return z ^ (z >>> 31); + }; + this.s = new long[] { + splitmix64.getAsLong(), + splitmix64.getAsLong(), + splitmix64.getAsLong(), + splitmix64.getAsLong() + }; + } + + private ForkableRandom(long[] s) { + this.s = s; + } + + public double nextDouble() { + return (this.next() >>> 11) * DOUBLE_UNIT; + } + + public long next() { + final long result = rotl(this.s[0] + this.s[3], 23) + this.s[0]; + final long t = this.s[1] << 17; + this.s[2] ^= this.s[0]; + this.s[3] ^= this.s[1]; + this.s[1] ^= this.s[2]; + this.s[0] ^= this.s[3]; + this.s[2] ^= t; + this.s[3] = rotl(this.s[3], 45); + return result; + } + + public ForkableRandom fork() { + return new ForkableRandom(Arrays.copyOf(this.s, 4)); + } + + private static long rotl(long x, int k) { + return (x << k) | (x >>> (64 - k)); + } +} diff --git a/src/main/java/baritone/cache/CachedChunk.java b/src/main/java/baritone/cache/CachedChunk.java index 3109d9293..2659c4feb 100644 --- a/src/main/java/baritone/cache/CachedChunk.java +++ b/src/main/java/baritone/cache/CachedChunk.java @@ -218,7 +218,7 @@ public final BlockState getBlock(int x, int y, int z, DimensionType dimension) { // nether roof is always unbreakable return Blocks.BEDROCK.defaultBlockState(); } - if (y < 5 && dimension.natural()) { + if (y < -59 && dimension.natural()) { // solid blocks below 5 are commonly bedrock // however, returning bedrock always would be a little yikes // discourage paths that include breaking blocks below 5 a little more heavily just so that it takes paths breaking what's known to be stone (at 5 or above) instead of what could maybe be bedrock (below 5) diff --git a/src/main/java/baritone/cache/CachedRegion.java b/src/main/java/baritone/cache/CachedRegion.java index 6c70f1eba..8aa992d79 100644 --- a/src/main/java/baritone/cache/CachedRegion.java +++ b/src/main/java/baritone/cache/CachedRegion.java @@ -167,7 +167,7 @@ public synchronized final void save(String directory) { out.writeShort(entry.getValue().size()); for (BlockPos pos : entry.getValue()) { out.writeByte((byte) (pos.getZ() << 4 | pos.getX())); - out.writeInt(pos.getY() - dimension.minY()); + out.writeInt(pos.getY()-dimension.minY()); } } } @@ -272,7 +272,7 @@ public synchronized void load(String directory) { int X = xz & 0x0f; int Z = (xz >>> 4) & 0x0f; int Y = in.readInt(); - locs.add(new BlockPos(X, Y + dimension.minY(), Z)); + locs.add(new BlockPos(X, Y+dimension.minY(), Z)); } } } diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 2384d1c58..aed1faeb5 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -23,6 +23,7 @@ import baritone.api.cache.ICachedWorld; import baritone.api.cache.IWorldData; import baritone.api.utils.Helper; +import com.google.common.cache.CacheBuilder; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.core.BlockPos; @@ -36,7 +37,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; /** @@ -70,7 +70,7 @@ public final class CachedWorld implements ICachedWorld, Helper { * All chunk positions pending packing. This map will be updated in-place if a new update to the chunk occurs * while waiting in the queue for the packer thread to get to it. */ - private final Map toPackMap = new ConcurrentHashMap<>(); + private final Map toPackMap = CacheBuilder.newBuilder().softValues().build().asMap(); private final DimensionType dimension; @@ -196,9 +196,7 @@ private synchronized void prune() { int distZ = ((region.getZ() << 9) + 256) - pruneCenter.getZ(); double dist = Math.sqrt(distX * distX + distZ * distZ); if (dist > 1024) { - if (!Baritone.settings().censorCoordinates.value) { - logDebug("Deleting cached region " + region.getX() + "," + region.getZ() + " from ram"); - } + logDebug("Deleting cached region from ram"); cachedRegions.remove(getRegionID(region.getX(), region.getZ())); } } @@ -308,6 +306,9 @@ public void run() { try { ChunkPos pos = toPackQueue.take(); LevelChunk chunk = toPackMap.remove(pos); + if (toPackQueue.size() > Baritone.settings().chunkPackerQueueMaxSize.value) { + continue; + } CachedChunk cached = ChunkPacker.pack(chunk); CachedWorld.this.updateCachedChunk(cached); //System.out.println("Processed chunk at " + chunk.x + "," + chunk.z); diff --git a/src/main/java/baritone/cache/ChunkPacker.java b/src/main/java/baritone/cache/ChunkPacker.java index b90b74432..5d619543d 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -21,7 +21,12 @@ import baritone.pathing.movement.MovementHelper; import baritone.utils.pathing.PathingBlockType; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.DoublePlantBlock; +import net.minecraft.world.level.block.FlowerBlock; +import net.minecraft.world.level.block.TallGrassBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; @@ -40,8 +45,7 @@ */ public final class ChunkPacker { - private ChunkPacker() { - } + private ChunkPacker() {} public static CachedChunk pack(LevelChunk chunk) { //long start = System.nanoTime() / 1000000L; @@ -80,7 +84,7 @@ public static CachedChunk pack(LevelChunk chunk) { Block block = state.getBlock(); if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { String name = BlockUtils.blockToString(block); - specialBlocks.computeIfAbsent(name, b -> new ArrayList<>()).add(new BlockPos(x, y + chunk.getMinBuildHeight(), z)); + specialBlocks.computeIfAbsent(name, b -> new ArrayList<>()).add(new BlockPos(x, y+chunk.getMinBuildHeight(), z)); } } } @@ -96,8 +100,7 @@ public static CachedChunk pack(LevelChunk chunk) { // get top block in columns // @formatter:off for (int z = 0; z < 16; z++) { - https: -//www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html + https://www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html for (int x = 0; x < 16; x++) { for (int y = height - 1; y >= 0; y--) { int index = CachedChunk.getPositionIndex(x, y, z); diff --git a/src/main/java/baritone/cache/FasterWorldScanner.java b/src/main/java/baritone/cache/FasterWorldScanner.java new file mode 100644 index 000000000..e364fd7b4 --- /dev/null +++ b/src/main/java/baritone/cache/FasterWorldScanner.java @@ -0,0 +1,303 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.cache; + +import baritone.api.cache.ICachedWorld; +import baritone.api.cache.IWorldScanner; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.BlockOptionalMeta; +import baritone.api.utils.BlockOptionalMetaLookup; +import baritone.api.utils.IPlayerContext; +import baritone.utils.accessor.IPalettedContainer; +import io.netty.buffer.Unpooled; +import net.minecraft.core.BlockPos; +import net.minecraft.core.IdMapper; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.util.BitStorage; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.GlobalPalette; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.Palette; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.SingleValuePalette; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum FasterWorldScanner implements IWorldScanner { + INSTANCE; + + private static final BlockState[] PALETTE_REGISTRY_SENTINEL = new BlockState[0]; + + @Override + public List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) { + assert ctx.world() != null; + if (maxSearchRadius < 0) { + throw new IllegalArgumentException("chunkRange must be >= 0"); + } + return scanChunksInternal(ctx, filter, getChunkRange(ctx.playerFeet().x >> 4, ctx.playerFeet().z >> 4, maxSearchRadius), max); + } + + @Override + public List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold) { + Stream stream = scanChunkInternal(ctx, filter, pos); + if (max >= 0) { + stream = stream.limit(max); + } + return stream.collect(Collectors.toList()); + } + + @Override + public int repack(IPlayerContext ctx) { + return this.repack(ctx, 40); + } + + @Override + public int repack(IPlayerContext ctx, int range) { + ChunkSource chunkProvider = ctx.world().getChunkSource(); + ICachedWorld cachedWorld = ctx.worldData().getCachedWorld(); + + BetterBlockPos playerPos = ctx.playerFeet(); + + int playerChunkX = playerPos.getX() >> 4; + int playerChunkZ = playerPos.getZ() >> 4; + + int minX = playerChunkX - range; + int minZ = playerChunkZ - range; + int maxX = playerChunkX + range; + int maxZ = playerChunkZ + range; + + int queued = 0; + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + LevelChunk chunk = chunkProvider.getChunk(x, z, false); + + if (chunk != null && !chunk.isEmpty()) { + queued++; + cachedWorld.queueForPacking(chunk); + } + } + } + + return queued; + } + + // ordered in a way that the closest blocks are generally first + public static List getChunkRange(int centerX, int centerZ, int chunkRadius) { + List chunks = new ArrayList<>(); + // spiral out + chunks.add(new ChunkPos(centerX, centerZ)); + for (int i = 1; i < chunkRadius; i++) { + for (int j = 0; j <= i; j++) { + chunks.add(new ChunkPos(centerX - j, centerZ - i)); + if (j != 0) { + chunks.add(new ChunkPos(centerX + j, centerZ - i)); + chunks.add(new ChunkPos(centerX - j, centerZ + i)); + } + chunks.add(new ChunkPos(centerX + j, centerZ + i)); + if (j != i) { + chunks.add(new ChunkPos(centerX - i, centerZ - j)); + chunks.add(new ChunkPos(centerX + i, centerZ - j)); + if (j != 0) { + chunks.add(new ChunkPos(centerX - i, centerZ + j)); + chunks.add(new ChunkPos(centerX + i, centerZ + j)); + } + } + } + } + return chunks; + } + + private List scanChunksInternal(IPlayerContext ctx, BlockOptionalMetaLookup lookup, List chunkPositions, int maxBlocks) { + assert ctx.world() != null; + try { + // p -> scanChunkInternal(ctx, lookup, p) + Stream posStream = chunkPositions.parallelStream().flatMap(p -> scanChunkInternal(ctx, lookup, p)); + if (maxBlocks >= 0) { + // WARNING: this can be expensive if maxBlocks is large... + // see limit's javadoc + posStream = posStream.limit(maxBlocks); + } + return posStream.collect(Collectors.toList()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + private Stream scanChunkInternal(IPlayerContext ctx, BlockOptionalMetaLookup lookup, ChunkPos pos) { + ChunkSource chunkProvider = ctx.world().getChunkSource(); + // if chunk is not loaded, return empty stream + if (!chunkProvider.hasChunk(pos.x, pos.z)) { + return Stream.empty(); + } + + long chunkX = (long) pos.x << 4; + long chunkZ = (long) pos.z << 4; + + int playerSectionY = (ctx.playerFeet().y - ctx.world().getMinBuildHeight()) >> 4; + + return collectChunkSections(lookup, chunkProvider.getChunk(pos.x, pos.z, false), chunkX, chunkZ, playerSectionY).stream(); + } + + + private List collectChunkSections(BlockOptionalMetaLookup lookup, LevelChunk chunk, long chunkX, long chunkZ, int playerSection) { + // iterate over sections relative to player + List blocks = new ArrayList<>(); + int chunkY = chunk.getMinBuildHeight(); + LevelChunkSection[] sections = chunk.getSections(); + int l = sections.length; + int i = playerSection - 1; + int j = playerSection; + for (; i >= 0 || j < l; ++j, --i) { + if (j < l) { + visitSection(lookup, sections[j], blocks, chunkX, chunkY + j * 16, chunkZ); + } + if (i >= 0) { + visitSection(lookup, sections[i], blocks, chunkX, chunkY + i * 16, chunkZ); + } + } + return blocks; + } + + private void visitSection(BlockOptionalMetaLookup lookup, LevelChunkSection section, List blocks, long chunkX, int sectionY, long chunkZ) { + if (section == null || section.hasOnlyAir()) { + return; + } + + PalettedContainer sectionContainer = section.getStates(); + //this won't work if the PaletteStorage is of the type EmptyPaletteStorage + if (((IPalettedContainer) sectionContainer).getStorage() == null) { + return; + } + + Palette palette = ((IPalettedContainer) sectionContainer).getPalette(); + + if (palette instanceof SingleValuePalette) { + // single value palette doesn't have any data + if (lookup.has(palette.valueFor(0))) { + // TODO this is 4k hits, maybe don't return all of them? + for (int x = 0; x < 16; ++x) { + for (int y = 0; y < 16; ++y) { + for (int z = 0; z < 16; ++z) { + blocks.add(new BlockPos( + (int) chunkX + x, + sectionY + y, + (int) chunkZ + z + )); + } + } + } + } + return; + } + + boolean[] isInFilter = getIncludedFilterIndices(lookup, palette); + if (isInFilter.length == 0) { + return; + } + + BitStorage array = ((IPalettedContainer) section.getStates()).getStorage(); + long[] longArray = array.getRaw(); + int arraySize = array.getSize(); + int bitsPerEntry = array.getBits(); + long maxEntryValue = (1L << bitsPerEntry) - 1L; + + for (int i = 0, idx = 0; i < longArray.length && idx < arraySize; ++i) { + long l = longArray[i]; + for (int offset = 0; offset <= (64 - bitsPerEntry) && idx < arraySize; offset += bitsPerEntry, ++idx) { + int value = (int) ((l >> offset) & maxEntryValue); + if (isInFilter[value]) { + //noinspection DuplicateExpressions + blocks.add(new BlockPos( + (int) chunkX + ((idx & 255) & 15), + sectionY + (idx >> 8), + (int) chunkZ + ((idx & 255) >> 4) + )); + } + } + } + } + + private boolean[] getIncludedFilterIndices(BlockOptionalMetaLookup lookup, Palette palette) { + boolean commonBlockFound = false; + BlockState[] paletteMap = getPalette(palette); + + if (paletteMap == PALETTE_REGISTRY_SENTINEL) { + return getIncludedFilterIndicesFromRegistry(lookup); + } + + int size = paletteMap.length; + + boolean[] isInFilter = new boolean[size]; + + for (int i = 0; i < size; i++) { + BlockState state = paletteMap[i]; + if (lookup.has(state)) { + isInFilter[i] = true; + commonBlockFound = true; + } else { + isInFilter[i] = false; + } + } + + if (!commonBlockFound) { + return new boolean[0]; + } + return isInFilter; + } + + private boolean[] getIncludedFilterIndicesFromRegistry(BlockOptionalMetaLookup lookup) { + boolean[] isInFilter = new boolean[Block.BLOCK_STATE_REGISTRY.size()]; + + for (BlockOptionalMeta bom : lookup.blocks()) { + for (BlockState state : bom.getAllBlockStates()) { + isInFilter[Block.BLOCK_STATE_REGISTRY.getId(state)] = true; + } + } + + return isInFilter; + } + + /** + * cheats to get the actual map of id -> blockstate from the various palette implementations + */ + private static BlockState[] getPalette(Palette palette) { + if (palette instanceof GlobalPalette) { + // copying the entire registry is not nice so we treat it as a special case + return PALETTE_REGISTRY_SENTINEL; + } else { + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + palette.write(buf); + int size = buf.readVarInt(); + BlockState[] states = new BlockState[size]; + for (int i = 0; i < size; i++) { + BlockState state = Block.BLOCK_STATE_REGISTRY.byId(buf.readVarInt()); + assert state != null; + states[i] = state; + } + return states; + } + } +} diff --git a/src/main/java/baritone/cache/WaypointCollection.java b/src/main/java/baritone/cache/WaypointCollection.java index 52dd1f539..222adfdf6 100644 --- a/src/main/java/baritone/cache/WaypointCollection.java +++ b/src/main/java/baritone/cache/WaypointCollection.java @@ -48,8 +48,7 @@ public class WaypointCollection implements IWaypointCollection { if (!Files.exists(directory)) { try { Files.createDirectories(directory); - } catch (IOException ignored) { - } + } catch (IOException ignored) {} } System.out.println("Would save waypoints to " + directory); this.waypoints = new HashMap<>(); @@ -89,8 +88,7 @@ private synchronized void load(Waypoint.Tag tag) { int z = in.readInt(); this.waypoints.get(tag).add(new Waypoint(name, tag, new BetterBlockPos(x, y, z), creationTimestamp)); } - } catch (IOException ignored) { - } + } catch (IOException ignored) {} } private synchronized void save(Waypoint.Tag tag) { diff --git a/src/main/java/baritone/cache/WorldProvider.java b/src/main/java/baritone/cache/WorldProvider.java index 0fc89c3e9..38202e65b 100644 --- a/src/main/java/baritone/cache/WorldProvider.java +++ b/src/main/java/baritone/cache/WorldProvider.java @@ -19,123 +19,163 @@ import baritone.Baritone; import baritone.api.cache.IWorldProvider; -import baritone.api.utils.Helper; -import net.minecraft.client.server.IntegratedServer; -import net.minecraft.resources.ResourceKey; +import baritone.api.utils.IPlayerContext; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Tuple; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import org.apache.commons.lang3.SystemUtils; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; -import java.util.function.Consumer; +import java.util.Optional; /** * @author Brady * @since 8/4/2018 */ -public class WorldProvider implements IWorldProvider, Helper { +public class WorldProvider implements IWorldProvider { - private static final Map worldCache = new HashMap<>(); // this is how the bots have the same cached world + private static final Map worldCache = new HashMap<>(); + private final Baritone baritone; + private final IPlayerContext ctx; private WorldData currentWorld; + /** + * This lets us detect a broken load/unload hook. + * @see #detectAndHandleBrokenLoading() + */ + private Level mcWorld; + + public WorldProvider(Baritone baritone) { + this.baritone = baritone; + this.ctx = baritone.getPlayerContext(); + } + @Override public final WorldData getCurrentWorld() { - // attempt reload if the worldData is null - if (currentWorld == null && mc.level != null) { - initWorld(mc.level.dimension(), mc.level.dimensionType()); - } + this.detectAndHandleBrokenLoading(); return this.currentWorld; } /** * Called when a new world is initialized to discover the * - * @param world The world's Registry Data + * @param world The new world */ - public final void initWorld(ResourceKey worldKey, DimensionType world) { - Path directory; - Path readme; + public final void initWorld(Level world) { + this.getSaveDirectories(world).ifPresent(dirs -> { + final Path worldDir = dirs.getA(); + final Path readmeDir = dirs.getB(); + + try { + // lol wtf is this baritone folder in my minecraft save? + // good thing we have a readme + Files.createDirectories(readmeDir); + Files.write( + readmeDir.resolve("readme.txt"), + "https://github.com/cabaletta/baritone\n".getBytes(StandardCharsets.US_ASCII) + ); + } catch (IOException ignored) {} + + // We will actually store the world data in a subfolder: "DIM" + final Path worldDataDir = this.getWorldDataDirectory(worldDir, world); + try { + Files.createDirectories(worldDataDir); + } catch (IOException ignored) {} + + System.out.println("Baritone world data dir: " + worldDataDir); + synchronized (worldCache) { + this.currentWorld = worldCache.computeIfAbsent(worldDataDir, d -> new WorldData(d, world.dimensionType())); + } + this.mcWorld = ctx.world(); + }); + } - IntegratedServer integratedServer = mc.getSingleplayerServer(); + public final void closeWorld() { + WorldData world = this.currentWorld; + this.currentWorld = null; + this.mcWorld = null; + if (world == null) { + return; + } + world.onClose(); + } + + private Path getWorldDataDirectory(Path parent, Level world) { + ResourceLocation dimId = world.dimension().location(); + int height = world.dimensionType().logicalHeight(); + return parent.resolve(dimId.getNamespace()).resolve(dimId.getPath() + "_" + height); + } + + /** + * @param world The world + * @return An {@link Optional} containing the world's baritone dir and readme dir, or {@link Optional#empty()} if + * the world isn't valid for caching. + */ + private Optional> getSaveDirectories(Level world) { + Path worldDir; + Path readmeDir; // If there is an integrated server running (Aka Singleplayer) then do magic to find the world save file - if (mc.hasSingleplayerServer()) { - directory = DimensionType.getStorageFolder(worldKey, integratedServer.getWorldPath(LevelResource.ROOT)); + if (ctx.minecraft().hasSingleplayerServer()) { + worldDir = ctx.minecraft().getSingleplayerServer().getWorldPath(LevelResource.ROOT); - // Gets the "depth" of this directory relative the the game's run directory, 2 is the location of the world - if (directory.relativize(mc.gameDirectory.toPath()).getNameCount() != 2) { + // Gets the "depth" of this directory relative to the game's run directory, 2 is the location of the world + if (worldDir.relativize(ctx.minecraft().gameDirectory.toPath()).getNameCount() != 2) { // subdirectory of the main save directory for this world - directory = directory.getParent(); + worldDir = worldDir.getParent(); } - directory = directory.resolve("baritone"); - readme = directory; + worldDir = worldDir.resolve("baritone"); + readmeDir = worldDir; } else { // Otherwise, the server must be remote... String folderName; - if (mc.isConnectedToRealms()) { - folderName = "realms"; + final ServerData serverData = ctx.minecraft().getCurrentServer(); + if (serverData != null) { + folderName = serverData.isRealm() ? "realms" : serverData.ip; } else { - if (mc.getCurrentServer() != null) { - folderName = mc.getCurrentServer().ip; - } else { - //replaymod causes null currentServerData and false singleplayer. - System.out.println("BARITONE: setting current world to null"); - currentWorld = null; - return; - } + //replaymod causes null currentServer and false singleplayer. + System.out.println("World seems to be a replay. Not loading Baritone cache."); + currentWorld = null; + mcWorld = ctx.world(); + return Optional.empty(); } if (SystemUtils.IS_OS_WINDOWS) { folderName = folderName.replace(":", "_"); } - directory = Baritone.getDir().toPath().resolve(folderName); - readme = Baritone.getDir().toPath(); - } - - // lol wtf is this baritone folder in my minecraft save? - try (FileOutputStream out = new FileOutputStream(readme.resolve("readme.txt").toFile())) { - // good thing we have a readme - out.write("https://github.com/cabaletta/baritone\n".getBytes()); - } catch (IOException ignored) { - } - - // We will actually store the world data in a subfolder: "DIM" - Path dir = getDimDir(worldKey, world.logicalHeight(), directory); - if (!Files.exists(dir)) { - try { - Files.createDirectories(dir); - } catch (IOException ignored) { - } - } - - System.out.println("Baritone world data dir: " + dir); - synchronized (worldCache) { - this.currentWorld = worldCache.computeIfAbsent(dir, d -> new WorldData(d, world)); + // TODO: This should probably be in "baritone/servers" + worldDir = baritone.getDirectory().resolve(folderName); + // Just write the readme to the baritone directory instead of each server save in it + readmeDir = baritone.getDirectory(); } - } - public final Path getDimDir(ResourceKey level, int height, Path directory) { - return directory.resolve(level.location().getNamespace()).resolve(level.location().getPath() + "_" + height); + return Optional.of(new Tuple<>(worldDir, readmeDir)); } - public final void closeWorld() { - WorldData world = this.currentWorld; - this.currentWorld = null; - if (world == null) { - return; - } - world.onClose(); - } - - public final void ifWorldLoaded(Consumer currentWorldConsumer) { - if (this.currentWorld != null) { - currentWorldConsumer.accept(this.currentWorld); + /** + * Why does this exist instead of fixing the event? Some mods break the event. Lol. + */ + private void detectAndHandleBrokenLoading() { + if (this.mcWorld != ctx.world()) { + if (this.currentWorld != null) { + System.out.println("mc.world unloaded unnoticed! Unloading Baritone cache now."); + closeWorld(); + } + if (ctx.world() != null) { + System.out.println("mc.world loaded unnoticed! Loading Baritone cache now."); + initWorld(ctx.world()); + } + } else if (this.currentWorld == null && ctx.world() != null && (ctx.minecraft().hasSingleplayerServer() || ctx.minecraft().getCurrentServer() != null)) { + System.out.println("Retrying to load Baritone cache"); + initWorld(ctx.world()); } } } diff --git a/src/main/java/baritone/command/ExampleBaritoneControl.java b/src/main/java/baritone/command/ExampleBaritoneControl.java index 2ba6f74c9..943b8afa6 100644 --- a/src/main/java/baritone/command/ExampleBaritoneControl.java +++ b/src/main/java/baritone/command/ExampleBaritoneControl.java @@ -17,8 +17,8 @@ package baritone.command; +import baritone.Baritone; import baritone.api.BaritoneAPI; -import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.command.argument.ICommandArgument; import baritone.api.command.exception.CommandNotEnoughArgumentsException; @@ -27,20 +27,17 @@ import baritone.api.command.manager.ICommandManager; import baritone.api.event.events.ChatEvent; import baritone.api.event.events.TabCompleteEvent; -import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.utils.Helper; import baritone.api.utils.SettingsUtil; +import baritone.behavior.Behavior; import baritone.command.argument.ArgConsumer; import baritone.command.argument.CommandArguments; import baritone.command.manager.CommandManager; import baritone.utils.accessor.IGuiScreen; import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.*; import net.minecraft.network.chat.MutableComponent; import net.minecraft.util.Tuple; - import java.net.URI; import java.net.URISyntaxException; import java.util.List; @@ -49,14 +46,14 @@ import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; -public class ExampleBaritoneControl implements Helper, AbstractGameEventListener { +public class ExampleBaritoneControl extends Behavior implements Helper { private static final Settings settings = BaritoneAPI.getSettings(); private final ICommandManager manager; - public ExampleBaritoneControl(IBaritone baritone) { + public ExampleBaritoneControl(Baritone baritone) { + super(baritone); this.manager = baritone.getCommandManager(); - baritone.getGameEventHandler().registerEventListener(this); } @Override @@ -100,9 +97,8 @@ public boolean runCommand(String msg) { return false; } else if (msg.trim().equalsIgnoreCase("orderpizza")) { try { - ((IGuiScreen) mc.screen).openLinkInvoker(new URI("https://www.dominos.com/en/pages/order/")); - } catch (NullPointerException | URISyntaxException ignored) { - } + ((IGuiScreen) ctx.minecraft().screen).openLinkInvoker(new URI("https://www.dominos.com/en/pages/order/")); + } catch (NullPointerException | URISyntaxException ignored) {} return false; } if (msg.isEmpty()) { @@ -125,15 +121,14 @@ public boolean runCommand(String msg) { } } else if (argc.hasExactlyOne()) { for (Settings.Setting setting : settings.allSettings) { - if (SettingsUtil.javaOnlySetting(setting)) { + if (setting.isJavaOnly()) { continue; } if (setting.getName().equalsIgnoreCase(pair.getA())) { logRanCommand(command, rest); try { this.manager.execute(String.format("set %s %s", setting.getName(), argc.getString())); - } catch (CommandNotEnoughArgumentsException ignored) { - } // The operation is safe + } catch (CommandNotEnoughArgumentsException ignored) {} // The operation is safe return true; } } @@ -179,7 +174,7 @@ public Stream tabComplete(String msg) { .stream(); } Settings.Setting setting = settings.byLowerName.get(argc.getString().toLowerCase(Locale.US)); - if (setting != null && !SettingsUtil.javaOnlySetting(setting)) { + if (setting != null && !setting.isJavaOnly()) { if (setting.getValueClass() == Boolean.class) { TabCompleteHelper helper = new TabCompleteHelper(); if ((Boolean) setting.value) { diff --git a/src/main/java/baritone/command/argument/ArgConsumer.java b/src/main/java/baritone/command/argument/ArgConsumer.java index c4a6df002..99f30ce96 100644 --- a/src/main/java/baritone/command/argument/ArgConsumer.java +++ b/src/main/java/baritone/command/argument/ArgConsumer.java @@ -380,6 +380,8 @@ public > T getDatatypeForOrNull(D datatype) { public Stream tabCompleteDatatype(T datatype) { try { return datatype.tabComplete(this.context); + } catch (CommandException ignored) { + // NOP } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/baritone/command/argument/CommandArguments.java b/src/main/java/baritone/command/argument/CommandArguments.java index d10fbc807..22a2ef887 100644 --- a/src/main/java/baritone/command/argument/CommandArguments.java +++ b/src/main/java/baritone/command/argument/CommandArguments.java @@ -30,8 +30,7 @@ */ public final class CommandArguments { - private CommandArguments() { - } + private CommandArguments() {} private static final Pattern ARG_PATTERN = Pattern.compile("\\S+"); diff --git a/src/main/java/baritone/command/defaults/BuildCommand.java b/src/main/java/baritone/command/defaults/BuildCommand.java index 622aef4f8..bb34254ae 100644 --- a/src/main/java/baritone/command/defaults/BuildCommand.java +++ b/src/main/java/baritone/command/defaults/BuildCommand.java @@ -35,10 +35,11 @@ public class BuildCommand extends Command { - private static final File schematicsDir = new File(mc.gameDirectory, "schematics"); + private final File schematicsDir; public BuildCommand(IBaritone baritone) { super(baritone, "build"); + this.schematicsDir = new File(baritone.getPlayerContext().minecraft().gameDirectory, "schematics"); } @Override diff --git a/src/main/java/baritone/command/defaults/ComeCommand.java b/src/main/java/baritone/command/defaults/ComeCommand.java index dfbbf06bd..b111ee1de 100644 --- a/src/main/java/baritone/command/defaults/ComeCommand.java +++ b/src/main/java/baritone/command/defaults/ComeCommand.java @@ -21,9 +21,7 @@ import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; -import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.pathing.goals.GoalBlock; -import net.minecraft.world.entity.Entity; import java.util.Arrays; import java.util.List; @@ -38,11 +36,7 @@ public ComeCommand(IBaritone baritone) { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); - Entity entity = mc.getCameraEntity(); - if (entity == null) { - throw new CommandInvalidStateException("render view entity is null"); - } - baritone.getCustomGoalProcess().setGoalAndPath(new GoalBlock(entity.blockPosition())); + baritone.getCustomGoalProcess().setGoalAndPath(new GoalBlock(ctx.viewerPos())); logDirect("Coming"); } diff --git a/src/main/java/baritone/command/defaults/DefaultCommands.java b/src/main/java/baritone/command/defaults/DefaultCommands.java index c2118ebe8..c810d07c1 100644 --- a/src/main/java/baritone/command/defaults/DefaultCommands.java +++ b/src/main/java/baritone/command/defaults/DefaultCommands.java @@ -66,7 +66,8 @@ public static List createAll(IBaritone baritone) { new WaypointsCommand(baritone), new CommandAlias(baritone, "sethome", "Sets your home waypoint", "waypoints save home"), new CommandAlias(baritone, "home", "Path to your home waypoint", "waypoints goto home"), - new SelCommand(baritone) + new SelCommand(baritone), + new ElytraCommand(baritone) )); ExecutionControlCommands prc = new ExecutionControlCommands(baritone); commands.add(prc.pauseCommand); diff --git a/src/main/java/baritone/command/defaults/ElytraCommand.java b/src/main/java/baritone/command/defaults/ElytraCommand.java new file mode 100644 index 000000000..3f7b6bfd6 --- /dev/null +++ b/src/main/java/baritone/command/defaults/ElytraCommand.java @@ -0,0 +1,225 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.command.defaults; + +import baritone.Baritone; +import baritone.api.IBaritone; +import baritone.api.command.Command; +import baritone.api.command.argument.IArgConsumer; +import baritone.api.command.exception.CommandException; +import baritone.api.command.exception.CommandInvalidStateException; +import baritone.api.command.helpers.TabCompleteHelper; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.process.IElytraProcess; +import net.minecraft.ChatFormatting; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.level.Level; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; + +public class ElytraCommand extends Command { + + public ElytraCommand(IBaritone baritone) { + super(baritone, "elytra"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + final ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); + final IElytraProcess elytra = baritone.getElytraProcess(); + if (args.hasExactlyOne() && args.peekString().equals("supported")) { + logDirect(elytra.isLoaded() ? "yes" : unsupportedSystemMessage()); + return; + } + if (!elytra.isLoaded()) { + throw new CommandInvalidStateException(unsupportedSystemMessage()); + } + + if (!args.hasAny()) { + if (Baritone.settings().elytraTermsAccepted.value) { + if (detectOn2b2t()) { + warn2b2t(); + } + } else { + gatekeep(); + } + Goal iGoal = customGoalProcess.mostRecentGoal(); + if (iGoal == null) { + throw new CommandInvalidStateException("No goal has been set"); + } + if (ctx.world().dimension() != Level.NETHER) { + throw new CommandInvalidStateException("Only works in the nether"); + } + try { + elytra.pathTo(iGoal); + } catch (IllegalArgumentException ex) { + throw new CommandInvalidStateException(ex.getMessage()); + } + return; + } + + final String action = args.getString(); + switch (action) { + case "reset": { + elytra.resetState(); + logDirect("Reset state but still flying to same goal"); + break; + } + case "repack": { + elytra.repackChunks(); + logDirect("Queued all loaded chunks for repacking"); + break; + } + default: { + throw new CommandInvalidStateException("Invalid action"); + } + } + } + + private void warn2b2t() { + if (Baritone.settings().elytraPredictTerrain.value) { + long seed = Baritone.settings().elytraNetherSeed.value; + if (seed != NEW_2B2T_SEED && seed != OLD_2B2T_SEED) { + logDirect(Component.literal("It looks like you're on 2b2t, but elytraNetherSeed is incorrect.")); // match color + logDirect(suggest2b2tSeeds()); + } + } + } + + private Component suggest2b2tSeeds() { + MutableComponent clippy = Component.literal(""); + clippy.append("Within a few hundred blocks of spawn/axis/highways/etc, the terrain is too fragmented to be predictable. Baritone Elytra will still work, just with backtracking. "); + clippy.append("However, once you get more than a few thousand blocks out, you should try "); + MutableComponent olderSeed = Component.literal("the older seed (click here)"); + olderSeed.setStyle(olderSeed.getStyle().withUnderlined(true).withBold(true).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraNetherSeed " + OLD_2B2T_SEED))).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + "set elytraNetherSeed " + OLD_2B2T_SEED))); + clippy.append(olderSeed); + clippy.append(". Once you're further out into newer terrain generation (this includes everything up through 1.12), you should try "); + MutableComponent newerSeed = Component.literal("the newer seed (click here)"); + newerSeed.setStyle(newerSeed.getStyle().withUnderlined(true).withBold(true).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraNetherSeed " + NEW_2B2T_SEED))).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, FORCE_COMMAND_PREFIX + "set elytraNetherSeed " + NEW_2B2T_SEED))); + clippy.append(newerSeed); + clippy.append(". Once you get into 1.19 terrain, the terrain becomes unpredictable again, due to custom non-vanilla generation, and you should set #elytraPredictTerrain to false. "); + return clippy; + } + + private void gatekeep() { + MutableComponent gatekeep = Component.literal(""); + gatekeep.append("To disable this message, enable the setting elytraTermsAccepted\n"); + gatekeep.append("Baritone Elytra is an experimental feature. It is only intended for long distance travel in the Nether using fireworks for vanilla boost. It will not work with any other mods (\"hacks\") for non-vanilla boost. "); + MutableComponent gatekeep2 = Component.literal("If you want Baritone to attempt to take off from the ground for you, you can enable the elytraAutoJump setting (not advisable on laggy servers!). "); + gatekeep2.setStyle(gatekeep2.getStyle().withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraAutoJump true")))); + gatekeep.append(gatekeep2); + MutableComponent gatekeep3 = Component.literal("If you want Baritone to go slower, enable the elytraConserveFireworks setting and/or decrease the elytraFireworkSpeed setting. "); + gatekeep3.setStyle(gatekeep3.getStyle().withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal(Baritone.settings().prefix.value + "set elytraConserveFireworks true\n" + Baritone.settings().prefix.value + "set elytraFireworkSpeed 0.6\n(the 0.6 number is just an example, tweak to your liking)")))); + gatekeep.append(gatekeep3); + MutableComponent gatekeep4 = Component.literal("Baritone Elytra "); + MutableComponent red = Component.literal("wants to know the seed"); + red.setStyle(red.getStyle().withColor(ChatFormatting.RED).withUnderlined(true).withBold(true)); + gatekeep4.append(red); + gatekeep4.append(" of the world you are in. If it doesn't have the correct seed, it will frequently backtrack. It uses the seed to generate terrain far beyond what you can see, since terrain obstacles in the Nether can be much larger than your render distance. "); + gatekeep.append(gatekeep4); + gatekeep.append("\n"); + if (detectOn2b2t()) { + MutableComponent gatekeep5 = Component.literal("It looks like you're on 2b2t. "); + gatekeep5.append(suggest2b2tSeeds()); + if (!Baritone.settings().elytraPredictTerrain.value) { + gatekeep5.append(Baritone.settings().prefix.value + "elytraPredictTerrain is currently disabled. "); + } else { + if (Baritone.settings().elytraNetherSeed.value == NEW_2B2T_SEED) { + gatekeep5.append("You are using the newer seed. "); + } else if (Baritone.settings().elytraNetherSeed.value == OLD_2B2T_SEED) { + gatekeep5.append("You are using the older seed. "); + } else { + gatekeep5.append("Defaulting to the newer seed. "); + Baritone.settings().elytraNetherSeed.value = NEW_2B2T_SEED; + } + } + gatekeep.append(gatekeep5); + } else { + if (Baritone.settings().elytraNetherSeed.value == NEW_2B2T_SEED) { + MutableComponent gatekeep5 = Component.literal("Baritone doesn't know the seed of your world. Set it with: " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere\n"); + gatekeep5.append("For the time being, elytraPredictTerrain is defaulting to false since the seed is unknown."); + gatekeep.append(gatekeep5); + Baritone.settings().elytraPredictTerrain.value = false; + } else { + if (Baritone.settings().elytraPredictTerrain.value) { + MutableComponent gatekeep5 = Component.literal("Baritone Elytra is predicting terrain assuming that " + Baritone.settings().elytraNetherSeed.value + " is the correct seed. Change that with " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere, or disable it with " + Baritone.settings().prefix.value + "set elytraPredictTerrain false"); + gatekeep.append(gatekeep5); + } else { + MutableComponent gatekeep5 = Component.literal("Baritone Elytra is not predicting terrain. If you don't know the seed, this is the correct thing to do. If you do know the seed, input it with " + Baritone.settings().prefix.value + "set elytraNetherSeed seedgoeshere, and then enable it with " + Baritone.settings().prefix.value + "set elytraPredictTerrain true"); + gatekeep.append(gatekeep5); + } + } + } + logDirect(gatekeep); + } + + private boolean detectOn2b2t() { + ServerData data = ctx.minecraft().getCurrentServer(); + return data != null && data.ip.toLowerCase().contains("2b2t.org"); + } + + private static final long OLD_2B2T_SEED = -4100785268875389365L; + private static final long NEW_2B2T_SEED = 146008555100680L; + + @Override + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + TabCompleteHelper helper = new TabCompleteHelper(); + if (args.hasExactlyOne()) { + helper.append("reset", "repack", "supported"); + } + return helper.filterPrefix(args.getString()).stream(); + } + + @Override + public String getShortDesc() { + return "elytra time"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "The elytra command tells baritone to, in the nether, automatically fly to the current goal.", + "", + "Usage:", + "> elytra - fly to the current goal", + "> elytra reset - Resets the state of the process, but will try to keep flying to the same goal.", + "> elytra repack - Queues all of the chunks in render distance to be given to the native library.", + "> elytra supported - Tells you if baritone ships a native library that is compatible with your PC." + ); + } + + private static String unsupportedSystemMessage() { + final String osArch = System.getProperty("os.arch"); + final String osName = System.getProperty("os.name"); + return String.format( + "Legacy architectures are not supported. Your CPU is %s and your operating system is %s. " + + "Supported architectures are 64 bit x86, and 64 bit ARM. Supported operating systems are Windows, " + + "Linux, and Mac", + osArch, osName + ); + } +} diff --git a/src/main/java/baritone/command/defaults/ExecutionControlCommands.java b/src/main/java/baritone/command/defaults/ExecutionControlCommands.java index eaab75286..6f6293ccd 100644 --- a/src/main/java/baritone/command/defaults/ExecutionControlCommands.java +++ b/src/main/java/baritone/command/defaults/ExecutionControlCommands.java @@ -56,6 +56,7 @@ public boolean isActive() { @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + baritone.getInputOverrideHandler().clearAllKeys(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } diff --git a/src/main/java/baritone/command/defaults/ExploreFilterCommand.java b/src/main/java/baritone/command/defaults/ExploreFilterCommand.java index 504f7c146..ecb5f8376 100644 --- a/src/main/java/baritone/command/defaults/ExploreFilterCommand.java +++ b/src/main/java/baritone/command/defaults/ExploreFilterCommand.java @@ -41,7 +41,7 @@ public ExploreFilterCommand(IBaritone baritone) { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(2); - File file = args.getDatatypePost(RelativeFile.INSTANCE, mc.gameDirectory.getAbsoluteFile().getParentFile()); + File file = args.getDatatypePost(RelativeFile.INSTANCE, ctx.minecraft().gameDirectory.getAbsoluteFile().getParentFile()); boolean invert = false; if (args.hasAny()) { if (args.getString().equalsIgnoreCase("invert")) { @@ -65,7 +65,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { @Override public Stream tabComplete(String label, IArgConsumer args) throws CommandException { if (args.hasExactlyOne()) { - return RelativeFile.tabComplete(args, RelativeFile.gameDir()); + return RelativeFile.tabComplete(args, RelativeFile.gameDir(ctx.minecraft())); } return Stream.empty(); } diff --git a/src/main/java/baritone/command/defaults/FindCommand.java b/src/main/java/baritone/command/defaults/FindCommand.java index 1efbbc93e..500981873 100644 --- a/src/main/java/baritone/command/defaults/FindCommand.java +++ b/src/main/java/baritone/command/defaults/FindCommand.java @@ -25,6 +25,7 @@ import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.BetterBlockPos; import baritone.cache.CachedChunk; +import net.minecraft.core.Registry; import net.minecraft.ChatFormatting; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.ClickEvent; @@ -83,8 +84,7 @@ private Component positionToComponent(BetterBlockPos pos) { .withColor(ChatFormatting.GRAY) .withInsertion(positionText) .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)) - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent)) - ); + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverComponent))); return baseComponent; } diff --git a/src/main/java/baritone/command/defaults/FollowCommand.java b/src/main/java/baritone/command/defaults/FollowCommand.java index b341db003..b7e708377 100644 --- a/src/main/java/baritone/command/defaults/FollowCommand.java +++ b/src/main/java/baritone/command/defaults/FollowCommand.java @@ -27,6 +27,10 @@ import baritone.api.command.exception.CommandErrorMessageException; import baritone.api.command.exception.CommandException; import baritone.api.command.helpers.TabCompleteHelper; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Stream; +import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; @@ -34,10 +38,6 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Stream; - public class FollowCommand extends Command { public FollowCommand(IBaritone baritone) { diff --git a/src/main/java/baritone/command/defaults/GoalCommand.java b/src/main/java/baritone/command/defaults/GoalCommand.java index 40822e057..a174ecad9 100644 --- a/src/main/java/baritone/command/defaults/GoalCommand.java +++ b/src/main/java/baritone/command/defaults/GoalCommand.java @@ -51,7 +51,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { } } else { args.requireMax(3); - BetterBlockPos origin = baritone.getPlayerContext().playerFeet(); + BetterBlockPos origin = ctx.playerFeet(); Goal goal = args.getDatatypePost(RelativeGoal.INSTANCE, origin); goalProcess.setGoal(goal); logDirect(String.format("Goal: %s", goal.toString())); diff --git a/src/main/java/baritone/command/defaults/GotoCommand.java b/src/main/java/baritone/command/defaults/GotoCommand.java index 896e3f5f8..c64d7fa00 100644 --- a/src/main/java/baritone/command/defaults/GotoCommand.java +++ b/src/main/java/baritone/command/defaults/GotoCommand.java @@ -20,7 +20,6 @@ import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; -import baritone.api.command.datatypes.BlockById; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.datatypes.RelativeCoordinate; import baritone.api.command.datatypes.RelativeGoal; @@ -46,7 +45,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { // is no need to handle the case of empty arguments. if (args.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) != null) { args.requireMax(3); - BetterBlockPos origin = baritone.getPlayerContext().playerFeet(); + BetterBlockPos origin = ctx.playerFeet(); Goal goal = args.getDatatypePost(RelativeGoal.INSTANCE, origin); logDirect(String.format("Going to: %s", goal.toString())); baritone.getCustomGoalProcess().setGoalAndPath(goal); @@ -61,7 +60,8 @@ public void execute(String label, IArgConsumer args) throws CommandException { public Stream tabComplete(String label, IArgConsumer args) throws CommandException { // since it's either a goal or a block, I don't think we can tab complete properly? // so just tab complete for the block variant - return args.tabCompleteDatatype(BlockById.INSTANCE); + args.requireMax(1); + return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); } @Override diff --git a/src/main/java/baritone/command/defaults/HelpCommand.java b/src/main/java/baritone/command/defaults/HelpCommand.java index b751730e7..822884b09 100644 --- a/src/main/java/baritone/command/defaults/HelpCommand.java +++ b/src/main/java/baritone/command/defaults/HelpCommand.java @@ -25,17 +25,19 @@ import baritone.api.command.exception.CommandNotFoundException; import baritone.api.command.helpers.Paginator; import baritone.api.command.helpers.TabCompleteHelper; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.MutableComponent; +import java.awt.*; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; + import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class HelpCommand extends Command { diff --git a/src/main/java/baritone/command/defaults/LitematicaCommand.java b/src/main/java/baritone/command/defaults/LitematicaCommand.java new file mode 100644 index 000000000..bfe0079b3 --- /dev/null +++ b/src/main/java/baritone/command/defaults/LitematicaCommand.java @@ -0,0 +1,71 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.command.Command; +import baritone.api.command.argument.IArgConsumer; +import baritone.api.command.exception.CommandException; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class LitematicaCommand extends Command { + + public LitematicaCommand(IBaritone baritone) { + super(baritone, "litematica"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + int schematic = 0; + if (args.hasAny()) { + args.requireMax(1); + if (args.is(Integer.class)) { + schematic = args.getAs(Integer.class) - 1; + } + } + try { + baritone.getBuilderProcess().buildOpenLitematic(schematic); + } catch (IndexOutOfBoundsException e) { + logDirect("Pleas provide a valid index."); + } + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Builds the loaded schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Build a schematic currently open in Litematica.", + "", + "Usage:", + "> litematica", + "> litematica <#>" + ); + } +} \ No newline at end of file diff --git a/src/main/java/baritone/command/defaults/MineCommand.java b/src/main/java/baritone/command/defaults/MineCommand.java index 98a2e264d..eb2c22c6d 100644 --- a/src/main/java/baritone/command/defaults/MineCommand.java +++ b/src/main/java/baritone/command/defaults/MineCommand.java @@ -17,14 +17,13 @@ package baritone.command.defaults; +import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; -import baritone.api.command.datatypes.BlockById; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.exception.CommandException; import baritone.api.utils.BlockOptionalMeta; -import baritone.cache.WorldScanner; import java.util.ArrayList; import java.util.Arrays; @@ -45,14 +44,18 @@ public void execute(String label, IArgConsumer args) throws CommandException { while (args.hasAny()) { boms.add(args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE)); } - WorldScanner.INSTANCE.repack(ctx); + BaritoneAPI.getProvider().getWorldScanner().repack(ctx); logDirect(String.format("Mining %s", boms.toString())); baritone.getMineProcess().mine(quantity, boms.toArray(new BlockOptionalMeta[0])); } @Override - public Stream tabComplete(String label, IArgConsumer args) { - return args.tabCompleteDatatype(BlockById.INSTANCE); + public Stream tabComplete(String label, IArgConsumer args) throws CommandException { + args.getAsOrDefault(Integer.class, 0); + while (args.has(2)) { + args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); + } + return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); } @Override diff --git a/src/main/java/baritone/command/defaults/PathCommand.java b/src/main/java/baritone/command/defaults/PathCommand.java index 182a1e5bc..b2021adf6 100644 --- a/src/main/java/baritone/command/defaults/PathCommand.java +++ b/src/main/java/baritone/command/defaults/PathCommand.java @@ -17,12 +17,12 @@ package baritone.command.defaults; +import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.process.ICustomGoalProcess; -import baritone.cache.WorldScanner; import java.util.Arrays; import java.util.List; @@ -38,7 +38,7 @@ public PathCommand(IBaritone baritone) { public void execute(String label, IArgConsumer args) throws CommandException { ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); args.requireMax(0); - WorldScanner.INSTANCE.repack(ctx); + BaritoneAPI.getProvider().getWorldScanner().repack(ctx); customGoalProcess.path(); logDirect("Now pathing"); } diff --git a/src/main/java/baritone/command/defaults/RenderCommand.java b/src/main/java/baritone/command/defaults/RenderCommand.java index 39dc6ea7c..543c3387c 100644 --- a/src/main/java/baritone/command/defaults/RenderCommand.java +++ b/src/main/java/baritone/command/defaults/RenderCommand.java @@ -37,8 +37,8 @@ public RenderCommand(IBaritone baritone) { public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); BetterBlockPos origin = ctx.playerFeet(); - int renderDistance = (mc.options.renderDistance().get() + 1) * 16; - mc.levelRenderer.setBlocksDirty( + int renderDistance = (ctx.minecraft().options.renderDistance().get() + 1) * 16; + ctx.minecraft().levelRenderer.setBlocksDirty( origin.x - renderDistance, 0, origin.z - renderDistance, diff --git a/src/main/java/baritone/command/defaults/RepackCommand.java b/src/main/java/baritone/command/defaults/RepackCommand.java index cafbea524..9f972561d 100644 --- a/src/main/java/baritone/command/defaults/RepackCommand.java +++ b/src/main/java/baritone/command/defaults/RepackCommand.java @@ -17,11 +17,11 @@ package baritone.command.defaults; +import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; -import baritone.cache.WorldScanner; import java.util.Arrays; import java.util.List; @@ -36,7 +36,7 @@ public RepackCommand(IBaritone baritone) { @Override public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(0); - logDirect(String.format("Queued %d chunks for repacking", WorldScanner.INSTANCE.repack(ctx))); + logDirect(String.format("Queued %d chunks for repacking", BaritoneAPI.getProvider().getWorldScanner().repack(ctx))); } @Override diff --git a/src/main/java/baritone/command/defaults/SelCommand.java b/src/main/java/baritone/command/defaults/SelCommand.java index 212498240..a374e92e8 100644 --- a/src/main/java/baritone/command/defaults/SelCommand.java +++ b/src/main/java/baritone/command/defaults/SelCommand.java @@ -21,6 +21,7 @@ import baritone.api.IBaritone; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; +import baritone.api.command.datatypes.ForAxis; import baritone.api.command.datatypes.ForBlockOptionalMeta; import baritone.api.command.datatypes.ForDirection; import baritone.api.command.datatypes.RelativeBlockPos; @@ -31,6 +32,8 @@ import baritone.api.event.events.RenderEvent; import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.schematic.*; +import baritone.api.schematic.mask.shape.CylinderMask; +import baritone.api.schematic.mask.shape.SphereMask; import baritone.api.selection.ISelection; import baritone.api.selection.ISelectionManager; import baritone.api.utils.BetterBlockPos; @@ -39,6 +42,7 @@ import baritone.utils.BlockStateInterface; import baritone.utils.IRenderer; import baritone.utils.schematic.StaticSchematic; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.world.level.block.Blocks; @@ -46,9 +50,10 @@ import net.minecraft.world.phys.AABB; import java.awt.*; -import java.util.List; import java.util.*; +import java.util.List; import java.util.function.Function; +import java.util.function.UnaryOperator; import java.util.stream.Stream; public class SelCommand extends Command { @@ -71,7 +76,7 @@ public void onRenderPass(RenderEvent event) { float lineWidth = Baritone.settings().selectionLineWidth.value; boolean ignoreDepth = Baritone.settings().renderSelectionIgnoreDepth.value; IRenderer.startLines(color, opacity, lineWidth, ignoreDepth); - IRenderer.drawAABB(event.getModelViewStack(), new AABB(pos1, pos1.offset(1, 1, 1))); + IRenderer.emitAABB(event.getModelViewStack(), new AABB(pos1)); IRenderer.endLines(ignoreDepth); } }); @@ -87,7 +92,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { if (action == Action.POS2 && pos1 == null) { throw new CommandInvalidStateException("Set pos1 first before using pos2"); } - BetterBlockPos playerPos = mc.getCameraEntity() != null ? BetterBlockPos.from(mc.getCameraEntity().blockPosition()) : ctx.playerFeet(); + BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); if (action == Action.POS1) { @@ -116,11 +121,13 @@ public void execute(String label, IArgConsumer args) throws CommandException { logDirect("Undid pos2"); } } - } else if (action == Action.SET || action == Action.WALLS || action == Action.SHELL || action == Action.CLEARAREA || action == Action.REPLACE) { + } else if (action.isFillAction()) { BlockOptionalMeta type = action == Action.CLEARAREA ? new BlockOptionalMeta(Blocks.AIR) : args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); - BlockOptionalMetaLookup replaces = null; + + final BlockOptionalMetaLookup replaces; // Action.REPLACE + final Direction.Axis alignment; // Action.(H)CYLINDER if (action == Action.REPLACE) { args.requireMin(1); List replacesList = new ArrayList<>(); @@ -130,8 +137,15 @@ public void execute(String label, IArgConsumer args) throws CommandException { } type = args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE); replaces = new BlockOptionalMetaLookup(replacesList.toArray(new BlockOptionalMeta[0])); + alignment = null; + } else if (action == Action.CYLINDER || action == Action.HCYLINDER) { + args.requireMax(1); + alignment = args.hasAny() ? args.getDatatypeFor(ForAxis.INSTANCE) : Direction.Axis.Y; + replaces = null; } else { args.requireMax(0); + replaces = null; + alignment = null; } ISelection[] selections = manager.getSelections(); if (selections.length == 0) { @@ -150,20 +164,41 @@ public void execute(String label, IArgConsumer args) throws CommandException { for (ISelection selection : selections) { Vec3i size = selection.size(); BetterBlockPos min = selection.min(); - ISchematic schematic = new FillSchematic(size.getX(), size.getY(), size.getZ(), type); - if (action == Action.WALLS) { - schematic = new WallsSchematic(schematic); - } else if (action == Action.SHELL) { - schematic = new ShellSchematic(schematic); - } else if (action == Action.REPLACE) { - schematic = new ReplaceSchematic(schematic, replaces); - } + + // Java 8 so no switch expressions 😿 + UnaryOperator create = fill -> { + final int w = fill.widthX(); + final int h = fill.heightY(); + final int l = fill.lengthZ(); + + switch (action) { + case WALLS: + return new WallsSchematic(fill); + case SHELL: + return new ShellSchematic(fill); + case REPLACE: + return new ReplaceSchematic(fill, replaces); + case SPHERE: + return MaskSchematic.create(fill, new SphereMask(w, h, l, true).compute()); + case HSPHERE: + return MaskSchematic.create(fill, new SphereMask(w, h, l, false).compute()); + case CYLINDER: + return MaskSchematic.create(fill, new CylinderMask(w, h, l, true, alignment).compute()); + case HCYLINDER: + return MaskSchematic.create(fill, new CylinderMask(w, h, l, false, alignment).compute()); + default: + // Silent fail + return fill; + } + }; + + ISchematic schematic = create.apply(new FillSchematic(size.getX(), size.getY(), size.getZ(), type)); composite.put(schematic, min.x - origin.x, min.y - origin.y, min.z - origin.z); } baritone.getBuilderProcess().build("Fill", composite, origin); logDirect("Filling now"); } else if (action == Action.COPY) { - BetterBlockPos playerPos = mc.getCameraEntity() != null ? BetterBlockPos.from(mc.getCameraEntity().blockPosition()) : ctx.playerFeet(); + BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); ISelection[] selections = manager.getSelections(); @@ -204,7 +239,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { clipboardOffset = origin.subtract(pos); logDirect("Selection copied"); } else if (action == Action.PASTE) { - BetterBlockPos playerPos = mc.getCameraEntity() != null ? BetterBlockPos.from(mc.getCameraEntity().blockPosition()) : ctx.playerFeet(); + BetterBlockPos playerPos = ctx.viewerPos(); BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, playerPos) : playerPos; args.requireMax(0); if (clipboard == null) { @@ -253,12 +288,15 @@ public Stream tabComplete(String label, IArgConsumer args) throws Comman if (args.hasAtMost(3)) { return args.tabCompleteDatatype(RelativeBlockPos.INSTANCE); } - } else if (action == Action.SET || action == Action.WALLS || action == Action.CLEARAREA || action == Action.REPLACE) { + } else if (action.isFillAction()) { if (args.hasExactlyOne() || action == Action.REPLACE) { while (args.has(2)) { args.get(); } return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE); + } else if (args.hasExactly(2) && (action == Action.CYLINDER || action == Action.HCYLINDER)) { + args.get(); + return args.tabCompleteDatatype(ForAxis.INSTANCE); } } else if (action == Action.EXPAND || action == Action.CONTRACT || action == Action.SHIFT) { if (args.hasExactlyOne()) { @@ -304,6 +342,10 @@ public List getLongDesc() { "> sel set/fill/s/f [block] - Completely fill all selections with a block.", "> sel walls/w [block] - Fill in the walls of the selection with a specified block.", "> sel shell/shl [block] - The same as walls, but fills in a ceiling and floor too.", + "> sel sphere/sph [block] - Fills the selection with a sphere bounded by the sides.", + "> sel hsphere/hsph [block] - The same as sphere, but hollow.", + "> sel cylinder/cyl [block] - Fills the selection with a cylinder bounded by the sides, oriented about the given axis. (default=y)", + "> sel hcylinder/hcyl [block] - The same as cylinder, but hollow.", "> sel cleararea/ca - Basically 'set air'.", "> sel replace/r - Replaces blocks with another block.", "> sel copy/cp - Copy the selected area relative to the specified or your position.", @@ -323,6 +365,10 @@ enum Action { SET("set", "fill", "s", "f"), WALLS("walls", "w"), SHELL("shell", "shl"), + SPHERE("sphere", "sph"), + HSPHERE("hsphere", "hsph"), + CYLINDER("cylinder", "cyl"), + HCYLINDER("hcylinder", "hcyl"), CLEARAREA("cleararea", "ca"), REPLACE("replace", "r"), EXPAND("expand", "ex"), @@ -354,6 +400,18 @@ public static String[] getAllNames() { } return names.toArray(new String[0]); } + + public final boolean isFillAction() { + return this == SET + || this == WALLS + || this == SHELL + || this == SPHERE + || this == HSPHERE + || this == CYLINDER + || this == HCYLINDER + || this == CLEARAREA + || this == REPLACE; + } } enum TransformTarget { diff --git a/src/main/java/baritone/command/defaults/SetCommand.java b/src/main/java/baritone/command/defaults/SetCommand.java index 99dac4ac1..0f4439cef 100644 --- a/src/main/java/baritone/command/defaults/SetCommand.java +++ b/src/main/java/baritone/command/defaults/SetCommand.java @@ -22,6 +22,7 @@ import baritone.api.Settings; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; +import baritone.api.command.datatypes.RelativeFile; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; @@ -29,8 +30,9 @@ import baritone.api.command.helpers.TabCompleteHelper; import baritone.api.utils.SettingsUtil; import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.ClickEvent; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; @@ -57,6 +59,18 @@ public void execute(String label, IArgConsumer args) throws CommandException { logDirect("Settings saved"); return; } + if (Arrays.asList("load", "ld").contains(arg)) { + String file = SETTINGS_DEFAULT_NAME; + if (args.hasAny()) { + file = args.getString(); + } + // reset to defaults + SettingsUtil.modifiedSettings(Baritone.settings()).forEach(Settings.Setting::reset); + // then load from disk + SettingsUtil.readAndApply(Baritone.settings(), file); + logDirect("Settings reloaded from " + file); + return; + } boolean viewModified = Arrays.asList("m", "mod", "modified").contains(arg); boolean viewAll = Arrays.asList("all", "l", "list").contains(arg); boolean paginate = viewModified || viewAll; @@ -65,7 +79,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { args.requireMax(1); List toPaginate = (viewModified ? SettingsUtil.modifiedSettings(Baritone.settings()) : Baritone.settings().allSettings).stream() - .filter(s -> !javaOnlySetting(s)) + .filter(s -> !s.isJavaOnly()) .filter(s -> s.getName().toLowerCase(Locale.US).contains(search.toLowerCase(Locale.US))) .sorted((s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(s1.getName(), s2.getName())) .collect(Collectors.toList()); @@ -129,7 +143,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { if (setting == null) { throw new CommandInvalidTypeException(args.consumed(), "a valid setting"); } - if (javaOnlySetting(setting)) { + if (setting.isJavaOnly()) { // ideally it would act as if the setting didn't exist // but users will see it in Settings.java or its javadoc // so at some point we have to tell them or they will see it as a bug @@ -147,7 +161,8 @@ public void execute(String label, IArgConsumer args) throws CommandException { throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting"); } //noinspection unchecked - ((Settings.Setting) setting).value ^= true; + Settings.Setting asBoolSetting = (Settings.Setting) setting; + asBoolSetting.value ^= true; logDirect(String.format( "Toggled setting %s to %s", setting.getName(), @@ -208,6 +223,9 @@ public Stream tabComplete(String label, IArgConsumer args) throws Comman .addToggleableSettings() .filterPrefix(args.getString()) .stream(); + } else if (Arrays.asList("ld", "load").contains(arg.toLowerCase(Locale.US))) { + // settings always use the directory of the main Minecraft instance + return RelativeFile.tabComplete(args, Minecraft.getInstance().gameDirectory.toPath().resolve("baritone").toFile()); } Settings.Setting setting = Baritone.settings().byLowerName.get(arg.toLowerCase(Locale.US)); if (setting != null) { @@ -227,7 +245,7 @@ public Stream tabComplete(String label, IArgConsumer args) throws Comman return new TabCompleteHelper() .addSettings() .sortAlphabetically() - .prepend("list", "modified", "reset", "toggle", "save") + .prepend("list", "modified", "reset", "toggle", "save", "load") .filterPrefix(arg) .stream(); } @@ -254,7 +272,9 @@ public List getLongDesc() { "> set reset all - Reset ALL SETTINGS to their defaults", "> set reset - Reset a setting to its default", "> set toggle - Toggle a boolean setting", - "> set save - Save all settings (this is automatic tho)" + "> set save - Save all settings (this is automatic tho)", + "> set load - Load settings from settings.txt", + "> set load [filename] - Load settings from another file in your minecraft/baritone" ); } } diff --git a/src/main/java/baritone/command/defaults/SurfaceCommand.java b/src/main/java/baritone/command/defaults/SurfaceCommand.java index d5e45bda2..a9c981cc3 100644 --- a/src/main/java/baritone/command/defaults/SurfaceCommand.java +++ b/src/main/java/baritone/command/defaults/SurfaceCommand.java @@ -24,11 +24,10 @@ import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; -import net.minecraft.world.level.block.AirBlock; - import java.util.Arrays; import java.util.List; import java.util.stream.Stream; +import net.minecraft.world.level.block.AirBlock; public class SurfaceCommand extends Command { @@ -38,13 +37,13 @@ protected SurfaceCommand(IBaritone baritone) { @Override public void execute(String label, IArgConsumer args) throws CommandException { - final BetterBlockPos playerPos = baritone.getPlayerContext().playerFeet(); - final int surfaceLevel = baritone.getPlayerContext().world().getSeaLevel(); - final int worldHeight = baritone.getPlayerContext().world().getHeight(); + final BetterBlockPos playerPos = ctx.playerFeet(); + final int surfaceLevel = ctx.world().getSeaLevel(); + final int worldHeight = ctx.world().getHeight(); // Ensure this command will not run if you are above the surface level and the block above you is air // As this would imply that your are already on the open surface - if (playerPos.getY() > surfaceLevel && mc.level.getBlockState(playerPos.above()).getBlock() instanceof AirBlock) { + if (playerPos.getY() > surfaceLevel && ctx.world().getBlockState(playerPos.above()).getBlock() instanceof AirBlock) { logDirect("Already at surface"); return; } @@ -54,7 +53,7 @@ public void execute(String label, IArgConsumer args) throws CommandException { for (int currentIteratedY = startingYPos; currentIteratedY < worldHeight; currentIteratedY++) { final BetterBlockPos newPos = new BetterBlockPos(playerPos.getX(), currentIteratedY, playerPos.getZ()); - if (!(mc.level.getBlockState(newPos).getBlock() instanceof AirBlock) && newPos.getY() > playerPos.getY()) { + if (!(ctx.world().getBlockState(newPos).getBlock() instanceof AirBlock) && newPos.getY() > playerPos.getY()) { Goal goal = new GoalBlock(newPos.above()); logDirect(String.format("Going to: %s", goal.toString())); baritone.getCustomGoalProcess().setGoalAndPath(goal); diff --git a/src/main/java/baritone/command/defaults/TunnelCommand.java b/src/main/java/baritone/command/defaults/TunnelCommand.java index 500e32cba..dec304a03 100644 --- a/src/main/java/baritone/command/defaults/TunnelCommand.java +++ b/src/main/java/baritone/command/defaults/TunnelCommand.java @@ -23,12 +23,11 @@ import baritone.api.command.exception.CommandException; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalStrictDirection; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; - import java.util.Arrays; import java.util.List; import java.util.stream.Stream; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; public class TunnelCommand extends Command { diff --git a/src/main/java/baritone/command/defaults/WaypointsCommand.java b/src/main/java/baritone/command/defaults/WaypointsCommand.java index 94e67c401..6e7f0a1e5 100644 --- a/src/main/java/baritone/command/defaults/WaypointsCommand.java +++ b/src/main/java/baritone/command/defaults/WaypointsCommand.java @@ -34,17 +34,17 @@ import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.MutableComponent; - import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; @@ -149,7 +149,11 @@ public void execute(String label, IArgConsumer args) throws CommandException { logDirect(component); } else if (action == Action.CLEAR) { args.requireMax(1); - IWaypoint.Tag tag = IWaypoint.Tag.getByName(args.getString()); + String name = args.getString(); + IWaypoint.Tag tag = IWaypoint.Tag.getByName(name); + if (tag == null) { + throw new CommandInvalidStateException("Invalid tag, \"" + name + "\""); + } IWaypoint[] waypoints = ForWaypoints.getWaypointsByTag(this.baritone, tag); for (IWaypoint waypoint : waypoints) { ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); diff --git a/src/main/java/baritone/command/manager/CommandManager.java b/src/main/java/baritone/command/manager/CommandManager.java index 4c33226d3..8712165f1 100644 --- a/src/main/java/baritone/command/manager/CommandManager.java +++ b/src/main/java/baritone/command/manager/CommandManager.java @@ -21,6 +21,7 @@ import baritone.api.IBaritone; import baritone.api.command.ICommand; import baritone.api.command.argument.ICommandArgument; +import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandUnhandledException; import baritone.api.command.exception.ICommandException; import baritone.api.command.helpers.TabCompleteHelper; @@ -153,9 +154,12 @@ private void execute() { private Stream tabComplete() { try { return this.command.tabComplete(this.label, this.args); + } catch (CommandException ignored) { + // NOP } catch (Throwable t) { - return Stream.empty(); + t.printStackTrace(); } + return Stream.empty(); } } } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index b4e12d4be..d716ff849 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -23,9 +23,13 @@ import baritone.api.event.listener.IEventBus; import baritone.api.event.listener.IGameEventListener; import baritone.api.utils.Helper; +import baritone.api.utils.Pair; +import baritone.cache.CachedChunk; import baritone.cache.WorldProvider; import baritone.utils.BlockStateInterface; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import java.util.List; @@ -60,6 +64,11 @@ public final void onTick(TickEvent event) { listeners.forEach(l -> l.onTick(event)); } + @Override + public void onPostTick(TickEvent event) { + listeners.forEach(l -> l.onPostTick(event)); + } + @Override public final void onPlayerUpdate(PlayerUpdateEvent event) { listeners.forEach(l -> l.onPlayerUpdate(event)); @@ -76,13 +85,10 @@ public void onPreTabComplete(TabCompleteEvent event) { } @Override - public final void onChunkEvent(ChunkEvent event) { + public void onChunkEvent(ChunkEvent event) { EventState state = event.getState(); ChunkEvent.Type type = event.getType(); - boolean isPostPopulate = state == EventState.POST - && (type == ChunkEvent.Type.POPULATE_FULL || type == ChunkEvent.Type.POPULATE_PARTIAL); - Level world = baritone.getPlayerContext().world(); // Whenever the server sends us to another dimension, chunks are unloaded @@ -92,7 +98,7 @@ public final void onChunkEvent(ChunkEvent event) { && type == ChunkEvent.Type.UNLOAD && world.getChunkSource().getChunk(event.getX(), event.getZ(), null, false) != null; - if (isPostPopulate || isPreUnload) { + if (event.isPostPopulate() || isPreUnload) { baritone.getWorldProvider().ifWorldLoaded(worldData -> { LevelChunk chunk = world.getChunk(event.getX(), event.getZ()); worldData.getCachedWorld().queueForPacking(chunk); @@ -103,6 +109,25 @@ public final void onChunkEvent(ChunkEvent event) { listeners.forEach(l -> l.onChunkEvent(event)); } + @Override + public void onBlockChange(BlockChangeEvent event) { + if (Baritone.settings().repackOnAnyBlockChange.value) { + final boolean keepingTrackOf = event.getBlocks().stream() + .map(Pair::second).map(BlockState::getBlock) + .anyMatch(CachedChunk.BLOCKS_TO_KEEP_TRACK_OF::contains); + + if (keepingTrackOf) { + baritone.getWorldProvider().ifWorldLoaded(worldData -> { + final Level world = baritone.getPlayerContext().world(); + ChunkPos pos = event.getChunkPos(); + worldData.getCachedWorld().queueForPacking(world.getChunk(pos.x, pos.z)); + }); + } + } + + listeners.forEach(l -> l.onBlockChange(event)); + } + @Override public final void onRenderPass(RenderEvent event) { listeners.forEach(l -> l.onRenderPass(event)); @@ -115,7 +140,7 @@ public final void onWorldEvent(WorldEvent event) { if (event.getState() == EventState.POST) { cache.closeWorld(); if (event.getWorld() != null) { - cache.initWorld(event.getWorld().dimension(), event.getWorld().dimensionType()); + cache.initWorld(event.getWorld()); } } diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index d2b75f481..b37b598ff 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -89,8 +89,7 @@ protected Optional calculate0(long primaryTimeout, long failureTimeout) { if (slowPath) { try { Thread.sleep(Baritone.settings().slowPathTimeDelayMS.value); - } catch (InterruptedException ignored) { - } + } catch (InterruptedException ignored) {} } PathNode currentNode = openSet.removeLowest(); mostRecentConsidered = currentNode; diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index f3df6fd08..1174bb5ee 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -22,6 +22,7 @@ import baritone.api.IBaritone; import baritone.api.pathing.movement.ActionCosts; import baritone.cache.WorldData; +import baritone.pathing.precompute.PrecomputedData; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import baritone.utils.pathing.BetterWorldBorder; @@ -31,8 +32,10 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EndPortalFrameBlock; import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; @@ -65,10 +68,13 @@ public class CalculationContext { public final boolean allowJumpAt256; public final boolean allowParkourAscend; public final boolean assumeWalkOnWater; + public boolean allowFallIntoLava; + public final int frostWalker; public final boolean allowDiagonalDescend; public final boolean allowDiagonalAscend; public final boolean allowDownward; - public final int maxFallHeightNoWater; + public int minFallHeight; + public int maxFallHeightNoWater; public final int maxFallHeightBucket; public final double waterWalkSpeed; public final double breakBlockAdditionalCost; @@ -77,17 +83,20 @@ public class CalculationContext { public final double walkOnWaterOnePenalty; public final BetterWorldBorder worldBorder; + public final PrecomputedData precomputedData; + public CalculationContext(IBaritone baritone) { this(baritone, false); } public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { + this.precomputedData = new PrecomputedData(); this.safeForThreadedUse = forUseOnAnotherThread; this.baritone = baritone; LocalPlayer player = baritone.getPlayerContext().player(); this.world = baritone.getPlayerContext().world(); - this.worldData = (WorldData) baritone.getWorldProvider().getCurrentWorld(); - this.bsi = new BlockStateInterface(world, worldData, forUseOnAnotherThread); + this.worldData = (WorldData) baritone.getPlayerContext().worldData(); + this.bsi = new BlockStateInterface(baritone.getPlayerContext(), forUseOnAnotherThread); this.toolSet = new ToolSet(player); this.hasThrowaway = !AltoClefSettings.getInstance().isInteractionPaused() && Baritone.settings().allowPlace.value && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway(); this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.value && Inventory.isHotbarSlot(player.getInventory().findSlotMatchingItem(STACK_BUCKET_WATER)) && world.dimension() != Level.NETHER; @@ -100,9 +109,12 @@ public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { this.allowJumpAt256 = Baritone.settings().allowJumpAt256.value; this.allowParkourAscend = Baritone.settings().allowParkourAscend.value; this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.value; + this.allowFallIntoLava = false; // Super secret internal setting for ElytraBehavior + this.frostWalker = EnchantmentHelper.getEnchantmentLevel(Enchantments.FROST_WALKER, baritone.getPlayerContext().player()); this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.value; this.allowDiagonalAscend = Baritone.settings().allowDiagonalAscend.value; this.allowDownward = Baritone.settings().allowDownward.value; + this.minFallHeight = 3; // Minimum fall height used by MovementFall this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.value; this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.value; int depth = EnchantmentHelper.getDepthStrider(player); @@ -164,7 +176,8 @@ public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { if (isPossiblyProtected(x, y, z)) { return COST_INF; } - if (AltoClefSettings.getInstance().shouldAvoidBreaking(new BlockPos(x, y, z))) { + if (AltoClefSettings.getInstance().shouldAvoidBreaking(new BlockPos(x, y, z)) + || current.getBlock() instanceof EndPortalFrameBlock) { return COST_INF; } return 1; diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index f671d359f..739c8ee89 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -25,13 +25,12 @@ import baritone.api.utils.input.Input; import baritone.behavior.PathingBehavior; import baritone.utils.BlockStateInterface; +import java.util.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.phys.AABB; -import java.util.*; - public abstract class Movement implements IMovement, MovementHelper { public static final Direction[] HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.DOWN}; @@ -163,7 +162,7 @@ protected boolean prepared(MovementState state) { if (!MovementHelper.canWalkThrough(ctx, blockPos)) { // can't break air, so don't try somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); - Optional reachable = RotationUtils.reachable(ctx.player(), blockPos, ctx.playerController().getBlockReachDistance()); + Optional reachable = RotationUtils.reachable(ctx, blockPos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 170147361..0c07cc666 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -27,16 +27,19 @@ import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.pathing.movement.MovementState.MovementTarget; +import baritone.pathing.precompute.Ternary; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.piston.MovingPistonBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.block.state.properties.StairsShape; import net.minecraft.world.level.material.*; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; @@ -46,6 +49,7 @@ import java.util.Optional; import static baritone.pathing.movement.Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP; +import static baritone.pathing.precompute.Ternary.*; /** * Static helpers for cost calculation @@ -55,8 +59,11 @@ public interface MovementHelper extends ActionCosts, Helper { static boolean avoidBreaking(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + if (bsi.get0(x, y + 1, z).getBlock() instanceof EndPortalFrameBlock) { + return true; + } if (AltoClefSettings.getInstance().shouldAvoidBreaking(new BlockPos(x, y, z))) return true; - if (!bsi.worldBorder.canPlaceAt(x, y)) { + if (!bsi.worldBorder.canPlaceAt(x, z)) { return true; } Block b = state.getBlock(); @@ -84,6 +91,19 @@ static boolean avoidAdjacentBreaking(BlockStateInterface bsi, int x, int y, int && FallingBlock.isFree(bsi.get0(x, y - 1, z))) { // and if it would fall (i.e. it's unsupported) return true; // dont break a block that is adjacent to unsupported gravel because it can cause really weird stuff } + // only pure liquids for now + // waterlogged blocks can have closed bottom sides and such + if (block instanceof LiquidBlock) { + if (directlyAbove || Baritone.settings().strictLiquidCheck.value) { + return true; + } + int level = state.getValue(LiquidBlock.LEVEL); + if (level == 0) { + return true; // source blocks like to flow horizontally + } + // everything else will prefer flowing down + return !(bsi.get0(x, y - 1, z).getBlock() instanceof LiquidBlock); // assume everything is in a static state + } return !state.getFluidState().isEmpty(); } @@ -95,42 +115,96 @@ static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z) { return canWalkThrough(bsi, x, y, z, bsi.get0(x, y, z)); } + static boolean canWalkThrough(CalculationContext context, int x, int y, int z, BlockState state) { + return context.precomputedData.canWalkThrough(context.bsi, x, y, z, state); + } + + static boolean canWalkThrough(CalculationContext context, int x, int y, int z) { + return context.precomputedData.canWalkThrough(context.bsi, x, y, z, context.get(x, y, z)); + } + static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + Ternary canWalkThrough = canWalkThroughBlockState(state); Block block = state.getBlock(); - if (block instanceof AirBlock) { // early return for most common case - return true; - } - if (block instanceof BaseFireBlock || block == Blocks.TRIPWIRE || block == Blocks.COBWEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof AbstractSkullBlock || block == Blocks.BUBBLE_COLUMN || block instanceof ShulkerBoxBlock || block instanceof SlabBlock || block instanceof TrapDoorBlock || block == Blocks.HONEY_BLOCK || block == Blocks.END_ROD || block == Blocks.SWEET_BERRY_BUSH || block == Blocks.POINTED_DRIPSTONE || block instanceof AmethystClusterBlock || block instanceof AzaleaBlock) { - return false; - } + BlockState up = bsi.get0(x, y + 1, z); if (AltoClefSettings.getInstance().canSwimThroughLava() && block == Blocks.LAVA) { - BlockState up = bsi.get0(x, y + 1, z); return up.getFluidState().isEmpty(); } - if (block == Blocks.BIG_DRIPLEAF) { + if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(x, y, z)) { return false; } - if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(x, y, z)) { + if (canWalkThrough == YES) { + return true; + } + if (canWalkThrough == NO) { return false; } + return canWalkThroughPosition(bsi, x, y, z, state); + } + + static Ternary canWalkThroughBlockState(BlockState state) { + Block block = state.getBlock(); + if (block instanceof AirBlock) { + return YES; + } + if (block instanceof BaseFireBlock || block == Blocks.TRIPWIRE || block == Blocks.COBWEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof AbstractSkullBlock || block == Blocks.BUBBLE_COLUMN || block instanceof ShulkerBoxBlock || block instanceof SlabBlock || block instanceof TrapDoorBlock || block == Blocks.HONEY_BLOCK || block == Blocks.END_ROD || block == Blocks.SWEET_BERRY_BUSH || block == Blocks.POINTED_DRIPSTONE || block instanceof AmethystClusterBlock || block instanceof AzaleaBlock) { + return NO; + } + if (block == Blocks.BIG_DRIPLEAF) { + return NO; + } if (block == Blocks.POWDER_SNOW) { - return false; + return NO; } if (Baritone.settings().blocksToAvoid.value.contains(block)) { - return false; + return NO; } if (block instanceof DoorBlock || block instanceof FenceGateBlock) { - // Because there's no nice method in vanilla to check if a door is openable or not, we just have to assume - // that anything that isn't an iron door isn't openable, ignoring that some doors introduced in mods can't - // be opened by just interacting. - return block != Blocks.IRON_DOOR; + // TODO this assumes that all doors in all mods are openable + if (block == Blocks.IRON_DOOR) { + return NO; + } + return YES; + } + if (block instanceof CarpetBlock) { + return MAYBE; + } + if (block instanceof SnowLayerBlock) { + // snow layers cached as the top layer of a packed chunk have no metadata, we can't make a decision based on their depth here + // it would otherwise make long distance pathing through snowy biomes impossible + return MAYBE; } - if (block instanceof WoolCarpetBlock) { + FluidState fluidState = state.getFluidState(); + if (!fluidState.isEmpty()) { + if (fluidState.getType().getAmount(fluidState) != 8) { + return NO; + } else { + return MAYBE; + } + } + if (block instanceof CauldronBlock) { + return NO; + } + try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information + if (state.isPathfindable(null, null, PathComputationType.LAND)) { + return YES; + } else { + return NO; + } + } catch (Throwable exception) { + System.out.println("The block " + state.getBlock().getName().getString() + " requires a special case due to the exception " + exception.getMessage()); + return MAYBE; + } + } + + static boolean canWalkThroughPosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + Block block = state.getBlock(); + + if (block instanceof CarpetBlock) { return canWalkOn(bsi, x, y - 1, z); } + if (block instanceof SnowLayerBlock) { - // we've already checked doors and fence gates - // so the only remaining dynamic isPassables are snow and trapdoor // if they're cached as a top block, we don't know their metadata // default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) if (!bsi.worldContainsLoadedChunk(x, z)) { @@ -145,55 +219,33 @@ static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, Bloc return canWalkOn(bsi, x, y - 1, z); } - if (isFlowing(x, y, z, state, bsi)) { - return false; // Don't walk through flowing liquids - } FluidState fluidState = state.getFluidState(); - if (fluidState.getType() instanceof WaterFluid) { + if (!fluidState.isEmpty()) { + if (isFlowing(x, y, z, state, bsi)) { + return false; + } + // Everything after this point has to be a special case as it relies on the water not being flowing, which means a special case is needed. if (Baritone.settings().assumeWalkOnWater.value) { return false; } + BlockState up = bsi.get0(x, y + 1, z); if (!up.getFluidState().isEmpty() || up.getBlock() instanceof WaterlilyBlock) { return false; } - return true; - } - if (block instanceof CauldronBlock) { - return false; + return fluidState.getType() instanceof WaterFluid; } + // every block that overrides isPassable with anything more complicated than a "return true;" or "return false;" // has already been accounted for above // therefore it's safe to not construct a blockpos from our x, y, z ints and instead just pass null return state.isPathfindable(bsi.access, BlockPos.ZERO, PathComputationType.LAND); // workaround for future compatibility =P } - /** - * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), - * not including water, and not including ladders or vines or cobwebs (they slow us down) - * - * @param context Calculation context to provide block state lookup - * @param x The block's x position - * @param y The block's y position - * @param z The block's z position - * @return Whether or not the block at the specified position - */ - static boolean fullyPassable(CalculationContext context, int x, int y, int z) { - return fullyPassable( - context.bsi.access, - context.bsi.isPassableBlockPos.set(x, y, z), - context.bsi.get0(x, y, z) - ); - } - - static boolean fullyPassable(IPlayerContext ctx, BlockPos pos) { - return fullyPassable(ctx.world(), pos, ctx.world().getBlockState(pos)); - } - - static boolean fullyPassable(BlockGetter access, BlockPos pos, BlockState state) { + static Ternary fullyPassableBlockState(BlockState state) { Block block = state.getBlock(); if (block instanceof AirBlock) { // early return for most common case - return true; + return YES; } // exceptions - blocks that are isPassable true, but we can't actually jump through if (block instanceof BaseFireBlock @@ -211,13 +263,52 @@ static boolean fullyPassable(BlockGetter access, BlockPos pos, BlockState state) || block instanceof EndPortalBlock || block instanceof SkullBlock || block instanceof ShulkerBoxBlock) { - return false; + return NO; + } + // door, fence gate, liquid, trapdoor have been accounted for, nothing else uses the world or pos parameters + // at least in 1.12.2 vanilla, that is..... + try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information + if (state.isPathfindable(null, null, PathComputationType.LAND)) { + return YES; + } else { + return NO; + } + } catch (Throwable exception) { + // see PR #1087 for why + System.out.println("The block " + state.getBlock().getName().getString() + " requires a special case due to the exception " + exception.getMessage()); + return MAYBE; } + } + + /** + * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), + * not including water, and not including ladders or vines or cobwebs (they slow us down) + */ + static boolean fullyPassable(CalculationContext context, int x, int y, int z) { + return fullyPassable(context, x, y, z, context.get(x, y, z)); + } + + static boolean fullyPassable(CalculationContext context, int x, int y, int z, BlockState state) { + return context.precomputedData.fullyPassable(context.bsi, x, y, z, state); + } + + static boolean fullyPassable(IPlayerContext ctx, BlockPos pos) { + BlockState state = ctx.world().getBlockState(pos); + Ternary fullyPassable = fullyPassableBlockState(state); if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(pos)) { return false; } - // door, fence gate, liquid, trapdoor have been accounted for, nothing else uses the world or pos parameters - return state.isPathfindable(access, pos, PathComputationType.LAND); + if (fullyPassable == YES) { + return true; + } + if (fullyPassable == NO) { + return false; + } + return fullyPassablePosition(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ(), state); // meh + } + + static boolean fullyPassablePosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + return state.isPathfindable(bsi.access, bsi.isPassableBlockPos.set(x, y, z), PathComputationType.LAND); } static boolean isReplaceable(int x, int y, int z, BlockState state, BlockStateInterface bsi) { @@ -246,7 +337,7 @@ static boolean isReplaceable(int x, int y, int z, BlockState state, BlockStateIn if (block == Blocks.LARGE_FERN || block == Blocks.TALL_GRASS) { return true; } - return state.getMaterial().isReplaceable(); + return state.canBeReplaced(); } @Deprecated @@ -307,6 +398,7 @@ static boolean avoidWalkingInto(BlockState state) { || block == Blocks.CACTUS || block == Blocks.SWEET_BERRY_BUSH || block instanceof BaseFireBlock + || block instanceof EndPortalFrameBlock || block == Blocks.END_PORTAL || block == Blocks.COBWEB || block == Blocks.BUBBLE_COLUMN; @@ -316,6 +408,8 @@ static boolean avoidWalkingInto(BlockState state) { * Can I walk on this block without anything weird happening like me falling * through? Includes water because we know that we automatically jump on * water + *

+ * If changing something in this function remember to also change it in precomputed data * * @param bsi Block state provider * @param x The block's x position @@ -325,38 +419,78 @@ static boolean avoidWalkingInto(BlockState state) { * @return Whether or not the specified block can be walked on */ static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, BlockState state) { - Block block = state.getBlock(); + Ternary canWalkOn = canWalkOnBlockState(state); if (AltoClefSettings.getInstance().canWalkOnForce(x, y, z)) return true; if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(x, y + 1, z)) return false; - if (block instanceof AirBlock || block == Blocks.MAGMA_BLOCK || block == Blocks.BUBBLE_COLUMN || block == Blocks.HONEY_BLOCK) { - // early return for most common case (air) - // plus magma, which is a normal cube but it hurts you + if (canWalkOn == YES) { + return true; + } + if (canWalkOn == NO) { return false; } - if (isBlockNormalCube(state)) { - return true; + return canWalkOnPosition(bsi, x, y, z, state); + } + + static Ternary canWalkOnBlockState(BlockState state) { + Block block = state.getBlock(); + //Extra blocks we may want to walk on. + if (block instanceof EndPortalFrameBlock) { + return YES; + } + if (block == Blocks.END_PORTAL && AltoClefSettings.getInstance().isCanWalkOnEndPortal()) { + return YES; + } + //***************************************** + if (isBlockNormalCube(state) && block != Blocks.MAGMA_BLOCK && block != Blocks.BUBBLE_COLUMN && block != Blocks.HONEY_BLOCK) { + return YES; } if (block instanceof AzaleaBlock) { - return true; + return YES; } if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this - return true; + return YES; } if (block == Blocks.FARMLAND || block == Blocks.DIRT_PATH) { - return true; + return YES; } if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) { - return true; + return YES; + } + if (block == Blocks.GLASS || block instanceof StainedGlassBlock) { + return YES; + } + if (block instanceof StairBlock) { + return YES; } + if (isWater(state)) { + return MAYBE; + } + if (MovementHelper.isLava(state) && Baritone.settings().assumeWalkOnLava.value) { + return MAYBE; + } + if (block instanceof SlabBlock) { + if (!Baritone.settings().allowWalkOnBottomSlab.value) { + if (state.getValue(SlabBlock.TYPE) != SlabType.BOTTOM) { + return YES; + } + return NO; + } + return YES; + } + return NO; + } + + static boolean canWalkOnPosition(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + Block block = state.getBlock(); if (isWater(state)) { // 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 BlockState upState = bsi.get0(x, y + 1, z); Block up = upState.getBlock(); - if (up == Blocks.LILY_PAD || up instanceof WoolCarpetBlock) { + if (up == Blocks.LILY_PAD || up instanceof CarpetBlock) { return true; } - if (isFlowing(x, y, z, state, bsi) || upState.getFluidState().getType() == Fluids.FLOWING_WATER) { + if (MovementHelper.isFlowing(x, y, z, state, bsi) || upState.getFluidState().getType() == Fluids.FLOWING_WATER) { // the only scenario in which we can walk on flowing water is if it's under still water with jesus off return isWater(upState) && !Baritone.settings().assumeWalkOnWater.value; } @@ -364,19 +498,20 @@ static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, BlockStat // if assumeWalkOnWater is off, we can only walk on water if there is water above it return isWater(upState) ^ Baritone.settings().assumeWalkOnWater.value; } - if (Baritone.settings().assumeWalkOnLava.value && isLava(state) && !isFlowing(x, y, z, state, bsi)) { - return true; - } - if (block == Blocks.GLASS || block instanceof StainedGlassBlock) { - return true; - } - if (block instanceof SlabBlock) { - if (!Baritone.settings().allowWalkOnBottomSlab.value) { - return state.getValue(SlabBlock.TYPE) != SlabType.BOTTOM; - } + + if (MovementHelper.isLava(state) && !MovementHelper.isFlowing(x, y, z, state, bsi) && Baritone.settings().assumeWalkOnLava.value) { // if we get here it means that assumeWalkOnLava must be true, so put it last return true; } - return block instanceof StairBlock; + + return false; // If we don't recognise it then we want to just return false to be safe. + } + + static boolean canWalkOn(CalculationContext context, int x, int y, int z, BlockState state) { + return context.precomputedData.canWalkOn(context.bsi, x, y, z, state); + } + + static boolean canWalkOn(CalculationContext context, int x, int y, int z) { + return canWalkOn(context, x, y, z, context.get(x, y, z)); } static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos, BlockState state) { @@ -395,6 +530,61 @@ static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z) { return canWalkOn(bsi, x, y, z, bsi.get0(x, y, z)); } + static boolean canUseFrostWalker(CalculationContext context, BlockState state) { + return context.frostWalker != 0 + && state == FrostedIceBlock.meltsInto() + && ((Integer) state.getValue(LiquidBlock.LEVEL)) == 0; + } + + static boolean canUseFrostWalker(IPlayerContext ctx, BlockPos pos) { + BlockState state = BlockStateInterface.get(ctx, pos); + return EnchantmentHelper.hasFrostWalker(ctx.player()) + && state == FrostedIceBlock.meltsInto() + && ((Integer) state.getValue(LiquidBlock.LEVEL)) == 0; + } + + /** + * If movements make us stand/walk on this block, will it have a top to walk on? + */ + static boolean mustBeSolidToWalkOn(CalculationContext context, int x, int y, int z, BlockState state) { + Block block = state.getBlock(); + if (block == Blocks.LADDER || block == Blocks.VINE) { + return false; + } + if (!state.getFluidState().isEmpty()) { + // used for frostwalker so only includes blocks where we are still on ground when leaving them to any side + if (block instanceof SlabBlock) { + if (state.getValue(SlabBlock.TYPE) != SlabType.BOTTOM) { + return true; + } + } else if (block instanceof StairBlock) { + if (state.getValue(StairBlock.HALF) == Half.TOP) { + return true; + } + StairsShape shape = state.getValue(StairBlock.SHAPE); + if (shape == StairsShape.INNER_LEFT || shape == StairsShape.INNER_RIGHT) { + return true; + } + } else if (block instanceof TrapDoorBlock) { + if (!state.getValue(TrapDoorBlock.OPEN) && state.getValue(TrapDoorBlock.HALF) == Half.TOP) { + return true; + } + } else if (block == Blocks.SCAFFOLDING) { + return true; + } else if (block instanceof LeavesBlock) { + return true; + } + if (context.assumeWalkOnWater) { + return false; + } + Block blockAbove = context.getBlock(x, y + 1, z); + if (blockAbove instanceof LiquidBlock) { + return false; + } + } + return true; + } + static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z) { return canPlaceAgainst(bsi, x, y, z, bsi.get0(x, y, z)); } @@ -424,7 +614,7 @@ static double getMiningDurationTicks(CalculationContext context, int x, int y, i static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, BlockState state, boolean includeFalling) { Block block = state.getBlock(); - if (!canWalkThrough(context.bsi, x, y, z, state)) { + if (!canWalkThrough(context, x, y, z, state)) { if (!state.getFluidState().isEmpty()) { return COST_INF; } @@ -487,9 +677,9 @@ static void switchToBestToolFor(IPlayerContext ctx, BlockState b, ToolSet ts, bo static void moveTowards(IPlayerContext ctx, MovementState state, BlockPos pos) { state.setTarget(new MovementTarget( - new Rotation(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), + RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(pos), - ctx.playerRotations()).getYaw(), ctx.player().getXRot()), + ctx.playerRotations()).withPitch(ctx.playerRotations().getPitch()), false )).setInput(Input.MOVE_FORWARD, true); } @@ -599,7 +789,8 @@ static PlaceResult attemptToPlaceABlock(MovementState state, IBaritone baritone, double faceY = (placeAt.getY() + against1.getY() + 0.5D) * 0.5D; double faceZ = (placeAt.getZ() + against1.getZ() + 1.0D) * 0.5D; Rotation place = RotationUtils.calcRotationFromVec3d(wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.playerHead(), new Vec3(faceX, faceY, faceZ), ctx.playerRotations()); - HitResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance(), wouldSneak); + Rotation actual = baritone.getLookBehavior().getAimProcessor().peekRotation(place); + HitResult res = RayTraceUtils.rayTraceTowards(ctx.player(), actual, ctx.playerController().getBlockReachDistance(), wouldSneak); if (res != null && res.getType() == HitResult.Type.BLOCK && ((BlockHitResult) res).getBlockPos().equals(against1) && ((BlockHitResult) res).getBlockPos().relative(((BlockHitResult) res).getDirection()).equals(placeAt)) { state.setTarget(new MovementTarget(place, true)); found = true; @@ -634,14 +825,14 @@ static PlaceResult attemptToPlaceABlock(MovementState state, IBaritone baritone, return PlaceResult.NO_OPTION; } + enum PlaceResult { + READY_TO_PLACE, ATTEMPTING, NO_OPTION; + } + static boolean isTransparent(Block b) { return b instanceof AirBlock || b == Blocks.LAVA || b == Blocks.WATER; } - - enum PlaceResult { - READY_TO_PLACE, ATTEMPTING, NO_OPTION; - } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 016b1d31c..717cd2e70 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -28,13 +28,12 @@ import baritone.pathing.movement.MovementState; import baritone.utils.BlockStateInterface; import com.google.common.collect.ImmutableSet; +import java.util.Set; import net.minecraft.core.Direction; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FallingBlock; import net.minecraft.world.level.block.state.BlockState; -import java.util.Set; - public class MovementAscend extends Movement { private int ticksWithoutPlacement = 0; @@ -68,7 +67,7 @@ protected Set calculateValidPositions() { public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { BlockState toPlace = context.get(destX, y, destZ); double additionalPlacementCost = 0; - if (!MovementHelper.canWalkOn(context.bsi, destX, y, destZ, toPlace)) { + if (!MovementHelper.canWalkOn(context, destX, y, destZ, toPlace)) { additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ, toPlace); if (additionalPlacementCost >= COST_INF) { return COST_INF; @@ -94,7 +93,7 @@ public static double cost(CalculationContext context, int x, int y, int z, int d } } BlockState srcUp2 = context.get(x, y + 2, z); // used lower down anyway - if (context.get(x, y + 3, z).getBlock() instanceof FallingBlock && (MovementHelper.canWalkThrough(context.bsi, x, y + 1, z) || !(srcUp2.getBlock() instanceof FallingBlock))) {//it would fall on us and possibly suffocate us + if (context.get(x, y + 3, z).getBlock() instanceof FallingBlock && (MovementHelper.canWalkThrough(context, x, y + 1, z) || !(srcUp2.getBlock() instanceof FallingBlock))) {//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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 2db0b4ee4..2ddfa85cb 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -21,7 +21,6 @@ import baritone.api.IBaritone; 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.input.Input; import baritone.pathing.movement.CalculationContext; @@ -31,7 +30,6 @@ import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import com.google.common.collect.ImmutableSet; -import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -44,6 +42,7 @@ public class MovementDescend extends Movement { private int numTicks = 0; + public boolean forceSafeMode = false; public MovementDescend(IBaritone baritone, BetterBlockPos start, BetterBlockPos end) { super(baritone, start, end, new BetterBlockPos[]{end.above(2), end.above(), end}, end.below()); @@ -53,6 +52,14 @@ public MovementDescend(IBaritone baritone, BetterBlockPos start, BetterBlockPos public void reset() { super.reset(); numTicks = 0; + forceSafeMode = false; + } + + /** + * Called by PathExecutor if needing safeMode can only be detected with knowledge about the next movement + */ + public void forceSafeMode() { + forceSafeMode = true; } @Override @@ -102,7 +109,7 @@ public static void cost(CalculationContext context, int x, int y, int z, int des //C, D, etc determine the length of the fall BlockState below = context.get(destX, y - 2, destZ); - if (!MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ, below)) { + if (!MovementHelper.canWalkOn(context, destX, y - 2, destZ, below)) { dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res); return; } @@ -110,6 +117,9 @@ public static void cost(CalculationContext context, int x, int y, int z, int des if (destDown.getBlock() == Blocks.LADDER || destDown.getBlock() == Blocks.VINE) { return; } + if (MovementHelper.canUseFrostWalker(context, destDown)) { // no need to check assumeWalkOnWater + return; // the water will freeze when we try to walk into it + } // 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) double walk = WALK_OFF_BLOCK_COST; @@ -131,7 +141,7 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, // and potentially replace the water we're going to fall into return false; } - if (!MovementHelper.canWalkThrough(context.bsi, destX, y - 2, destZ, below)) { + if (!MovementHelper.canWalkThrough(context, destX, y - 2, destZ, below)) { return false; } double costSoFar = 0; @@ -143,11 +153,12 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, // this check prevents it from getting the block at y=-1 and crashing return false; } + boolean reachedMinimum = fallHeight >= context.minFallHeight; BlockState ontoBlock = context.get(destX, newY, destZ); int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar; - if (MovementHelper.isWater(ontoBlock)) { - if (!MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) { + if (reachedMinimum && MovementHelper.isWater(ontoBlock)) { + if (!MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { return false; } if (context.assumeWalkOnWater) { @@ -156,7 +167,7 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, if (MovementHelper.isFlowing(destX, newY, destZ, ontoBlock, context.bsi)) { return false; // TODO flowing check required here? } - if (!MovementHelper.canWalkOn(context.bsi, destX, newY - 1, destZ)) { + if (!MovementHelper.canWalkOn(context, destX, newY - 1, destZ)) { // we could punch right through the water into something else return false; } @@ -167,6 +178,14 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, res.cost = tentativeCost;// TODO incorporate water swim up cost? return false; } + if (reachedMinimum && context.allowFallIntoLava && MovementHelper.isLava(ontoBlock)) { + // found a fall into lava + res.x = destX; + res.y = newY; + res.z = destZ; + res.cost = tentativeCost; + return false; + } if (unprotectedFallHeight <= 11 && (ontoBlock.getBlock() == Blocks.VINE || ontoBlock.getBlock() == Blocks.LADDER)) { // if fall height is greater than or equal to 11, we don't actually grab on to vines or ladders. the more you know // this effectively "resets" our falling speed @@ -175,16 +194,16 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, effectiveStartHeight = newY; continue; } - if (MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) { + if (MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { continue; } - if (!MovementHelper.canWalkOn(context.bsi, destX, newY, destZ, ontoBlock)) { + if (!MovementHelper.canWalkOn(context, destX, newY, destZ, ontoBlock)) { return false; } if (MovementHelper.isBottomSlab(ontoBlock)) { return false; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect } - if (unprotectedFallHeight <= context.maxFallHeightNoWater + 1) { + if (reachedMinimum && unprotectedFallHeight <= context.maxFallHeightNoWater + 1) { // fallHeight = 4 means onto.up() is 3 blocks down, which is the max res.x = destX; res.y = newY + 1; @@ -192,7 +211,7 @@ public static boolean dynamicFallCost(CalculationContext context, int x, int y, res.cost = tentativeCost; return false; } - if (context.hasWaterBucket && unprotectedFallHeight <= context.maxFallHeightBucket + 1) { + if (reachedMinimum && context.hasWaterBucket && unprotectedFallHeight <= context.maxFallHeightBucket + 1) { res.x = destX; res.y = newY + 1;// this is the block we're falling onto, so dest is +1 res.z = destZ; @@ -223,11 +242,10 @@ public MovementState updateState(MovementState state) { if (safeMode()) { double destX = (src.getX() + 0.5) * 0.17 + (dest.getX() + 0.5) * 0.83; double destZ = (src.getZ() + 0.5) * 0.17 + (dest.getZ() + 0.5) * 0.83; - LocalPlayer player = ctx.player(); state.setTarget(new MovementState.MovementTarget( - new Rotation(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), + RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3(destX, dest.getY(), destZ), - new Rotation(player.getYRot(), player.getXRot())).getYaw(), player.getXRot()), + ctx.playerRotations()).withPitch(ctx.playerRotations().getPitch()), false )).setInput(Input.MOVE_FORWARD, true); return state; @@ -249,6 +267,9 @@ public MovementState updateState(MovementState state) { } public boolean safeMode() { + if (forceSafeMode) { + return true; + } // (dest - src) + dest is offset 1 more in the same direction // so it's the block we'd need to worry about running into if we decide to sprint straight through this descend BlockPos into = dest.subtract(src.below()).offset(dest); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index 3637ca7ff..cf387d5c9 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -29,6 +29,9 @@ import baritone.utils.BlockStateInterface; import baritone.utils.pathing.MutableMoveResult; import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -36,10 +39,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - public class MovementDiagonal extends Movement { private static final double SQRT_2 = Math.sqrt(2); @@ -60,7 +59,7 @@ private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPo @Override protected boolean safeToCancel(MovementState state) { //too simple. backfill does not work after cornering with this - //return MovementHelper.canWalkOn(ctx, ctx.playerFeet().down()); + //return context.precomputedData.canWalkOn(ctx, ctx.playerFeet().down()); LocalPlayer player = ctx.player(); double offset = 0.25; double x = player.position().x; @@ -110,40 +109,49 @@ protected Set calculateValidPositions() { } public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { - if (!MovementHelper.canWalkThrough(context.bsi, destX, y + 1, destZ)) { + if (!MovementHelper.canWalkThrough(context, destX, y + 1, destZ)) { return; } BlockState destInto = context.get(destX, y, destZ); + BlockState fromDown; boolean ascend = false; BlockState destWalkOn; boolean descend = false; - if (!MovementHelper.canWalkThrough(context.bsi, destX, y, destZ, destInto)) { + boolean frostWalker = false; + if (!MovementHelper.canWalkThrough(context, destX, y, destZ, destInto)) { ascend = true; - if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context.bsi, x, y + 2, z) || !MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi, destX, y + 2, destZ)) { + if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context, x, y + 2, z) || !MovementHelper.canWalkOn(context, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context, destX, y + 2, destZ)) { return; } destWalkOn = destInto; + fromDown = context.get(x, y - 1, z); } else { destWalkOn = context.get(destX, y - 1, destZ); - if (!MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) { + fromDown = context.get(x, y - 1, z); + boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y - 1, z, fromDown); + frostWalker = standingOnABlock && MovementHelper.canUseFrostWalker(context, destWalkOn); + if (!frostWalker && !MovementHelper.canWalkOn(context, destX, y - 1, destZ, destWalkOn)) { descend = true; - if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) { + if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context, destX, y - 1, destZ, destWalkOn)) { return; } } + frostWalker &= !context.assumeWalkOnWater; // do this after checking for descends because jesus can't prevent the water from freezing, it just prevents us from relying on the water freezing } double multiplier = WALK_ONE_BLOCK_COST; // For either possible soul sand, that affects half of our walking if (destWalkOn.getBlock() == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; + } else if (frostWalker) { + // frostwalker lets us walk on water without the penalty } else if (destWalkOn.getBlock() == Blocks.WATER) { multiplier += context.walkOnWaterOnePenalty * SQRT_2; } - Block fromDown = context.get(x, y - 1, z).getBlock(); - if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { + Block fromDownBlock = fromDown.getBlock(); + if (fromDownBlock == Blocks.LADDER || fromDownBlock == Blocks.VINE) { return; } - if (fromDown == Blocks.SOUL_SAND) { + if (fromDownBlock == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } BlockState cuttingOver1 = context.get(x, y - 1, destZ); @@ -170,17 +178,17 @@ public static void cost(CalculationContext context, int x, int y, int z, int des BlockState pb0 = context.get(x, y, destZ); BlockState pb2 = context.get(destX, y, z); if (ascend) { - boolean ATop = MovementHelper.canWalkThrough(context.bsi, x, y + 2, destZ); - boolean AMid = MovementHelper.canWalkThrough(context.bsi, x, y + 1, destZ); - boolean ALow = MovementHelper.canWalkThrough(context.bsi, x, y, destZ, pb0); - boolean BTop = MovementHelper.canWalkThrough(context.bsi, destX, y + 2, z); - boolean BMid = MovementHelper.canWalkThrough(context.bsi, destX, y + 1, z); - boolean BLow = MovementHelper.canWalkThrough(context.bsi, destX, y, z, pb2); + boolean ATop = MovementHelper.canWalkThrough(context, x, y + 2, destZ); + boolean AMid = MovementHelper.canWalkThrough(context, x, y + 1, destZ); + boolean ALow = MovementHelper.canWalkThrough(context, x, y, destZ, pb0); + boolean BTop = MovementHelper.canWalkThrough(context, destX, y + 2, z); + boolean BMid = MovementHelper.canWalkThrough(context, destX, y + 1, z); + boolean BLow = MovementHelper.canWalkThrough(context, destX, y, z, pb2); if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option || MovementHelper.avoidWalkingInto(pb0) // bad || MovementHelper.avoidWalkingInto(pb2) // bad - || (ATop && AMid && MovementHelper.canWalkOn(context.bsi, x, y, destZ, pb0)) // we could just ascend - || (BTop && BMid && MovementHelper.canWalkOn(context.bsi, destX, y, z, pb2)) // we could just ascend + || (ATop && AMid && MovementHelper.canWalkOn(context, x, y, destZ, pb0)) // we could just ascend + || (BTop && BMid && MovementHelper.canWalkOn(context, destX, y, z, pb2)) // we could just ascend || (!ATop && AMid && ALow) // head bonk A || (!BTop && BMid && BLow)) { // head bonk B return; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index 45d18d1c7..a37df38b6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -25,12 +25,11 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import com.google.common.collect.ImmutableSet; +import java.util.Set; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; -import java.util.Set; - public class MovementDownward extends Movement { private int numTicks = 0; @@ -59,7 +58,7 @@ public static double cost(CalculationContext context, int x, int y, int z) { if (!context.allowDownward) { return COST_INF; } - if (!MovementHelper.canWalkOn(context.bsi, x, y - 2, z)) { + if (!MovementHelper.canWalkOn(context, x, y - 2, z)) { return COST_INF; } BlockState down = context.get(x, y - 1, z); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index f41504520..441bdf993 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -78,16 +78,10 @@ protected Set calculateValidPositions() { return set; } - private static BetterBlockPos[] buildPositionsToBreak(BetterBlockPos src, BetterBlockPos dest) { - BetterBlockPos[] toBreak; - int diffX = src.getX() - dest.getX(); - int diffZ = src.getZ() - dest.getZ(); - int diffY = Math.abs(src.getY() - dest.getY()); - toBreak = new BetterBlockPos[diffY + 2]; - for (int i = 0; i < toBreak.length; i++) { - toBreak[i] = new BetterBlockPos(src.getX() - diffX, src.getY() + 1 - i, src.getZ() - diffZ); - } - return toBreak; + private boolean willPlaceBucket() { + CalculationContext context = new CalculationContext(baritone); + MutableMoveResult result = new MutableMoveResult(); + return MovementDescend.dynamicFallCost(context, src.x, src.y, src.z, dest.x, dest.z, 0, context.get(dest.x, src.y - 2, dest.z), result); } @Override @@ -109,7 +103,7 @@ public MovementState updateState(MovementState state) { return state.setStatus(MovementStatus.UNREACHABLE); } - if (ctx.player().position().y - dest.getY() < ctx.playerController().getBlockReachDistance() && !ctx.player().isOnGround()) { + if (ctx.player().position().y - dest.getY() < ctx.playerController().getBlockReachDistance() && !ctx.player().onGround()) { ctx.player().getInventory().selected = ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_WATER); targetRotation = new Rotation(toDest.getYaw(), 90.0F); @@ -144,7 +138,7 @@ public MovementState updateState(MovementState state) { } Vec3 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(ctx.player().position().x + ctx.player().getDeltaMovement().x - destCenter.x) > 0.1 || Math.abs(ctx.player().position().z + ctx.player().getDeltaMovement().z - destCenter.z) > 0.1) { - if (!ctx.player().isOnGround() && Math.abs(ctx.player().getDeltaMovement().y) > 0.4) { + if (!ctx.player().onGround() && Math.abs(ctx.player().getDeltaMovement().y) > 0.4) { state.setInput(Input.SNEAK, true); } state.setInput(Input.MOVE_FORWARD, true); @@ -156,7 +150,7 @@ public MovementState updateState(MovementState state) { double dist = Math.abs(avoid.getX() * (destCenter.x - avoid.getX() / 2.0 - ctx.player().position().x)) + Math.abs(avoid.getZ() * (destCenter.z - avoid.getZ() / 2.0 - ctx.player().position().z)); if (dist < 0.6) { state.setInput(Input.MOVE_FORWARD, true); - } else if (!ctx.player().isOnGround()) { + } else if (!ctx.player().onGround()) { state.setInput(Input.SNEAK, false); } } @@ -177,12 +171,6 @@ private Direction avoid() { return null; } - private boolean willPlaceBucket() { - CalculationContext context = new CalculationContext(baritone); - MutableMoveResult result = new MutableMoveResult(); - return MovementDescend.dynamicFallCost(context, src.x, src.y, src.z, dest.x, dest.z, 0, context.get(dest.x, src.y - 2, dest.z), result); - } - @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 @@ -190,6 +178,18 @@ public boolean safeToCancel(MovementState state) { return ctx.playerFeet().equals(src) || state.getStatus() != MovementStatus.RUNNING; } + private static BetterBlockPos[] buildPositionsToBreak(BetterBlockPos src, BetterBlockPos dest) { + BetterBlockPos[] toBreak; + int diffX = src.getX() - dest.getX(); + int diffZ = src.getZ() - dest.getZ(); + int diffY = Math.abs(src.getY() - dest.getY()); + toBreak = new BetterBlockPos[diffY + 2]; + for (int i = 0; i < toBreak.length; i++) { + toBreak[i] = new BetterBlockPos(src.getX() - diffX, src.getY() + 1 - i, src.getZ() - diffZ); + } + return toBreak; + } + @Override protected boolean prepared(MovementState state) { if (state.getStatus() == MovementStatus.WAITING) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index 483fdc048..1fa150a21 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; @@ -32,7 +33,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.StairBlock; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.WaterFluid; import java.util.HashSet; @@ -75,7 +75,7 @@ public static void cost(CalculationContext context, int x, int y, int z, Directi return; } BlockState adj = context.get(x + xDiff, y - 1, z + zDiff); - if (MovementHelper.canWalkOn(context.bsi, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now) + if (MovementHelper.canWalkOn(context, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now) // second most common case -- we could just traverse not parkour return; } @@ -92,9 +92,16 @@ public static void cost(CalculationContext context, int x, int y, int z, Directi return; } BlockState standingOn = context.get(x, y - 1, z); - if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof StairBlock || MovementHelper.isBottomSlab(standingOn) || standingOn.getFluidState().getType() != Fluids.EMPTY) { + if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof StairBlock || MovementHelper.isBottomSlab(standingOn)) { return; } + // we can't jump from (frozen) water with assumeWalkOnWater because we can't be sure it will be frozen + if (context.assumeWalkOnWater && !standingOn.getFluidState().isEmpty()) { + return; + } + if (!context.get(x, y, z).getFluidState().isEmpty()) { + return; // can't jump out of water + } int maxJump; if (standingOn.getBlock() == Blocks.SOUL_SAND) { maxJump = 2; // 1 block gap @@ -122,8 +129,8 @@ public static void cost(CalculationContext context, int x, int y, int z, Directi // check for ascend landing position BlockState destInto = context.bsi.get0(destX, y, destZ); - if (!MovementHelper.fullyPassable(context.bsi.access, context.bsi.isPassableBlockPos.set(destX, y, destZ), destInto)) { - if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { + if (!MovementHelper.fullyPassable(context, destX, y, destZ, destInto)) { + if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { res.x = destX; res.y = y + 1; res.z = destZ; @@ -136,7 +143,10 @@ public static void cost(CalculationContext context, int x, int y, int z, Directi // check for flat landing position BlockState landingOn = context.bsi.get0(destX, y - 1, destZ); // farmland needs to be canWalkOn otherwise farm can never work at all, but we want to specifically disallow ending a jump on farmland haha - if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) { + // frostwalker works here because we can't jump from possibly unfrozen water + if ((landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context, destX, y - 1, destZ, landingOn)) + || (Math.min(16, context.frostWalker + 2) >= i && MovementHelper.canUseFrostWalker(context, landingOn)) + ) { if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { res.x = destX; res.y = y; @@ -266,7 +276,12 @@ public MovementState updateState(MovementState state) { } } else if (!ctx.playerFeet().equals(src)) { if (ctx.playerFeet().equals(src.relative(direction)) || ctx.player().position().y - src.y > 0.0001) { - if (!MovementHelper.canWalkOn(ctx, dest.below()) && !ctx.player().isOnGround() && MovementHelper.attemptToPlaceABlock(state, baritone, dest.below(), true, false) == PlaceResult.READY_TO_PLACE) { + if (Baritone.settings().allowPlace.value // see PR #3775 + && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway() + && !MovementHelper.canWalkOn(ctx, dest.below()) + && !ctx.player().onGround() + && MovementHelper.attemptToPlaceABlock(state, baritone, dest.below(), true, false) == PlaceResult.READY_TO_PLACE + ) { // go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory state.setInput(Input.CLICK_RIGHT, true); } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index 6e6a41351..18c105d2b 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -100,6 +100,10 @@ public static double cost(CalculationContext context, int x, int y, int z) { // if we're standing on water and assumeWalkOnWater is false, we must have ascended to here, or sneak backplaced, so it is possible to pillar again return COST_INF; } + if ((from == Blocks.LILY_PAD || from instanceof CarpetBlock) && !fromDown.getFluidState().isEmpty()) { + // to ascend here we'd have to break the block we are standing on + return COST_INF; + } double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); if (hardness >= COST_INF) { return COST_INF; @@ -119,7 +123,7 @@ public static double cost(CalculationContext context, int x, int y, int z) { } } // this is commented because it may have had a purpose, but it's very unclear what it was. it's from the minebot era. - //if (!MovementHelper.canWalkOn(chkPos, check) || MovementHelper.canWalkThrough(chkPos, check)) {//if the block above where we want to break is not a full block, don't do it + //if (!MovementHelper.canWalkOn(context, chkPos, check) || MovementHelper.canWalkThrough(context, chkPos, check)) {//if the block above where we want to break is not a full block, don't do it // TODO why does canWalkThrough mean this action is COST_INF? // FallingBlock makes sense, and !canWalkOn deals with weird cases like if it were lava // but I don't understand why canWalkThrough makes it impossible @@ -185,9 +189,9 @@ public MovementState updateState(MovementState state) { boolean vine = fromDown.getBlock() == Blocks.VINE; Rotation rotation = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(positionToPlace), - new Rotation(ctx.player().getYRot(), ctx.player().getXRot())); + ctx.playerRotations()); if (!ladder) { - state.setTarget(new MovementState.MovementTarget(new Rotation(ctx.player().getYRot(), rotation.getPitch()), true)); + state.setTarget(new MovementState.MovementTarget(ctx.playerRotations().withPitch(rotation.getPitch()), true)); } boolean blockIsThere = MovementHelper.canWalkOn(ctx, src) || ladder; @@ -245,8 +249,8 @@ public MovementState updateState(MovementState state) { BlockState frState = BlockStateInterface.get(ctx, src); Block fr = frState.getBlock(); // TODO: Evaluate usage of getMaterial().isReplaceable() - if (!(fr instanceof AirBlock || frState.getMaterial().isReplaceable())) { - RotationUtils.reachable(ctx.player(), src, ctx.playerController().getBlockReachDistance()) + if (!(fr instanceof AirBlock || frState.canBeReplaced())) { + RotationUtils.reachable(ctx, src, ctx.playerController().getBlockReachDistance()) .map(rot -> new MovementState.MovementTarget(rot, true)) .ifPresent(state::setTarget); state.setInput(Input.JUMP, false); // breaking is like 5x slower when you're jumping diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 52cbd0968..54ac87d60 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -36,7 +36,6 @@ import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.SlabType; -import net.minecraft.world.level.material.WaterFluid; import net.minecraft.world.phys.Vec3; import java.util.Optional; @@ -73,9 +72,11 @@ public static double cost(CalculationContext context, int x, int y, int z, int d BlockState pb0 = context.get(destX, y + 1, destZ); BlockState pb1 = context.get(destX, y, destZ); BlockState destOn = context.get(destX, y - 1, destZ); - BlockState down = context.get(x, y - 1, z); - Block srcDown = down.getBlock(); - if (MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge + BlockState srcDown = context.get(x, y - 1, z); + Block srcDownBlock = srcDown.getBlock(); + boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y - 1, z, srcDown); + boolean frostWalker = standingOnABlock && !context.assumeWalkOnWater && MovementHelper.canUseFrostWalker(context, destOn); + if (frostWalker || MovementHelper.canWalkOn(context, destX, y - 1, destZ, destOn)) { //this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; boolean water = false; if (MovementHelper.isWater(pb0) || MovementHelper.isWater(pb1)) { @@ -84,10 +85,12 @@ public static double cost(CalculationContext context, int x, int y, int z, int d } else { if (destOn.getBlock() == Blocks.SOUL_SAND) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; + } else if (frostWalker) { + // with frostwalker we can walk on water without the penalty, if we are sure we won't be using jesus } else if (destOn.getBlock() == Blocks.WATER) { WC += context.walkOnWaterOnePenalty; } - if (srcDown == Blocks.SOUL_SAND) { + if (srcDownBlock == Blocks.SOUL_SAND) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } } @@ -105,13 +108,13 @@ public static double cost(CalculationContext context, int x, int y, int z, int d } return WC; } - if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { + if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { hardness1 *= 5; hardness2 *= 5; } return WC + hardness1 + hardness2; } else {//this is a bridge, so we need to place a block - if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { + if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { return COST_INF; } if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) { @@ -142,12 +145,16 @@ public static double cost(CalculationContext context, int x, int y, int z, int d } } // now that we've checked all possible directions to side place, we actually need to backplace - if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof SlabBlock && down.getValue(SlabBlock.TYPE) != SlabType.DOUBLE)) { + if (srcDownBlock == Blocks.SOUL_SAND || (srcDownBlock instanceof SlabBlock && srcDown.getValue(SlabBlock.TYPE) != SlabType.DOUBLE)) { return COST_INF; // can't sneak and backplace against soul sand or half slabs (regardless of whether it's top half or bottom half) =/ } - if (down.getFluidState().getType() instanceof WaterFluid) { + if (!standingOnABlock) { // standing on water / swimming return COST_INF; // this is obviously impossible } + Block blockSrc = context.getBlock(x, y, z); + if ((blockSrc == Blocks.LILY_PAD || blockSrc instanceof CarpetBlock) && !srcDown.getFluidState().isEmpty()) { + return COST_INF; // we can stand on these but can't place against them + } WC = WC * (SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST);//since we are sneak backplacing, we are sneaking lol return WC + placeCost + hardness1 + hardness2; } @@ -231,7 +238,7 @@ public MovementState updateState(MovementState state) { } } - boolean isTheBridgeBlockThere = MovementHelper.canWalkOn(ctx, positionToPlace) || ladder; + boolean isTheBridgeBlockThere = MovementHelper.canWalkOn(ctx, positionToPlace) || ladder || MovementHelper.canUseFrostWalker(ctx, positionToPlace); BlockPos feet = ctx.playerFeet(); if (feet.getY() != dest.getY() && !ladder) { logDebug("Wrong Y coordinate"); @@ -251,7 +258,7 @@ public MovementState updateState(MovementState state) { } Block low = BlockStateInterface.get(ctx, src).getBlock(); Block high = BlockStateInterface.get(ctx, src.above()).getBlock(); - if (ctx.player().position().y > src.y + 0.1D && !ctx.player().isOnGround() && (low == Blocks.VINE || low == Blocks.LADDER || high == Blocks.VINE || high == Blocks.LADDER)) { + if (ctx.player().position().y > src.y + 0.1D && !ctx.player().onGround() && (low == Blocks.VINE || low == Blocks.LADDER || high == Blocks.VINE || high == Blocks.LADDER)) { // hitting W could cause us to climb the ladder instead of going forward // wait until we're on the ground return state; @@ -259,8 +266,6 @@ public MovementState updateState(MovementState state) { BlockPos into = dest.subtract(src).offset(dest); BlockState intoBelow = BlockStateInterface.get(ctx, into); BlockState intoAbove = BlockStateInterface.get(ctx, into.above()); - boolean avoidBelow = MovementHelper.avoidWalkingInto(intoBelow) || AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(into); - boolean avoidAbove = MovementHelper.avoidWalkingInto(intoAbove) || AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(into.above()); if (wasTheBridgeBlockAlwaysThere && (!MovementHelper.isLiquid(ctx, feet) || Baritone.settings().sprintInWater.value) && (!MovementHelper.avoidWalkingInto(intoBelow) || MovementHelper.isWater(intoBelow)) && !MovementHelper.avoidWalkingInto(intoAbove)) { state.setInput(Input.SPRINT, true); } diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index ce06a3b84..a17e7c8f2 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -254,19 +254,19 @@ public boolean onTick() { return canCancel; // movement is in progress, but if it reports cancellable, PathingBehavior is good to cut onto the next path } - private static boolean skipNow(IPlayerContext ctx, IMovement current) { - double offTarget = Math.abs(current.getDirection().getX() * (current.getSrc().z + 0.5D - ctx.player().position().z)) + Math.abs(current.getDirection().getZ() * (current.getSrc().x + 0.5D - ctx.player().position().x)); - if (offTarget > 0.1) { - return false; - } - // we are centered - BlockPos headBonk = current.getSrc().subtract(current.getDirection()).above(2); - if (MovementHelper.fullyPassable(ctx, headBonk)) { - return true; + private Tuple closestPathPos(IPath path) { + double best = -1; + BlockPos bestPos = null; + for (IMovement movement : path.movements()) { + for (BlockPos pos : ((Movement) movement).getValidPositions()) { + double dist = VecUtils.entityDistanceToCenter(ctx.player(), pos); + if (dist < best || best == -1) { + best = dist; + bestPos = pos; + } + } } - // wait 0.3 - double flatDist = Math.abs(current.getDirection().getX() * (headBonk.getX() + 0.5D - ctx.player().position().x)) + Math.abs(current.getDirection().getZ() * (headBonk.getZ() + 0.5 - ctx.player().position().z)); - return flatDist > 0.8; + return new Tuple<>(best, bestPos); } private boolean shouldPause() { @@ -274,7 +274,7 @@ private boolean shouldPause() { if (!current.isPresent()) { return false; } - if (!ctx.player().isOnGround()) { + if (!ctx.player().onGround()) { return false; } if (!MovementHelper.canWalkOn(ctx, ctx.playerFeet().below())) { @@ -323,7 +323,7 @@ private boolean possiblyOffPath(Tuple status, double leniency) * @return Whether or not it was possible to snap to the current player feet */ public boolean snipsnapifpossible() { - if (!ctx.player().isOnGround() && ctx.world().getFluidState(ctx.playerFeet()).isEmpty()) { + if (!ctx.player().onGround() && ctx.world().getFluidState(ctx.playerFeet()).isEmpty()) { // if we're falling in the air, and not in water, don't splice return false; } else { @@ -343,113 +343,6 @@ public boolean snipsnapifpossible() { return true; } - private static boolean sprintableAscend(IPlayerContext ctx, MovementTraverse current, MovementAscend next, IMovement nextnext) { - if (!Baritone.settings().sprintAscends.value) { - return false; - } - if (!current.getDirection().equals(next.getDirection().below())) { - return false; - } - if (nextnext.getDirection().getX() != next.getDirection().getX() || nextnext.getDirection().getZ() != next.getDirection().getZ()) { - return false; - } - if (!MovementHelper.canWalkOn(ctx, current.getDest().below())) { - return false; - } - if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { - return false; - } - if (!next.toBreakCached.isEmpty()) { - return false; // it's breaking - } - for (int x = 0; x < 2; x++) { - for (int y = 0; y < 3; y++) { - BlockPos chk = current.getSrc().above(y); - if (x == 1) { - chk = chk.offset(current.getDirection()); - } - if (!MovementHelper.fullyPassable(ctx, chk)) { - return false; - } - } - } - if (MovementHelper.avoidWalkingInto(ctx.world().getBlockState(current.getSrc().above(3)))) { - return false; - } - if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(current.getSrc().above(3)) || - AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(current.getSrc().above(2))) { - return false; - } - return !MovementHelper.avoidWalkingInto(ctx.world().getBlockState(next.getDest().above(2))); // codacy smh my head - } - - private static boolean canSprintFromDescendInto(IPlayerContext ctx, IMovement current, IMovement next) { - if (next instanceof MovementDescend && next.getDirection().equals(current.getDirection())) { - return true; - } - if (!MovementHelper.canWalkOn(ctx, current.getDest().offset(current.getDirection()))) { - return false; - } - if (next instanceof MovementTraverse && next.getDirection().equals(current.getDirection())) { - return true; - } - return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.value; - } - - private Tuple overrideFall(MovementFall movement) { - Vec3i dir = movement.getDirection(); - if (dir.getY() < -3) { - return null; - } - if (!movement.toBreakCached.isEmpty()) { - return null; // it's breaking - } - Vec3i flatDir = new Vec3i(dir.getX(), 0, dir.getZ()); - int i; - outer: - for (i = pathPosition + 1; i < path.length() - 1 && i < pathPosition + 3; i++) { - IMovement next = path.movements().get(i); - if (!(next instanceof MovementTraverse)) { - break; - } - if (!flatDir.equals(next.getDirection())) { - break; - } - for (int y = next.getDest().y; y <= movement.getSrc().y + 1; y++) { - BlockPos chk = new BlockPos(next.getDest().x, y, next.getDest().z); - if (!MovementHelper.fullyPassable(ctx, chk)) { - break outer; - } - } - if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { - break; - } - } - i--; - if (i == pathPosition) { - return null; // no valid extension exists - } - double len = i - pathPosition - 0.4; - return new Tuple<>( - new Vec3(flatDir.getX() * len + movement.getDest().x + 0.5, movement.getDest().y, flatDir.getZ() * len + movement.getDest().z + 0.5), - movement.getDest().offset(flatDir.getX() * (i - pathPosition), 0, flatDir.getZ() * (i - pathPosition))); - } - - private Tuple closestPathPos(IPath path) { - double best = -1; - BlockPos bestPos = null; - for (IMovement movement : path.movements()) { - for (BlockPos pos : ((Movement) movement).getValidPositions()) { - double dist = VecUtils.entityDistanceToCenter(ctx.player(), pos); - if (dist < best || best == -1) { - best = dist; - bestPos = pos; - } - } - } - return new Tuple<>(best, bestPos); - } - private boolean shouldSprintNextTick() { boolean requested = behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT); @@ -457,7 +350,7 @@ private boolean shouldSprintNextTick() { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint - if (!new CalculationContext(behavior.baritone).canSprint) { + if (!new CalculationContext(behavior.baritone, false).canSprint) { return false; } IMovement current = path.movements().get(pathPosition); @@ -487,6 +380,26 @@ private boolean shouldSprintNextTick() { // however, descend and ascend don't request sprinting, because they don't know the context of what movement comes after it if (current instanceof MovementDescend) { + if (pathPosition < path.length() - 2) { + // keep this out of onTick, even if that means a tick of delay before it has an effect + IMovement next = path.movements().get(pathPosition + 1); + if (MovementHelper.canUseFrostWalker(ctx, next.getDest().below())) { + // frostwalker only works if you cross the edge of the block on ground so in some cases we may not overshoot + // Since MovementDescend can't know the next movement we have to tell it + if (next instanceof MovementTraverse || next instanceof MovementParkour) { + boolean couldPlaceInstead = Baritone.settings().allowPlace.value && behavior.baritone.getInventoryBehavior().hasGenericThrowaway() && next instanceof MovementParkour; // traverse doesn't react fast enough + // this is true if the next movement does not ascend or descends and goes into the same cardinal direction (N-NE-E-SE-S-SW-W-NW) as the descend + // in that case current.getDirection() is e.g. (0, -1, 1) and next.getDirection() is e.g. (0, 0, 3) so the cross product of (0, 0, 1) and (0, 0, 3) is taken, which is (0, 0, 0) because the vectors are colinear (don't form a plane) + // since movements in exactly the opposite direction (e.g. descend (0, -1, 1) and traverse (0, 0, -1)) would also pass this check we also have to rule out that case + // we can do that by adding the directions because traverse is always 1 long like descend and parkour can't jump through current.getSrc().down() + boolean sameFlatDirection = !current.getDirection().above().offset(next.getDirection()).equals(BlockPos.ZERO) + && current.getDirection().above().cross(next.getDirection()).equals(BlockPos.ZERO); // here's why you learn maths in school + if (sameFlatDirection && !couldPlaceInstead) { + ((MovementDescend) current).forceSafeMode(); + } + } + } + } if (((MovementDescend) current).safeMode() && !((MovementDescend) current).skipToAscend()) { logDebug("Sprinting would be unsafe"); return false; @@ -561,6 +474,113 @@ private boolean shouldSprintNextTick() { return false; } + private Tuple overrideFall(MovementFall movement) { + Vec3i dir = movement.getDirection(); + if (dir.getY() < -3) { + return null; + } + if (!movement.toBreakCached.isEmpty()) { + return null; // it's breaking + } + Vec3i flatDir = new Vec3i(dir.getX(), 0, dir.getZ()); + int i; + outer: + for (i = pathPosition + 1; i < path.length() - 1 && i < pathPosition + 3; i++) { + IMovement next = path.movements().get(i); + if (!(next instanceof MovementTraverse)) { + break; + } + if (!flatDir.equals(next.getDirection())) { + break; + } + for (int y = next.getDest().y; y <= movement.getSrc().y + 1; y++) { + BlockPos chk = new BlockPos(next.getDest().x, y, next.getDest().z); + if (!MovementHelper.fullyPassable(ctx, chk)) { + break outer; + } + } + if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { + break; + } + } + i--; + if (i == pathPosition) { + return null; // no valid extension exists + } + double len = i - pathPosition - 0.4; + return new Tuple<>( + new Vec3(flatDir.getX() * len + movement.getDest().x + 0.5, movement.getDest().y, flatDir.getZ() * len + movement.getDest().z + 0.5), + movement.getDest().offset(flatDir.getX() * (i - pathPosition), 0, flatDir.getZ() * (i - pathPosition))); + } + + private static boolean skipNow(IPlayerContext ctx, IMovement current) { + double offTarget = Math.abs(current.getDirection().getX() * (current.getSrc().z + 0.5D - ctx.player().position().z)) + Math.abs(current.getDirection().getZ() * (current.getSrc().x + 0.5D - ctx.player().position().x)); + if (offTarget > 0.1) { + return false; + } + // we are centered + BlockPos headBonk = current.getSrc().subtract(current.getDirection()).above(2); + if (MovementHelper.fullyPassable(ctx, headBonk)) { + return true; + } + // wait 0.3 + double flatDist = Math.abs(current.getDirection().getX() * (headBonk.getX() + 0.5D - ctx.player().position().x)) + Math.abs(current.getDirection().getZ() * (headBonk.getZ() + 0.5 - ctx.player().position().z)); + return flatDist > 0.8; + } + + private static boolean sprintableAscend(IPlayerContext ctx, MovementTraverse current, MovementAscend next, IMovement nextnext) { + if (!Baritone.settings().sprintAscends.value) { + return false; + } + if (!current.getDirection().equals(next.getDirection().below())) { + return false; + } + if (nextnext.getDirection().getX() != next.getDirection().getX() || nextnext.getDirection().getZ() != next.getDirection().getZ()) { + return false; + } + if (!MovementHelper.canWalkOn(ctx, current.getDest().below())) { + return false; + } + if (!MovementHelper.canWalkOn(ctx, next.getDest().below())) { + return false; + } + if (!next.toBreakCached.isEmpty()) { + return false; // it's breaking + } + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 3; y++) { + BlockPos chk = current.getSrc().above(y); + if (x == 1) { + chk = chk.offset(current.getDirection()); + } + if (!MovementHelper.fullyPassable(ctx, chk)) { + return false; + } + } + } + if (MovementHelper.avoidWalkingInto(ctx.world().getBlockState(current.getSrc().above(3)))) { + return false; + } + if (AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(current.getSrc().above(3)) || + AltoClefSettings.getInstance().shouldAvoidWalkThroughForce(current.getSrc().above(2))) { + return false; + } + return !MovementHelper.avoidWalkingInto(ctx.world().getBlockState(next.getDest().above(2))); // codacy smh my head + } + + private static boolean canSprintFromDescendInto(IPlayerContext ctx, IMovement current, IMovement next) { + if (next instanceof MovementDescend && next.getDirection().equals(current.getDirection())) { + return true; + } + if (!MovementHelper.canWalkOn(ctx, current.getDest().offset(current.getDirection()))) { + return false; + } + if (next instanceof MovementTraverse && next.getDirection().equals(current.getDirection())) { + return true; + } + return next instanceof MovementDiagonal && Baritone.settings().allowOvershootDiagonalDescend.value; + } + private void onChangeInPathPosition() { clearKeys(); ticksOnCurrent = 0; diff --git a/src/main/java/baritone/pathing/precompute/PrecomputedData.java b/src/main/java/baritone/pathing/precompute/PrecomputedData.java new file mode 100644 index 000000000..9d7394254 --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/PrecomputedData.java @@ -0,0 +1,117 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.precompute; + +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BlockStateInterface; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import static baritone.pathing.precompute.Ternary.MAYBE; +import static baritone.pathing.precompute.Ternary.YES; + +public class PrecomputedData { + + private final int[] data = new int[Block.BLOCK_STATE_REGISTRY.size()]; + + private static final int COMPLETED_MASK = 1 << 0; + private static final int CAN_WALK_ON_MASK = 1 << 1; + private static final int CAN_WALK_ON_SPECIAL_MASK = 1 << 2; + private static final int CAN_WALK_THROUGH_MASK = 1 << 3; + private static final int CAN_WALK_THROUGH_SPECIAL_MASK = 1 << 4; + private static final int FULLY_PASSABLE_MASK = 1 << 5; + private static final int FULLY_PASSABLE_SPECIAL_MASK = 1 << 6; + + private int fillData(int id, BlockState state) { + int blockData = 0; + + Ternary canWalkOnState = MovementHelper.canWalkOnBlockState(state); + if (canWalkOnState == YES) { + blockData |= CAN_WALK_ON_MASK; + } + if (canWalkOnState == MAYBE) { + blockData |= CAN_WALK_ON_SPECIAL_MASK; + } + + Ternary canWalkThroughState = MovementHelper.canWalkThroughBlockState(state); + if (canWalkThroughState == YES) { + blockData |= CAN_WALK_THROUGH_MASK; + } + if (canWalkThroughState == MAYBE) { + blockData |= CAN_WALK_THROUGH_SPECIAL_MASK; + } + + Ternary fullyPassableState = MovementHelper.fullyPassableBlockState(state); + if (fullyPassableState == YES) { + blockData |= FULLY_PASSABLE_MASK; + } + if (fullyPassableState == MAYBE) { + blockData |= FULLY_PASSABLE_SPECIAL_MASK; + } + + blockData |= COMPLETED_MASK; + + data[id] = blockData; // in theory, this is thread "safe" because every thread should compute the exact same int to write? + return blockData; + } + + public boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + int id = Block.BLOCK_STATE_REGISTRY.getId(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & CAN_WALK_ON_SPECIAL_MASK) != 0) { + return MovementHelper.canWalkOnPosition(bsi, x, y, z, state); + } else { + return (blockData & CAN_WALK_ON_MASK) != 0; + } + } + + public boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + int id = Block.BLOCK_STATE_REGISTRY.getId(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & CAN_WALK_THROUGH_SPECIAL_MASK) != 0) { + return MovementHelper.canWalkThroughPosition(bsi, x, y, z, state); + } else { + return (blockData & CAN_WALK_THROUGH_MASK) != 0; + } + } + + public boolean fullyPassable(BlockStateInterface bsi, int x, int y, int z, BlockState state) { + int id = Block.BLOCK_STATE_REGISTRY.getId(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & FULLY_PASSABLE_SPECIAL_MASK) != 0) { + return MovementHelper.fullyPassablePosition(bsi, x, y, z, state); + } else { + return (blockData & FULLY_PASSABLE_MASK) != 0; + } + } +} diff --git a/src/main/java/baritone/pathing/precompute/Ternary.java b/src/main/java/baritone/pathing/precompute/Ternary.java new file mode 100644 index 000000000..d4d8424e0 --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/Ternary.java @@ -0,0 +1,22 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.precompute; + +public enum Ternary { + YES, MAYBE, NO +} diff --git a/src/main/java/baritone/process/BackfillProcess.java b/src/main/java/baritone/process/BackfillProcess.java index cebe07c97..af191ae1e 100644 --- a/src/main/java/baritone/process/BackfillProcess.java +++ b/src/main/java/baritone/process/BackfillProcess.java @@ -26,14 +26,13 @@ import baritone.pathing.movement.MovementState; import baritone.pathing.path.PathExecutor; import baritone.utils.BaritoneProcessHelper; +import java.util.*; +import java.util.stream.Collectors; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.EmptyLevelChunk; -import java.util.*; -import java.util.stream.Collectors; - public final class BackfillProcess extends BaritoneProcessHelper { public HashMap blocksToReplace = new HashMap<>(); diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 9ca546840..825635e0b 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -43,10 +43,11 @@ import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import baritone.utils.PathingCommandContext; -import baritone.utils.Trail; import baritone.utils.schematic.MapArtSchematic; import baritone.utils.schematic.SchematicSystem; import baritone.utils.schematic.SelectionSchematic; +import baritone.utils.schematic.format.defaults.LitematicaSchematic; +import baritone.utils.schematic.litematica.LitematicaHelper; import baritone.utils.schematic.schematica.SchematicaHelper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -55,6 +56,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; import net.minecraft.network.chat.Component; import net.minecraft.util.Tuple; import net.minecraft.world.InteractionHand; @@ -70,17 +73,20 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; -import org.jetbrains.annotations.Nullable; +import javax.annotation.Nullable; import java.io.File; import java.io.FileInputStream; +import java.nio.file.Files; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import static baritone.api.pathing.movement.ActionCosts.COST_INF; public final class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess { + private HashSet incorrectPositions; private LongOpenHashSet observedCompleted; // positions that are completed even if they're out of render distance and we can't make sure right now private String name; @@ -92,11 +98,6 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil private int layer; private int numRepeats; private List approxPlaceable; - private Map missing; - private boolean active; - private Stack stateStack = new Stack<>(); - private Vec3i schemSize; - private boolean fromAltoclefFinished; public static final Set> orientationProps = ImmutableSet.of( RotatedPillarBlock.AXIS, HorizontalDirectionalBlock.FACING, @@ -104,6 +105,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil PipeBlock.NORTH, PipeBlock.EAST, PipeBlock.SOUTH, PipeBlock.WEST, PipeBlock.UP, TrapDoorBlock.OPEN, TrapDoorBlock.HALF ); + + public int stopAtHeight = 0; private final Map protectedItems = new HashMap<>(); private final Function, Comparable>, PropertyContainer> PROPERTY_ENTRY_TO_STRING_FUNCTION = new Function<>() { public PropertyContainer apply(@Nullable Map.Entry, Comparable> entry) { @@ -122,21 +125,59 @@ private > String getName(Property property, Comparabl return property.getName((T) comparable); } }; + private boolean fromAltoclef; + private Map missing; + private boolean active; + private Stack stateStack = new Stack<>(); public BuilderProcess(Baritone baritone) { super(baritone); } + private Vec3i schemSize; + private boolean fromAltoclefFinished; + private Map blockBreakHistory = new HashMap<>(); + + private void pushState() { + stateStack.clear(); + stateStack.push(this.approxPlaceable); + stateStack.push(this.ticks); + stateStack.push(this.incorrectPositions); + stateStack.push(this.name); + stateStack.push(this.schematic); + stateStack.push(this.realSchematic); + stateStack.push(this.origin); + stateStack.push(this.paused); + stateStack.push(this.layer); + stateStack.push(this.numRepeats); + stateStack.push(this.observedCompleted); + stateStack.push(this.active); + stateStack.push(this.missing); + stateStack.push(this.schemSize); + stateStack.push(this.fromAltoclefFinished); + stateStack.push(this.fromAltoclef); + } + + @Override + public double priority() { + return super.priority(); + } + + @Override + public String displayName() { + return super.displayName(); + } + @Override public void build(String name, ISchematic schematic, Vec3i origin) { //Shouldn't get initially called if (this.fromAltoclef && this.stateStack.isEmpty()) { pushState(); } - this.name = name; this.schematic = schematic; this.realSchematic = null; + boolean buildingSelectionSchematic = schematic instanceof SelectionSchematic; if (!Baritone.settings().buildSubstitutes.value.isEmpty()) { this.schematic = new SubstituteSchematic(this.schematic, Baritone.settings().buildSubstitutes.value); } @@ -155,25 +196,41 @@ public void build(String name, ISchematic schematic, Vec3i origin) { this.origin = new Vec3i(x, y, z); this.paused = false; this.layer = Baritone.settings().startAtLayer.value; + this.stopAtHeight = schematic.heightY(); + if (Baritone.settings().buildOnlySelection.value && buildingSelectionSchematic) { // currently redundant but safer maybe + if (baritone.getSelectionManager().getSelections().length == 0) { + logDirect("Poor little kitten forgot to set a selection while BuildOnlySelection is true"); + this.stopAtHeight = 0; + } else if (Baritone.settings().buildInLayers.value) { + OptionalInt minim = Stream.of(baritone.getSelectionManager().getSelections()).mapToInt(sel -> sel.min().y).min(); + OptionalInt maxim = Stream.of(baritone.getSelectionManager().getSelections()).mapToInt(sel -> sel.max().y).max(); + if (minim.isPresent() && maxim.isPresent()) { + int startAtHeight = Baritone.settings().layerOrder.value ? y + schematic.heightY() - maxim.getAsInt() : minim.getAsInt() - y; + this.stopAtHeight = (Baritone.settings().layerOrder.value ? y + schematic.heightY() - minim.getAsInt() : maxim.getAsInt() - y) + 1; + this.layer = Math.max(this.layer, startAtHeight / Baritone.settings().layerHeight.value); // startAtLayer or startAtHeight, whichever is highest + logDebug(String.format("Schematic starts at y=%s with height %s", y, schematic.heightY())); + logDebug(String.format("Selection starts at y=%s and ends at y=%s", minim.getAsInt(), maxim.getAsInt())); + logDebug(String.format("Considering relevant height %s - %s", startAtHeight, this.stopAtHeight)); + } + } + } + this.numRepeats = 0; this.observedCompleted = new LongOpenHashSet(); + this.incorrectPositions = null; this.active = true; - - //stopProtectItemOfMissing(); - if (this.missing != null) { this.missing.clear(); } else { missing = new HashMap<>(); } - this.schemSize = new Vec3i(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); this.fromAltoclefFinished = false; this.fromAltoclef = false; } private void protectItemOfMissing() { - if (missing != null && missing.keySet() != null) { + if (missing != null) { protectedItems.putAll(missing); protectedItems.keySet().forEach(e -> { if (!AltoClefSettings.getInstance().isItemProtected(e.getBlock().asItem())) { @@ -183,42 +240,6 @@ private void protectItemOfMissing() { } } - private void stopProtectItemOfMissing() { - if (protectedItems != null && protectedItems.keySet() != null && protectedItems.size() > 0) { - protectedItems.keySet().forEach(e -> { - if (AltoClefSettings.getInstance().isItemProtected(e.getBlock().asItem())) { - AltoClefSettings.getInstance().stopProtectingItem(e.getBlock().asItem()); - } - }); - } - protectedItems.clear(); - } - - @Override - public Vec3i getSchemSize() { - return schemSize; - } - - private void pushState() { - stateStack.clear(); - stateStack.push(this.approxPlaceable); - stateStack.push(this.ticks); - stateStack.push(this.incorrectPositions); - stateStack.push(this.name); - stateStack.push(this.schematic); - stateStack.push(this.realSchematic); - stateStack.push(this.origin); - stateStack.push(this.paused); - stateStack.push(this.layer); - stateStack.push(this.numRepeats); - stateStack.push(this.observedCompleted); - stateStack.push(this.active); - stateStack.push(this.missing); - stateStack.push(this.schemSize); - stateStack.push(this.fromAltoclefFinished); - stateStack.push(this.fromAltoclef); - } - @Override public void popStack() { if (this.stateStack.isEmpty()) { @@ -241,47 +262,37 @@ public void popStack() { this.incorrectPositions = (HashSet) stateStack.pop(); this.ticks = (int) stateStack.pop(); this.approxPlaceable = (List) stateStack.pop(); - pushState(); - if (!stateStack.isEmpty()) { logDebug("ERROR: state stack was not empty after state restoration. Will throw away the rest for now."); stateStack.clear(); } } - private boolean fromAltoclef; - private Map sbtMissing; - private Map blockBreakHistory = new HashMap<>(); + @Override + public boolean isFromAltoclefFinished() { + return this.fromAltoclefFinished; + } - private static Vec3[] aabbSideMultipliers(Direction side) { - switch (side) { - case UP: - return new Vec3[]{new Vec3(0.5, 1, 0.5), new Vec3(0.1, 1, 0.5), new Vec3(0.9, 1, 0.5), new Vec3(0.5, 1, 0.1), new Vec3(0.5, 1, 0.9)}; - case DOWN: - return new Vec3[]{new Vec3(0.5, 0, 0.5), new Vec3(0.1, 0, 0.5), new Vec3(0.9, 0, 0.5), new Vec3(0.5, 0, 0.1), new Vec3(0.5, 0, 0.9)}; - case NORTH: - case SOUTH: - case EAST: - case WEST: - double x = side.getStepX() == 0 ? 0.5 : (1 + side.getStepX()) / 2D; - double z = side.getStepZ() == 0 ? 0.5 : (1 + side.getStepZ()) / 2D; - return new Vec3[]{new Vec3(x, 0.25, z), new Vec3(x, 0.75, z)}; - default: // null - throw new IllegalStateException(); + private void stopProtectItemOfMissing() { + if (protectedItems != null && protectedItems.size() > 0) { + protectedItems.keySet().forEach(e -> { + if (AltoClefSettings.getInstance().isItemProtected(e.getBlock().asItem())) { + AltoClefSettings.getInstance().stopProtectingItem(e.getBlock().asItem()); + } + }); } + assert protectedItems != null; + protectedItems.clear(); } - @Override - public boolean clearState() { - final boolean isEmpty = !stateStack.isEmpty(); - stateStack.clear(); - return isEmpty; + public void pause() { + paused = true; } @Override - public boolean isFromAltoclefFinished() { - return this.fromAltoclefFinished; + public boolean isPaused() { + return paused; } @Override @@ -290,7 +301,6 @@ public boolean build(String name, File schematic, Vec3i origin) { if (!format.isPresent()) { return false; } - ISchematic parsed; try { parsed = format.get().parse(new FileInputStream(schematic)); @@ -298,36 +308,20 @@ public boolean build(String name, File schematic, Vec3i origin) { e.printStackTrace(); return false; } - - if (Baritone.settings().mapArtMode.value) { - parsed = new MapArtSchematic((IStaticSchematic) parsed); - } - - if (Baritone.settings().buildOnlySelection.value) { - parsed = new SelectionSchematic(parsed, origin, baritone.getSelectionManager().getSelections()); - } - - + parsed = applyMapArtAndSelection(origin, (IStaticSchematic) parsed); build(name, parsed, origin); return true; } - @Override - public boolean build(String schematicFile, BlockPos origin) { - return IBuilderProcess.super.build(schematicFile, origin); - } - - public void resume() { - if (!this.stateStack.isEmpty()) { - popStack(); + private ISchematic applyMapArtAndSelection(Vec3i origin, IStaticSchematic parsed) { + ISchematic schematic = parsed; + if (Baritone.settings().mapArtMode.value) { + schematic = new MapArtSchematic(parsed); } - - this.paused = false; - this.active = true; - } - - public void pause() { - paused = true; + if (Baritone.settings().buildOnlySelection.value) { + schematic = new SelectionSchematic(schematic, origin, baritone.getSelectionManager().getSelections()); + } + return schematic; } @Override @@ -356,7 +350,25 @@ public void buildOpenSchematic() { @Override public void buildOpenLitematic(int i) { - + if (LitematicaHelper.isLitematicaPresent()) { + //if java.lang.NoSuchMethodError is thrown see comment in SchematicPlacementManager + if (LitematicaHelper.hasLoadedSchematic()) { + String name = LitematicaHelper.getName(i); + try { + LitematicaSchematic schematic1 = new LitematicaSchematic(NbtIo.readCompressed(Files.newInputStream(LitematicaHelper.getSchematicFile(i).toPath()), NbtAccounter.unlimitedHeap()), false); + Vec3i correctedOrigin = LitematicaHelper.getCorrectedOrigin(schematic1, i); + ISchematic schematic2 = LitematicaHelper.blackMagicFuckery(schematic1, i); + schematic2 = applyMapArtAndSelection(origin, (IStaticSchematic) schematic2); + build(name, schematic2, correctedOrigin); + } catch (Exception e) { + logDirect("Schematic File could not be loaded."); + } + } else { + logDirect("No schematic currently loaded"); + } + } else { + logDirect("Litematica is not present"); + } } public void clearArea(BlockPos corner1, BlockPos corner2) { @@ -367,40 +379,23 @@ public void clearArea(BlockPos corner1, BlockPos corner2) { build("clear area", new FillSchematic(widthX, heightY, lengthZ, Blocks.AIR.defaultBlockState()), origin); } - @Override - public void reset() { - onLostControl(); - } - - @Override - public Map getMissing() { - if (this.sbtMissing == null) this.sbtMissing = new HashMap<>(); - if (this.fromAltoclef && this.missing != null) { - this.sbtMissing.clear(); - this.sbtMissing.putAll(this.missing); - } - return new HashMap<>(this.sbtMissing); - //return (this.fromAltoclef) ? new HashMap<>(this.missing) : null; - } - @Override public List getApproxPlaceable() { - return this.approxPlaceable; + return new ArrayList<>(approxPlaceable); } @Override public boolean isActive() { - return active;//schematic != null && !paused; + return schematic != null; } public BlockState placeAt(int x, int y, int z, BlockState current) { - if (this.schematic == null) { + if (!isActive()) { return null; } if (!schematic.inSchematic(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current)) { return null; } - BlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ(), current, this.approxPlaceable); if (state.getBlock() instanceof AirBlock) { return null; @@ -408,57 +403,51 @@ public BlockState placeAt(int x, int y, int z, BlockState current) { return state; } - private boolean blockPosMatches(final BlockPos pos1, final BlockPos pos2) { - return (pos1.getX() == pos2.getX() && pos1.getY() == pos2.getY() && pos1.getZ() == pos2.getZ()); + public void resume() { + if (!this.stateStack.isEmpty()) { + popStack(); + } + + this.paused = false; + this.active = true; } - /*private boolean isBlacklistedByAltoclef(final BlockPos pos) { - return this.ignoredBlocksInMesh.stream().anyMatch(e -> blockPosMatches(e, pos)); - }*/ + public static class Placement { - /*public void decideToIgnoreInSchematic(BetterBlockPos positionToPlace) { - if (isActive() && isFromAltoclef()) { - // I was too lazy to check which one should be checked - if ((this.realSchematic != null && realSchematic.inSchematic(positionToPlace.x, positionToPlace.y, positionToPlace.z, ctx.world().getBlockState(positionToPlace))) - || (this.schematic != null && schematic.inSchematic(positionToPlace.x, positionToPlace.y, positionToPlace.z, ctx.world().getBlockState(positionToPlace)))) { - BlockState state = ctx.world().getBlockState(positionToPlace); - if (state.getBlock() instanceof AirBlock) { - System.out.println("where banana?"); - this.ignoredBlocksInMesh.add(positionToPlace); - } - } + private final int hotbarSelection; + private final BlockPos placeAgainst; + private final Direction side; + private final Rotation rot; + + public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot) { + this.hotbarSelection = hotbarSelection; + this.placeAgainst = placeAgainst; + this.side = side; + this.rot = rot; } - }*/ + } - private Optional> toBreakNearPlayer(BuilderCalculationContext bcc) { + private Optional searchForPlacables(BuilderCalculationContext bcc, List desirableOnHotbar) { BetterBlockPos center = ctx.playerFeet(); - BetterBlockPos pathStart = baritone.getPathingBehavior().pathStart(); for (int dx = -5; dx <= 5; dx++) { - for (int dy = Baritone.settings().breakFromAbove.value ? -1 : 0; dy <= 5; dy++) { + for (int dy = -5; dy <= 1; dy++) { for (int dz = -5; dz <= 5; dz++) { int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; - if (dy == -1 && x == pathStart.x && z == pathStart.z) { - continue; // dont mine what we're supported by, but not directly standing on - } - BlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } - - final BlockPos tmp = new BlockPos(x, y, z); - if (anyHistoryMatch(tmp) && breakLimitExceeded(tmp)) { - continue; - } - BlockState curr = bcc.bsi.get0(x, y, z); - if (!(curr.getBlock() instanceof AirBlock) && !(curr.getBlock() == Blocks.WATER || curr.getBlock() == Blocks.LAVA) && !valid(curr, desired, false)) { - BetterBlockPos pos = new BetterBlockPos(x, y, z); - Optional rot = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); - if (rot.isPresent()) { - return Optional.of(new Tuple<>(pos, rot.get())); + if (MovementHelper.isReplaceable(x, y, z, curr, bcc.bsi) && !valid(curr, desired, false)) { + if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() instanceof AirBlock) { + continue; + } + desirableOnHotbar.add(desired); + Optional opt = possibleToPlace(desired, x, y, z, bcc.bsi); + if (opt.isPresent()) { + return opt; } } } @@ -467,32 +456,28 @@ private Optional> toBreakNearPlayer(BuilderCalcu return Optional.empty(); } - @Override - public boolean isPaused() { - return paused; - } - - private Optional searchForPlacables(BuilderCalculationContext bcc, List desirableOnHotbar) { + private Optional> toBreakNearPlayer(BuilderCalculationContext bcc) { BetterBlockPos center = ctx.playerFeet(); + BetterBlockPos pathStart = baritone.getPathingBehavior().pathStart(); for (int dx = -5; dx <= 5; dx++) { - for (int dy = -5; dy <= 3; dy++) { // maybe do dy <= 5 because range seems like to be 5 + for (int dy = Baritone.settings().breakFromAbove.value ? -1 : 0; dy <= 5; dy++) { for (int dz = -5; dz <= 5; dz++) { int x = center.x + dx; int y = center.y + dy; int z = center.z + dz; + if (dy == -1 && x == pathStart.x && z == pathStart.z) { + continue; // dont mine what we're supported by, but not directly standing on + } BlockState desired = bcc.getSchematic(x, y, z, bcc.bsi.get0(x, y, z)); if (desired == null) { continue; // irrelevant } BlockState curr = bcc.bsi.get0(x, y, z); - if (MovementHelper.isReplaceable(x, y, z, curr, bcc.bsi) && !valid(curr, desired, false)) { - if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() instanceof AirBlock) { - continue; - } - desirableOnHotbar.add(desired); - Optional opt = possibleToPlace(desired, x, y, z, bcc.bsi); - if (opt.isPresent()) { - return opt; + if (!(curr.getBlock() instanceof AirBlock) && !(curr.getBlock() == Blocks.WATER || curr.getBlock() == Blocks.LAVA) && !valid(curr, desired, false)) { + BetterBlockPos pos = new BetterBlockPos(x, y, z); + Optional rot = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); + if (rot.isPresent()) { + return Optional.of(new Tuple<>(pos, rot.get())); } } } @@ -507,59 +492,6 @@ public boolean placementPlausible(BlockPos pos, BlockState state) { return voxelshape.isEmpty() || ctx.world().isUnobstructed(null, voxelshape.move(pos.getX(), pos.getY(), pos.getZ())); } - private Optional possibleToPlace(BlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { - for (Direction against : Direction.values()) { - BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).relative(against); - BlockState placeAgainstState = bsi.get0(placeAgainstPos); - if (MovementHelper.isReplaceable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { - continue; - } - if (placeAgainstState.getBlock() instanceof AirBlock) { - // isReplacable used to check for this, but we changed that. - continue; - } - if (!toPlace.canSurvive(ctx.world(), new BetterBlockPos(x, y, z))) { - continue; - } - if (!placementPlausible(new BetterBlockPos(x, y, z), toPlace)) { - continue; - } - AABB aabb = placeAgainstState.getShape(ctx.world(), placeAgainstPos).bounds(); - for (Vec3 placementMultiplier : aabbSideMultipliers(against)) { - double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); - double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); - double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); - Rotation rot = RotationUtils.calcRotationFromVec3d(RayTraceUtils.inferSneakingEyePosition(ctx.player()), new Vec3(placeX, placeY, placeZ), ctx.playerRotations()); - HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance(), true); - if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(placeAgainstPos) && ((BlockHitResult) result).getDirection() == against.getOpposite()) { - OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); - if (hotbar.isPresent()) { - return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); - } - } - } - } - return Optional.empty(); - } - - @Override - public boolean isFromAltoclef() { - return this.fromAltoclef; - } - - @Override - public void build(String name, ISchematic schematic, Vec3i origin, boolean fromAltoclef) { - this.build(name, schematic, origin); - this.fromAltoclef = fromAltoclef; - } - - @Override - public boolean build(String name, File schematic, Vec3i origin, boolean fromAltoclef) { - final boolean bl = this.build(name, schematic, origin); - this.fromAltoclef = fromAltoclef; - return bl; - } - private OptionalInt hasAnyItemThatWouldPlace(BlockState desired, HitResult result, Rotation rot) { for (int i = 0; i < 9; i++) { ItemStack stack = ctx.player().getInventory().items.get(i); @@ -577,8 +509,7 @@ private OptionalInt hasAnyItemThatWouldPlace(BlockState desired, HitResult resul InteractionHand.MAIN_HAND, stack, (BlockHitResult) result - ) { - }); // that {} gives us access to a protected constructor lmfao + ) {}); // that {} gives us access to a protected constructor lmfao BlockState wouldBePlaced = ((BlockItem) stack.getItem()).getBlock().getStateForPlacement(meme); ctx.player().setYRot(originalYaw); ctx.player().setXRot(originalPitch); @@ -595,6 +526,37 @@ private OptionalInt hasAnyItemThatWouldPlace(BlockState desired, HitResult resul return OptionalInt.empty(); } + private Optional possibleToPlace(BlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { + for (Direction against : Direction.values()) { + BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).relative(against); + BlockState placeAgainstState = bsi.get0(placeAgainstPos); + if (MovementHelper.isReplaceable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { + continue; + } + if (!toPlace.canSurvive(ctx.world(), new BetterBlockPos(x, y, z))) { + continue; + } + if (!placementPlausible(new BetterBlockPos(x, y, z), toPlace)) { + continue; + } + AABB aabb = placeAgainstState.getShape(ctx.world(), placeAgainstPos).bounds(); + for (Vec3 placementMultiplier : aabbSideMultipliers(against)) { + double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); + double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); + double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); + Rotation rot = RotationUtils.calcRotationFromVec3d(RayTraceUtils.inferSneakingEyePosition(ctx.player()), new Vec3(placeX, placeY, placeZ), ctx.playerRotations()); + HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance(), true); + if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(placeAgainstPos) && ((BlockHitResult) result).getDirection() == against.getOpposite()) { + OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); + if (hotbar.isPresent()) { + return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); + } + } + } + } + return Optional.empty(); + } + private BlockPos getFromHistory(final BlockPos pos) { if (pos == null) return null; @@ -612,12 +574,27 @@ private boolean anyHistoryMatch(final BlockPos pos) { return getFromHistory(pos) != null; //blockBreakHistory.keySet().stream().anyMatch(e -> e.getX() == pos.getX() && e.getY() == pos.getY() && e.getZ() == pos.getZ()); } - private boolean breakLimitExceeded(final BlockPos pos) { - if (!anyHistoryMatch(pos)) { - return false; + private static Vec3[] aabbSideMultipliers(Direction side) { + switch (side) { + case UP: + return new Vec3[]{new Vec3(0.5, 1, 0.5), new Vec3(0.1, 1, 0.5), new Vec3(0.9, 1, 0.5), new Vec3(0.5, 1, 0.1), new Vec3(0.5, 1, 0.9)}; + case DOWN: + return new Vec3[]{new Vec3(0.5, 0, 0.5), new Vec3(0.1, 0, 0.5), new Vec3(0.9, 0, 0.5), new Vec3(0.5, 0, 0.1), new Vec3(0.5, 0, 0.9)}; + case NORTH: + case SOUTH: + case EAST: + case WEST: + double x = side.getStepX() == 0 ? 0.5 : (1 + side.getStepX()) / 2D; + double z = side.getStepZ() == 0 ? 0.5 : (1 + side.getStepZ()) / 2D; + return new Vec3[]{new Vec3(x, 0.25, z), new Vec3(x, 0.75, z)}; + default: // null + throw new IllegalStateException(); } + } - return blockBreakHistory.get(getFromHistory(pos)).counter > 10; + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + return onTick(calcFailed, isSafeToCancel, 0); } private void noteRemoval(final BlockPos pos) { @@ -631,41 +608,33 @@ private void noteRemoval(final BlockPos pos) { } } - @Override - public void noteInsert(final BlockPos pos) { - if (anyHistoryMatch(pos)) { - final BlockPos orig = getFromHistory(pos); - final HistoryInfo info = blockBreakHistory.get(orig); - if (info.brokenPreviously) { - info.counter++; - info.brokenPreviously = false; + private boolean recalc(BuilderCalculationContext bcc) { + if (incorrectPositions == null) { + incorrectPositions = new HashSet<>(); + fullRecalc(bcc); + if (incorrectPositions.isEmpty()) { + return false; } - } else { - blockBreakHistory.put(pos, new HistoryInfo()); } + recalcNearby(bcc); + if (incorrectPositions.isEmpty()) { + fullRecalc(bcc); + } + return !incorrectPositions.isEmpty(); } - /*private long getHistoryCount(final BlockPos pos) { - if(!anyHistoryMatch(pos)) { - return 0; + private void trim() { + HashSet copy = new HashSet<>(incorrectPositions); + copy.removeIf(pos -> pos.distSqr(ctx.player().blockPosition()) > 200); + if (!copy.isEmpty()) { + incorrectPositions = copy; } - - return blockBreakHistory.get(getFromHistory(pos)).counter; - }*/ - - @Override - public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { - return onTick(calcFailed, isSafeToCancel, 0); } public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel, int recursions) { - if (Trail.getInstance().updateAndCheck()) { - return Trail.getInstance().getRunAwayCommand(); - } if (recursions > 1000) { // onTick calls itself, don't crash return new PathingCommand(null, PathingCommandType.SET_GOAL_AND_PATH); } - protectItemOfMissing(); approxPlaceable = approxPlaceable(36); if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { ticks = 5; @@ -724,10 +693,9 @@ public int lengthZ() { } }; } - BuilderCalculationContext bcc = new BuilderCalculationContext(); if (!recalc(bcc)) { - if (Baritone.settings().buildInLayers.value && layer * Baritone.settings().layerHeight.value < realSchematic.heightY()) { + if (Baritone.settings().buildInLayers.value && layer * Baritone.settings().layerHeight.value < stopAtHeight) { logDirect("Starting layer " + layer); layer++; return onTick(calcFailed, isSafeToCancel, recursions + 1); @@ -740,17 +708,13 @@ public int lengthZ() { if (Baritone.settings().notificationOnBuildFinished.value) { logNotification("Done building", false); } - if (this.fromAltoclef) { this.fromAltoclefFinished = true; } - - reset(); - + onLostControl(); if (this.fromAltoclefFinished) { this.stateStack.clear(); } - return null; } // build repeat time @@ -762,9 +726,12 @@ public int lengthZ() { logDirect("Repeating build in vector " + repeat + ", new origin is " + origin); return onTick(calcFailed, isSafeToCancel, recursions + 1); } + if (Baritone.settings().distanceTrim.value) { + trim(); + } Optional> toBreak = toBreakNearPlayer(bcc); - if (toBreak.isPresent() && isSafeToCancel && ctx.player().isOnGround()) { + if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround()) { // we'd like to pause to break this block // only change look direction if it's safe (don't want to fuck up an in progress parkour for example Rotation rot = toBreak.get().getB(); @@ -786,7 +753,8 @@ public int lengthZ() { } List desirableOnHotbar = new ArrayList<>(); Optional toPlace = searchForPlacables(bcc, desirableOnHotbar); - if (!AltoClefSettings.getInstance().isInteractionPaused() && toPlace.isPresent() && isSafeToCancel && ctx.player().isOnGround() && ticks <= 0) { + if (!AltoClefSettings.getInstance().isInteractionPaused() && toPlace.isPresent() && isSafeToCancel && + ctx.player().onGround() && ticks <= 0) { Rotation rot = toPlace.get().rot; baritone.getLookBehavior().updateTarget(rot, true); ctx.player().getInventory().selected = toPlace.get().hotbarSelection; @@ -837,43 +805,10 @@ public int lengthZ() { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } - updateMovement(); return new PathingCommandContext(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH, bcc); } - private void updateMovement() { - PathExecutor exec = baritone.getPathingBehavior().getCurrent(); - if (exec == null || exec.finished() || exec.failed()) { - return; - } - Movement movement = (Movement) exec.getPath().movements().get(exec.getPosition()); - movement.update(); - } - - private boolean recalc(BuilderCalculationContext bcc) { - if (incorrectPositions == null) { - incorrectPositions = new HashSet<>(); - fullRecalc(bcc); - if (incorrectPositions.isEmpty()) { - return false; - } - } - recalcNearby(bcc); - if (incorrectPositions.isEmpty()) { - fullRecalc(bcc); - } - return !incorrectPositions.isEmpty(); - } - - /*private void trim() { - HashSet copy = new HashSet<>(incorrectPositions); - copy.removeIf(pos -> pos.distSqr(ctx.player().blockPosition()) > 200); - if (!copy.isEmpty()) { - incorrectPositions = copy; - } - }*/ - private void recalcNearby(BuilderCalculationContext bcc) { BetterBlockPos center = ctx.playerFeet(); int radius = Baritone.settings().builderTickScanRadius.value; @@ -887,7 +822,7 @@ private void recalcNearby(BuilderCalculationContext bcc) { if (desired != null) { // we care about this position BetterBlockPos pos = new BetterBlockPos(x, y, z); - if (valid(bcc.bsi.get0(x, y, z), desired, false) || breakLimitExceeded(new BlockPos(x, y, z))) { + if (valid(bcc.bsi.get0(x, y, z), desired, false)) { incorrectPositions.remove(pos); observedCompleted.add(BetterBlockPos.longHash(pos)); } else { @@ -914,7 +849,7 @@ private void fullRecalc(BuilderCalculationContext bcc) { } if (bcc.bsi.worldContainsLoadedChunk(blockX, blockZ)) { // check if its in render distance, not if its in cache // we can directly observe this block, it is in render distance - if (valid(bcc.bsi.get0(blockX, blockY, blockZ), schematic.desiredState(x, y, z, current, this.approxPlaceable), false) || breakLimitExceeded(new BlockPos(x, y, z))) { + if (valid(bcc.bsi.get0(blockX, blockY, blockZ), schematic.desiredState(x, y, z, current, this.approxPlaceable), false)) { observedCompleted.add(BetterBlockPos.longHash(blockX, blockY, blockZ)); } else { incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); @@ -944,23 +879,13 @@ private Goal assemble(BuilderCalculationContext bcc, List approxPlac return assemble(bcc, approxPlaceable, false); } - @Override - public String displayName0() { - return paused ? "Builder Paused" : "Building " + name; - } - - private boolean sameWithoutOrientation(BlockState first, BlockState second) { - if (first.getBlock() != second.getBlock()) { - return false; - } - ImmutableMap, Comparable> map1 = first.getValues(); - ImmutableMap, Comparable> map2 = second.getValues(); - for (Property prop : map1.keySet()) { - if (map1.get(prop) != map2.get(prop) && !orientationProps.contains(prop)) { - return false; - } + private void updateMovement() { + PathExecutor exec = baritone.getPathingBehavior().getCurrent(); + if (exec == null || exec.finished() || exec.failed()) { + return; } - return true; + Movement movement = (Movement) exec.getPath().movements().get(exec.getPosition()); + movement.update(); } private boolean isDefaultState(final BlockState state) { @@ -976,48 +901,55 @@ private boolean isDefaultState(final BlockState state) { return true; } - /*private boolean inInventoryWithUnderDifferentProperties(final BlockState state) { - final List propertyContainers = state.getValues().entrySet().stream().map(PROPERTY_ENTRY_TO_STRING_FUNCTION).collect(Collectors.toList()); - for (final PropertyContainer container : propertyContainers) { - + private Goal placementGoal(BlockPos pos, BuilderCalculationContext bcc) { + if (!(ctx.world().getBlockState(pos).getBlock() instanceof AirBlock)) { // TODO can this even happen? + return new GoalPlace(pos); } + boolean allowSameLevel = !(ctx.world().getBlockState(pos.above()).getBlock() instanceof AirBlock); + BlockState current = ctx.world().getBlockState(pos); + for (Direction facing : Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP) { + //noinspection ConstantConditions + if (MovementHelper.canPlaceAgainst(ctx, pos.relative(facing)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current))) { + return new GoalAdjacent(pos, pos.relative(facing), allowSameLevel); + } + } + return new GoalPlace(pos); + } - return false; - }*/ - - /*private final List asInventoryList(final List placeables) { - final List invStates = new ArrayList<>(); - placeables.forEach(e -> { - final BlockState defaultState = e.getBlock().defaultBlockState(); - invStates.removeIf(a -> a.toString().equals(defaultState.toString())); - invStates.add(defaultState); - }); - - return invStates; - }*/ + private Goal breakGoal(BlockPos pos, BuilderCalculationContext bcc) { + if (Baritone.settings().goalBreakFromAbove.value && bcc.bsi.get0(pos.above()).getBlock() instanceof AirBlock && bcc.bsi.get0(pos.above(2)).getBlock() instanceof AirBlock) { // TODO maybe possible without the up(2) check? + return new JankyGoalComposite(new GoalBreak(pos), new GoalGetToBlock(pos.above()) { + @Override + public boolean isInGoal(int x, int y, int z) { + if (y > this.y || (x == this.x && y == this.y && z == this.z)) { + return false; + } + return super.isInGoal(x, y, z); + } + }); + } + return new GoalBreak(pos); + } private Goal assemble(BuilderCalculationContext bcc, List approxPlaceable, boolean logMissing) { List placeable = new ArrayList<>(); List breakable = new ArrayList<>(); List sourceLiquids = new ArrayList<>(); List flowingLiquids = new ArrayList<>(); - //stopProtectItemOfMissing(); - missing.clear(); + Map missing = new HashMap<>(); incorrectPositions.forEach(pos -> { BlockState state = bcc.bsi.get0(pos); if (state.getBlock() instanceof AirBlock) { final BlockState bsSchematic = bcc.getSchematic(pos.x, pos.y, pos.z, state); - if (approxPlaceable.stream().anyMatch(e -> e != null && e.getBlock().defaultBlockState().equals(bsSchematic.getBlock().defaultBlockState())) - //|| approxPlaceable.stream().anyMatch(e -> e != null && !(e.getBlock() instanceof AirBlock) && e.getBlock().getClass().equals(state.getBlock().getClass())) - //&& !hasNonHoldable(state) - ) { + if (approxPlaceable.contains(bcc.getSchematic(pos.x, pos.y, pos.z, state))) { + assert bsSchematic != null; if (isDefaultState(bsSchematic)) { placeable.add(pos); } } else { BlockState desired = bcc.getSchematic(pos.x, pos.y, pos.z, state); if (desired != null) { - missing.put(desired.getBlock().defaultBlockState(), 1 + missing.getOrDefault(desired, 0)); + missing.put(desired, 1 + missing.getOrDefault(desired, 0)); } } } else { @@ -1035,10 +967,8 @@ private Goal assemble(BuilderCalculationContext bcc, List approxPlac } } }); - List toBreak = new ArrayList<>(); breakable.forEach(pos -> toBreak.add(breakGoal(pos, bcc))); - List toPlace = new ArrayList<>(); placeable.forEach(pos -> { if (!placeable.contains(pos.below()) && !placeable.contains(pos.below(2))) { @@ -1054,7 +984,6 @@ private Goal assemble(BuilderCalculationContext bcc, List approxPlac if (!missing.isEmpty()) { protectItemOfMissing(); } - if (logMissing && !missing.isEmpty()) { logDirect("Missing materials for at least:"); logDirect(missing.entrySet().stream() @@ -1142,95 +1071,9 @@ public void logDirect(String message) { super.logDirect(message); } - public static class Placement { - - private final int hotbarSelection; - private final BlockPos placeAgainst; - private final Direction side; - private final Rotation rot; - - public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot) { - this.hotbarSelection = hotbarSelection; - this.placeAgainst = placeAgainst; - this.side = side; - this.rot = rot; - } - } - - public static class JankyGoalComposite implements Goal { - - private final Goal primary; - private final Goal fallback; - - public JankyGoalComposite(Goal primary, Goal fallback) { - this.primary = primary; - this.fallback = fallback; - } - - - @Override - public boolean isInGoal(int x, int y, int z) { - return primary.isInGoal(x, y, z) || fallback.isInGoal(x, y, z); - } - - @Override - public double heuristic(int x, int y, int z) { - return primary.heuristic(x, y, z); - } - - @Override - public String toString() { - return "JankyComposite Primary: " + primary + " Fallback: " + fallback; - } - } - - private Goal placementGoal(BlockPos pos, BuilderCalculationContext bcc) { - if (!(ctx.world().getBlockState(pos).getBlock() instanceof AirBlock)) { // TODO can this even happen? - return new GoalPlace(pos); - } - boolean allowSameLevel = !(ctx.world().getBlockState(pos.above()).getBlock() instanceof AirBlock); - BlockState current = ctx.world().getBlockState(pos); - for (Direction facing : Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP) { - //noinspection ConstantConditions - if (MovementHelper.canPlaceAgainst(ctx, pos.relative(facing)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ(), current))) { - return new GoalAdjacent(pos, pos.relative(facing), allowSameLevel); - } - } - noteInsert(pos); - return new GoalPlace(pos); - } - - private Goal breakGoal(BlockPos pos, BuilderCalculationContext bcc) { - if (Baritone.settings().goalBreakFromAbove.value && bcc.bsi.get0(pos.above()).getBlock() instanceof AirBlock && bcc.bsi.get0(pos.above(2)).getBlock() instanceof AirBlock) { - return new JankyGoalComposite(new GoalBreak(pos), new GoalGetToBlock(pos.above()) { - @Override - public boolean isInGoal(int x, int y, int z) { - if (y > this.y || (x == this.x && y == this.y && z == this.z)) { - return false; - } - return super.isInGoal(x, y, z); - } - }); - } - //TODO: maybe noteRemoval here? - return new GoalBreak(pos); - } - - public static class GoalBreak extends GoalGetToBlock { - - public GoalBreak(BlockPos pos) { - super(pos); - } - - @Override - public boolean isInGoal(int x, int y, int z) { - // can't stand right on top of a block, that might not work (what if it's unsupported, can't break then) - if (y > this.y) { - return false; - } - // but any other adjacent works for breaking, including inside or below - return super.isInGoal(x, y, z); - } + @Override + public String displayName0() { + return paused ? "Builder Paused" : "Building " + name; } public static class GoalAdjacent extends GoalGetToBlock { @@ -1271,7 +1114,6 @@ public void onLostControl() { if (this.fromAltoclef && this.stateStack.isEmpty()) { pushState(); } - incorrectPositions = null; name = null; schematic = null; @@ -1288,86 +1130,114 @@ public void onLostControl() { blockBreakHistory.clear(); } - @Override - public double priority() { - return super.priority(); - } - - @Override - public String displayName() { - return super.displayName(); - } - - public static class GoalPlace extends GoalBlock { - - public GoalPlace(BlockPos placeAt) { - super(placeAt.above()); - } - - public double heuristic(int x, int y, int z) { - // prioritize lower y coordinates - return this.y * 100 + super.heuristic(x, y, z); - } - } - private List approxPlaceable(int size) { List result = new ArrayList<>(); for (int i = 0; i < size; i++) { ItemStack stack = ctx.player().getInventory().items.get(i); - //if (stack.getItem().toString() != "air") System.out.println("BARIT1: " + stack.getItem().toString()); if (stack.isEmpty() || !(stack.getItem() instanceof BlockItem)) { result.add(Blocks.AIR.defaultBlockState()); continue; } - //if (stack.getItem().toString() != "air") System.out.println("BARIT2: " + stack.getItem().toString()); - /*final Vec3 playerPos = new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z); - final BlockHitResult playerFeetHitResult = new BlockHitResult(playerPos, Direction.UP, ctx.playerFeet(), false); - final UseOnContext usageMainHandAction = new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, playerFeetHitResult) {}; - final BlockPlaceContext blockPlacementContext = new BlockPlaceContext(usageMainHandAction); - final Block targetBlock = ((BlockItem) stack.getItem()).getBlock(); - final BlockState targetState = targetBlock.getStateForPlacement(blockPlacementContext);*/ + // + BlockState itemState = ((BlockItem) stack.getItem()) + .getBlock() + .getStateForPlacement( + new BlockPlaceContext( + new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) { + } + ) + ); + if (itemState != null) { + result.add(itemState); + } else { + result.add(Blocks.AIR.defaultBlockState()); + } + // + } + return result; + } + + public static class JankyGoalComposite implements Goal { + + private final Goal primary; + private final Goal fallback; - final Block targetBlock = ((BlockItem) stack.getItem()).getBlock(); - final BlockState targetState = targetBlock.getStateDefinition().getPossibleStates().get(0); + public JankyGoalComposite(Goal primary, Goal fallback) { + this.primary = primary; + this.fallback = fallback; + } - //if (stack.getItem().toString() != "air") System.out.println("BARIT3: " + targetState); - result.add(targetState); - //result.add(((BlockItem) stack.getItem()).getBlock().getStateForPlacement(new BlockPlaceContext(new UseOnContext(ctx.world(), ctx.player(), InteractionHand.MAIN_HAND, stack, new BlockHitResult(new Vec3(ctx.player().position().x, ctx.player().position().y, ctx.player().position().z), Direction.UP, ctx.playerFeet(), false)) {}))); + @Override + public boolean isInGoal(int x, int y, int z) { + return primary.isInGoal(x, y, z) || fallback.isInGoal(x, y, z); + } + + @Override + public double heuristic(int x, int y, int z) { + return primary.heuristic(x, y, z); + } + + @Override + public String toString() { + return "JankyComposite Primary: " + primary + " Fallback: " + fallback; } - return result; } - private final class HistoryInfo { - public long counter = 0; - public boolean brokenPreviously = false; + public static class GoalBreak extends GoalGetToBlock { + + public GoalBreak(BlockPos pos) { + super(pos); + } + + @Override + public boolean isInGoal(int x, int y, int z) { + // can't stand right on top of a block, that might not work (what if it's unsupported, can't break then) + if (y > this.y) { + return false; + } + // but any other adjacent works for breaking, including inside or below + return super.isInGoal(x, y, z); + } } - private final class PropertyContainer { - private final String propertyKey; - private final String propertyValue; + public static class GoalPlace extends GoalBlock { - public PropertyContainer(final String propertyKey, final String propertyValue) { - this.propertyKey = propertyKey; - this.propertyValue = propertyValue; + public GoalPlace(BlockPos placeAt) { + super(placeAt.above()); } - public String getPropertyKey() { - return this.propertyKey; + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); } + } - public String getPropertyValue() { - return this.propertyValue; + private boolean sameBlockstate(BlockState first, BlockState second) { + if (first.getBlock() != second.getBlock()) { + return false; + } + boolean ignoreDirection = Baritone.settings().buildIgnoreDirection.value; + List ignoredProps = Baritone.settings().buildIgnoreProperties.value; + if (!ignoreDirection && ignoredProps.isEmpty()) { + return first.equals(second); // early return if no properties are being ignored } + ImmutableMap, Comparable> map1 = first.getValues(); + ImmutableMap, Comparable> map2 = second.getValues(); + for (Property prop : map1.keySet()) { + if (map1.get(prop) != map2.get(prop) + && !(ignoreDirection && orientationProps.contains(prop)) + && !ignoredProps.contains(prop.getName())) { + return false; + } + } + return true; } private boolean valid(BlockState current, BlockState desired, boolean itemVerify) { if (desired == null) { return true; } - if (current == null) { - return true; // No idea why but current might be null..... oof - } if (current.getBlock() instanceof LiquidBlock && Baritone.settings().okIfWater.value) { return true; } @@ -1383,27 +1253,13 @@ private boolean valid(BlockState current, BlockState desired, boolean itemVerify if (Baritone.settings().buildSkipBlocks.value.contains(desired.getBlock()) && !itemVerify) { return true; } - if (desired.getBlock() instanceof SlabBlock) { - return true; - } - if (current.getBlock() instanceof ChestBlock && desired.getBlock() instanceof ChestBlock) { - return true; - } - if (current.getBlock() instanceof DoorBlock && desired.getBlock() instanceof DoorBlock) { - return true; - } - if (current.getBlock().defaultBlockState().equals(desired.getBlock().defaultBlockState())) { - if (current.getBlock() instanceof CrossCollisionBlock) { - return true; - } - } if (Baritone.settings().buildValidSubstitutes.value.getOrDefault(desired.getBlock(), Collections.emptyList()).contains(current.getBlock()) && !itemVerify) { return true; } if (current.equals(desired)) { return true; } - return Baritone.settings().buildIgnoreDirection.value && sameWithoutOrientation(current, desired); + return sameBlockstate(current, desired); } public class BuilderCalculationContext extends CalculationContext { @@ -1470,12 +1326,12 @@ public double costOfPlacingAt(int x, int y, int z, BlockState current) { @Override public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { - if (!allowBreak || isPossiblyProtected(x, y, z)) { + if ((!allowBreak && !allowBreakAnyway.contains(current.getBlock())) || isPossiblyProtected(x, y, z)) { return COST_INF; } BlockState sch = getSchematic(x, y, z, current); if (sch != null && !Baritone.settings().buildSkipBlocks.value.contains(sch.getBlock())) { - if (sch.getBlock() == Blocks.AIR) { + if (sch.getBlock() instanceof AirBlock) { // it should be air // regardless of current contents, we can break it return 1; @@ -1498,4 +1354,27 @@ public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { } } } + + private final class PropertyContainer { + private final String propertyKey; + private final String propertyValue; + + public PropertyContainer(final String propertyKey, final String propertyValue) { + this.propertyKey = propertyKey; + this.propertyValue = propertyValue; + } + + public String getPropertyKey() { + return this.propertyKey; + } + + public String getPropertyValue() { + return this.propertyValue; + } + } + + private final class HistoryInfo { + public long counter = 0; + public boolean brokenPreviously = false; + } } diff --git a/src/main/java/baritone/process/CustomGoalProcess.java b/src/main/java/baritone/process/CustomGoalProcess.java index 3ee3e7b7c..d0dca9cbf 100644 --- a/src/main/java/baritone/process/CustomGoalProcess.java +++ b/src/main/java/baritone/process/CustomGoalProcess.java @@ -36,6 +36,11 @@ public final class CustomGoalProcess extends BaritoneProcessHelper implements IC */ private Goal goal; + /** + * The most recent goal. Not invalidated upon {@link #onLostControl()} + */ + private Goal mostRecentGoal; + /** * The current process state. * @@ -50,6 +55,10 @@ public CustomGoalProcess(Baritone baritone) { @Override public void setGoal(Goal goal) { this.goal = goal; + this.mostRecentGoal = goal; + if (baritone.getElytraProcess().isActive()) { + baritone.getElytraProcess().pathTo(goal); + } if (this.state == State.NONE) { this.state = State.GOAL_SET; } @@ -68,6 +77,11 @@ public Goal getGoal() { return this.goal; } + @Override + public Goal mostRecentGoal() { + return this.mostRecentGoal; + } + @Override public boolean isActive() { return this.state != State.NONE; diff --git a/src/main/java/baritone/process/ElytraProcess.java b/src/main/java/baritone/process/ElytraProcess.java new file mode 100644 index 000000000..082e3d6b6 --- /dev/null +++ b/src/main/java/baritone/process/ElytraProcess.java @@ -0,0 +1,569 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.IBaritone; +import baritone.api.event.events.*; +import baritone.api.event.events.type.EventState; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.pathing.goals.GoalYLevel; +import baritone.api.pathing.movement.IMovement; +import baritone.api.pathing.path.IPathExecutor; +import baritone.api.process.IBaritoneProcess; +import baritone.api.process.IElytraProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Rotation; +import baritone.api.utils.RotationUtils; +import baritone.api.utils.input.Input; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.movements.MovementFall; +import baritone.process.elytra.ElytraBehavior; +import baritone.process.elytra.NetherPathfinderContext; +import baritone.process.elytra.NullElytraProcess; +import baritone.utils.BaritoneProcessHelper; +import baritone.utils.PathingCommandContext; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; + +import java.util.*; + +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + +public class ElytraProcess extends BaritoneProcessHelper implements IBaritoneProcess, IElytraProcess, AbstractGameEventListener { + public State state; + private boolean goingToLandingSpot; + private BetterBlockPos landingSpot; + private boolean reachedGoal; // this basically just prevents potential notification spam + private Goal goal; + private ElytraBehavior behavior; + private boolean predictingTerrain; + + @Override + public void onLostControl() { + this.state = State.START_FLYING; // TODO: null state? + this.goingToLandingSpot = false; + this.landingSpot = null; + this.reachedGoal = false; + this.goal = null; + destroyBehaviorAsync(); + } + + private ElytraProcess(Baritone baritone) { + super(baritone); + baritone.getGameEventHandler().registerEventListener(this); + } + + public static T create(final Baritone baritone) { + return (T) (NetherPathfinderContext.isSupported() + ? new ElytraProcess(baritone) + : new NullElytraProcess(baritone)); + } + + @Override + public boolean isActive() { + return this.behavior != null; + } + + @Override + public void resetState() { + BlockPos destination = this.currentDestination(); + this.onLostControl(); + if (destination != null) { + this.pathTo(destination); + this.repackChunks(); + } + } + + private static final String AUTO_JUMP_FAILURE_MSG = "Failed to compute a walking path to a spot to jump off from. Consider starting from a higher location, near an overhang. Or, you can disable elytraAutoJump and just manually begin gliding."; + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + final long seedSetting = Baritone.settings().elytraNetherSeed.value; + if (seedSetting != this.behavior.context.getSeed()) { + logDirect("Nether seed changed, recalculating path"); + this.resetState(); + } + if (predictingTerrain != Baritone.settings().elytraPredictTerrain.value) { + logDirect("elytraPredictTerrain setting changed, recalculating path"); + predictingTerrain = Baritone.settings().elytraPredictTerrain.value; + this.resetState(); + } + + this.behavior.onTick(); + + if (calcFailed) { + onLostControl(); + logDirect(AUTO_JUMP_FAILURE_MSG); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + + boolean safetyLanding = false; + if (ctx.player().isFallFlying() && shouldLandForSafety()) { + if (Baritone.settings().elytraAllowEmergencyLand.value) { + logDirect("Emergency landing - almost out of elytra durability or fireworks"); + safetyLanding = true; + } else { + logDirect("almost out of elytra durability or fireworks, but I'm going to continue since elytraAllowEmergencyLand is false"); + } + } + if (ctx.player().isFallFlying() && this.state != State.LANDING && (this.behavior.pathManager.isComplete() || safetyLanding)) { + final BetterBlockPos last = this.behavior.pathManager.path.getLast(); + if (last != null && (ctx.player().position().distanceToSqr(last.getCenter()) < (48 * 48) || safetyLanding) && (!goingToLandingSpot || (safetyLanding && this.landingSpot == null))) { + logDirect("Path complete, picking a nearby safe landing spot..."); + BetterBlockPos landingSpot = findSafeLandingSpot(ctx.playerFeet()); + // if this fails we will just keep orbiting the last node until we run out of rockets or the user intervenes + if (landingSpot != null) { + this.pathTo0(landingSpot, true); + this.landingSpot = landingSpot; + } + this.goingToLandingSpot = true; + } + + if (last != null && ctx.player().position().distanceToSqr(last.getCenter()) < 1) { + if (Baritone.settings().notificationOnPathComplete.value && !reachedGoal) { + logNotification("Pathing complete", false); + } + if (Baritone.settings().disconnectOnArrival.value && !reachedGoal) { + // don't be active when the user logs back in + this.onLostControl(); + ctx.world().disconnect(); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + reachedGoal = true; + + // we are goingToLandingSpot and we are in the last node of the path + if (this.goingToLandingSpot) { + this.state = State.LANDING; + logDirect("Above the landing spot, landing..."); + } + } + } + + if (this.state == State.LANDING) { + final BetterBlockPos endPos = this.landingSpot != null ? this.landingSpot : behavior.pathManager.path.getLast(); + if (ctx.player().isFallFlying() && endPos != null) { + Vec3 from = ctx.player().position(); + Vec3 to = new Vec3(((double) endPos.x) + 0.5, from.y, ((double) endPos.z) + 0.5); + Rotation rotation = RotationUtils.calcRotationFromVec3d(from, to, ctx.playerRotations()); + baritone.getLookBehavior().updateTarget(new Rotation(rotation.getYaw(), 0), false); // this will be overwritten, probably, by behavior tick + + if (ctx.player().position().y < endPos.y - LANDING_COLUMN_HEIGHT) { + logDirect("bad landing spot, trying again..."); + landingSpotIsBad(endPos); + } + } + } + + if (ctx.player().isFallFlying()) { + behavior.landingMode = this.state == State.LANDING; + this.goal = null; + baritone.getInputOverrideHandler().clearAllKeys(); + behavior.tick(); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } else if (this.state == State.LANDING) { + if (ctx.playerMotion().multiply(1, 0, 1).length() > 0.001) { + logDirect("Landed, but still moving, waiting for velocity to die down... "); + baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + logDirect("Done :)"); + baritone.getInputOverrideHandler().clearAllKeys(); + this.onLostControl(); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + + if (this.state == State.FLYING || this.state == State.START_FLYING) { + this.state = ctx.player().onGround() && Baritone.settings().elytraAutoJump.value + ? State.LOCATE_JUMP + : State.START_FLYING; + } + + if (this.state == State.LOCATE_JUMP) { + if (shouldLandForSafety()) { + logDirect("Not taking off, because elytra durability or fireworks are so low that I would immediately emergency land anyway."); + onLostControl(); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + if (this.goal == null) { + this.goal = new GoalYLevel(31); + } + final IPathExecutor executor = baritone.getPathingBehavior().getCurrent(); + if (executor != null && executor.getPath().getGoal() == this.goal) { + final IMovement fall = executor.getPath().movements().stream() + .filter(movement -> movement instanceof MovementFall) + .findFirst().orElse(null); + + if (fall != null) { + final BetterBlockPos from = new BetterBlockPos( + (fall.getSrc().x + fall.getDest().x) / 2, + (fall.getSrc().y + fall.getDest().y) / 2, + (fall.getSrc().z + fall.getDest().z) / 2 + ); + behavior.pathManager.pathToDestination(from).whenComplete((result, ex) -> { + if (ex == null) { + this.state = State.GET_TO_JUMP; + return; + } + onLostControl(); + }); + this.state = State.PAUSE; + } else { + onLostControl(); + logDirect(AUTO_JUMP_FAILURE_MSG); + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + } + return new PathingCommandContext(this.goal, PathingCommandType.SET_GOAL_AND_PAUSE, new WalkOffCalculationContext(baritone)); + } + + // yucky + if (this.state == State.PAUSE) { + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + + if (this.state == State.GET_TO_JUMP) { + final IPathExecutor executor = baritone.getPathingBehavior().getCurrent(); + final boolean canStartFlying = ctx.player().fallDistance > 1.0f + && !isSafeToCancel + && executor != null + && executor.getPath().movements().get(executor.getPosition()) instanceof MovementFall; + + if (canStartFlying) { + this.state = State.START_FLYING; + } else { + return new PathingCommand(null, PathingCommandType.SET_GOAL_AND_PATH); + } + } + + if (this.state == State.START_FLYING) { + if (!isSafeToCancel) { + // owned + baritone.getPathingBehavior().secretInternalSegmentCancel(); + } + baritone.getInputOverrideHandler().clearAllKeys(); + if (ctx.player().fallDistance > 1.0f) { + baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + } + } + return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); + } + + public void landingSpotIsBad(BetterBlockPos endPos) { + badLandingSpots.add(endPos); + goingToLandingSpot = false; + this.landingSpot = null; + this.state = State.FLYING; + } + + private void destroyBehaviorAsync() { + ElytraBehavior behavior = this.behavior; + if (behavior != null) { + this.behavior = null; + Baritone.getExecutor().execute(behavior::destroy); + } + } + + @Override + public double priority() { + return 0; // higher priority than CustomGoalProcess + } + + @Override + public String displayName0() { + return "Elytra - " + this.state.description; + } + + @Override + public void repackChunks() { + if (this.behavior != null) { + this.behavior.repackChunks(); + } + } + + @Override + public BlockPos currentDestination() { + return this.behavior != null ? this.behavior.destination : null; + } + + @Override + public void pathTo(BlockPos destination) { + this.pathTo0(destination, false); + } + + private void pathTo0(BlockPos destination, boolean appendDestination) { + if (ctx.player() == null || ctx.player().level().dimension() != Level.NETHER) { + return; + } + this.onLostControl(); + this.predictingTerrain = Baritone.settings().elytraPredictTerrain.value; + this.behavior = new ElytraBehavior(this.baritone, this, destination, appendDestination); + if (ctx.world() != null) { + this.behavior.repackChunks(); + } + this.behavior.pathTo(); + } + + @Override + public void pathTo(Goal iGoal) { + final int x; + final int y; + final int z; + if (iGoal instanceof GoalXZ) { + GoalXZ goal = (GoalXZ) iGoal; + x = goal.getX(); + y = 64; + z = goal.getZ(); + } else if (iGoal instanceof GoalBlock) { + GoalBlock goal = (GoalBlock) iGoal; + x = goal.x; + y = goal.y; + z = goal.z; + } else { + throw new IllegalArgumentException("The goal must be a GoalXZ or GoalBlock"); + } + if (y <= 0 || y >= 128) { + throw new IllegalArgumentException("The y of the goal is not between 0 and 128"); + } + this.pathTo(new BlockPos(x, y, z)); + } + + private boolean shouldLandForSafety() { + ItemStack chest = ctx.player().getItemBySlot(EquipmentSlot.CHEST); + if (chest.getItem() != Items.ELYTRA || chest.getItem().getMaxDamage() - chest.getDamageValue() < Baritone.settings().elytraMinimumDurability.value) { + // elytrabehavior replaces when durability <= minimumDurability, so if durability < minimumDurability then we can reasonably assume that the elytra will soon be broken without replacement + return true; + } + + NonNullList inv = ctx.player().getInventory().items; + int qty = 0; + for (int i = 0; i < 36; i++) { + if (ElytraBehavior.isFireworks(inv.get(i))) { + qty += inv.get(i).getCount(); + } + } + if (qty <= Baritone.settings().elytraMinFireworksBeforeLanding.value) { + return true; + } + return false; + } + + @Override + public boolean isLoaded() { + return true; + } + + @Override + public boolean isSafeToCancel() { + return !this.isActive() || !(this.state == State.FLYING || this.state == State.START_FLYING); + } + + public enum State { + LOCATE_JUMP("Finding spot to jump off"), + PAUSE("Waiting for elytra path"), + GET_TO_JUMP("Walking to takeoff"), + START_FLYING("Begin flying"), + FLYING("Flying"), + LANDING("Landing"); + + public final String description; + + State(String desc) { + this.description = desc; + } + } + + @Override + public void onRenderPass(RenderEvent event) { + if (this.behavior != null) this.behavior.onRenderPass(event); + } + + @Override + public void onWorldEvent(WorldEvent event) { + if (event.getWorld() != null && event.getState() == EventState.POST) { + // Exiting the world, just destroy + destroyBehaviorAsync(); + } + } + + @Override + public void onChunkEvent(ChunkEvent event) { + if (this.behavior != null) this.behavior.onChunkEvent(event); + } + + @Override + public void onBlockChange(BlockChangeEvent event) { + if (this.behavior != null) this.behavior.onBlockChange(event); + } + + @Override + public void onReceivePacket(PacketEvent event) { + if (this.behavior != null) this.behavior.onReceivePacket(event); + } + + @Override + public void onPostTick(TickEvent event) { + IBaritoneProcess procThisTick = baritone.getPathingControlManager().mostRecentInControl().orElse(null); + if (this.behavior != null && procThisTick == this) this.behavior.onPostTick(event); + } + + /** + * Custom calculation context which makes the player fall into lava + */ + public static final class WalkOffCalculationContext extends CalculationContext { + + public WalkOffCalculationContext(IBaritone baritone) { + super(baritone, true); + this.allowFallIntoLava = true; + this.minFallHeight = 8; + this.maxFallHeightNoWater = 10000; + } + + @Override + public double costOfPlacingAt(int x, int y, int z, BlockState current) { + return COST_INF; + } + + @Override + public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { + return COST_INF; + } + + @Override + public double placeBucketCost() { + return COST_INF; + } + } + + private static boolean isInBounds(BlockPos pos) { + return pos.getY() >= 0 && pos.getY() < 128; + } + + private boolean isSafeBlock(Block block) { + return block == Blocks.NETHERRACK || block == Blocks.GRAVEL || (block == Blocks.NETHER_BRICKS && Baritone.settings().elytraAllowLandOnNetherFortress.value); + } + + private boolean isSafeBlock(BlockPos pos) { + return isSafeBlock(ctx.world().getBlockState(pos).getBlock()); + } + + private boolean isAtEdge(BlockPos pos) { + return !isSafeBlock(pos.north()) + || !isSafeBlock(pos.south()) + || !isSafeBlock(pos.east()) + || !isSafeBlock(pos.west()) + // corners + || !isSafeBlock(pos.north().west()) + || !isSafeBlock(pos.north().east()) + || !isSafeBlock(pos.south().west()) + || !isSafeBlock(pos.south().east()); + } + + private boolean isColumnAir(BlockPos landingSpot, int minHeight) { + BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(landingSpot.getX(), landingSpot.getY(), landingSpot.getZ()); + final int maxY = mut.getY() + minHeight; + for (int y = mut.getY() + 1; y <= maxY; y++) { + mut.set(mut.getX(), y, mut.getZ()); + if (!(ctx.world().getBlockState(mut).getBlock() instanceof AirBlock)) { + return false; + } + } + return true; + } + + private boolean hasAirBubble(BlockPos pos) { + final int radius = 4; // Half of the full width, rounded down, as we're counting blocks in each direction from the center + BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(); + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + mut.set(pos.getX() + x, pos.getY() + y, pos.getZ() + z); + if (!(ctx.world().getBlockState(mut).getBlock() instanceof AirBlock)) { + return false; + } + } + } + } + + return true; + } + + private BetterBlockPos checkLandingSpot(BlockPos pos, LongOpenHashSet checkedSpots) { + BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(pos.getX(), pos.getY(), pos.getZ()); + while (mut.getY() >= 0) { + if (checkedSpots.contains(mut.asLong())) { + return null; + } + checkedSpots.add(mut.asLong()); + Block block = ctx.world().getBlockState(mut).getBlock(); + + if (isSafeBlock(block)) { + if (!isAtEdge(mut)) { + return new BetterBlockPos(mut); + } + return null; + } else if (block != Blocks.AIR) { + return null; + } + mut.set(mut.getX(), mut.getY() - 1, mut.getZ()); + } + return null; // void + } + + private static final int LANDING_COLUMN_HEIGHT = 15; + private Set badLandingSpots = new HashSet<>(); + + private BetterBlockPos findSafeLandingSpot(BetterBlockPos start) { + Queue queue = new PriorityQueue<>(Comparator.comparingInt(pos -> (pos.x - start.x) * (pos.x - start.x) + (pos.z - start.z) * (pos.z - start.z)).thenComparingInt(pos -> -pos.y)); + Set visited = new HashSet<>(); + LongOpenHashSet checkedPositions = new LongOpenHashSet(); + queue.add(start); + + while (!queue.isEmpty()) { + BetterBlockPos pos = queue.poll(); + if (ctx.world().isLoaded(pos) && isInBounds(pos) && ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) { + BetterBlockPos actualLandingSpot = checkLandingSpot(pos, checkedPositions); + if (actualLandingSpot != null && isColumnAir(actualLandingSpot, LANDING_COLUMN_HEIGHT) && hasAirBubble(actualLandingSpot.above(LANDING_COLUMN_HEIGHT)) && !badLandingSpots.contains(actualLandingSpot.above(LANDING_COLUMN_HEIGHT))) { + return actualLandingSpot.above(LANDING_COLUMN_HEIGHT); + } + if (visited.add(pos.north())) queue.add(pos.north()); + if (visited.add(pos.east())) queue.add(pos.east()); + if (visited.add(pos.south())) queue.add(pos.south()); + if (visited.add(pos.west())) queue.add(pos.west()); + if (visited.add(pos.above())) queue.add(pos.above()); + if (visited.add(pos.below())) queue.add(pos.below()); + } + } + return null; + } +} diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java index e1f053e02..23fe7c23a 100644 --- a/src/main/java/baritone/process/ExploreProcess.java +++ b/src/main/java/baritone/process/ExploreProcess.java @@ -32,14 +32,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; - import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; public final class ExploreProcess extends BaritoneProcessHelper implements IExploreProcess { diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index 6ad41d90e..bccb021c6 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -18,17 +18,19 @@ package baritone.process; import baritone.Baritone; +import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalGetToBlock; import baritone.api.pathing.goals.GoalComposite; import baritone.api.process.IFarmProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.BetterBlockPos; import baritone.api.utils.RayTraceUtils; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; -import baritone.cache.WorldScanner; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import net.minecraft.core.BlockPos; @@ -39,7 +41,15 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; +import net.minecraft.world.level.block.CactusBlock; +import net.minecraft.world.level.block.CocoaBlock; +import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.NetherWartBlock; +import net.minecraft.world.level.block.SugarCaneBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; @@ -83,6 +93,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro Items.POTATO, Items.CARROT, Items.NETHER_WART, + Items.COCOA_BEANS, Blocks.SUGAR_CANE.asItem(), Blocks.CACTUS.asItem() ); @@ -116,6 +127,7 @@ private enum Harvest { PUMPKIN(Blocks.PUMPKIN, state -> true), MELON(Blocks.MELON, state -> true), NETHERWART(Blocks.NETHER_WART, state -> state.getValue(NetherWartBlock.AGE) >= 3), + COCOA(Blocks.COCOA, state -> state.getValue(CocoaBlock.AGE) >= 2), SUGARCANE(Blocks.SUGAR_CANE, null) { @Override public boolean readyToHarvest(Level world, BlockPos pos, BlockState state) { @@ -173,6 +185,10 @@ private boolean isNetherWart(ItemStack stack) { return !stack.isEmpty() && stack.getItem().equals(Items.NETHER_WART); } + private boolean isCocoa(ItemStack stack) { + return !stack.isEmpty() && stack.getItem().equals(Items.COCOA_BEANS); + } + @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { ArrayList scan = new ArrayList<>(); @@ -181,13 +197,14 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { } if (Baritone.settings().replantCrops.value) { scan.add(Blocks.FARMLAND); + scan.add(Blocks.JUNGLE_LOG); if (Baritone.settings().replantNetherWart.value) { scan.add(Blocks.SOUL_SAND); } } if (Baritone.settings().mineGoalUpdateInterval.value != 0 && tickCount++ % Baritone.settings().mineGoalUpdateInterval.value == 0) { - Baritone.getExecutor().execute(() -> locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, scan, 256, 10, 10)); + Baritone.getExecutor().execute(() -> locations = BaritoneAPI.getProvider().getWorldScanner().scanChunkRadius(ctx, scan, 256, 10, 10)); } if (locations == null) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); @@ -196,6 +213,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { List openFarmland = new ArrayList<>(); List bonemealable = new ArrayList<>(); List openSoulsand = new ArrayList<>(); + List openLog = new ArrayList<>(); for (BlockPos pos : locations) { //check if the target block is out of range. if (range != 0 && pos.distSqr(center) > range * range) { @@ -216,13 +234,22 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { } continue; } + if (state.getBlock() == Blocks.JUNGLE_LOG) { + for (Direction direction : Direction.Plane.HORIZONTAL) { + if (ctx.world().getBlockState(pos.relative(direction)).getBlock() instanceof AirBlock) { + openLog.add(pos); + break; + } + } + continue; + } if (readyForHarvest(ctx.world(), pos, state)) { toBreak.add(pos); continue; } if (state.getBlock() instanceof BonemealableBlock) { BonemealableBlock ig = (BonemealableBlock) state.getBlock(); - if (ig.isValidBonemealTarget(ctx.world(), pos, state, true) && ig.isBonemealSuccess(ctx.world(), ctx.world().random, pos, state)) { + if (ig.isValidBonemealTarget(ctx.world(), pos, state) && ig.isBonemealSuccess(ctx.world(), ctx.world().random, pos, state)) { bonemealable.add(pos); } } @@ -244,7 +271,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { both.addAll(openSoulsand); for (BlockPos pos : both) { boolean soulsand = openSoulsand.contains(pos); - Optional rot = RotationUtils.reachableOffset(ctx.player(), pos, new Vec3(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance(), false); + Optional rot = RotationUtils.reachableOffset(ctx, pos, new Vec3(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance(), false); if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, soulsand ? this::isNetherWart : this::isPlantable)) { HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance()); if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == Direction.UP) { @@ -256,6 +283,25 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { } } } + for (BlockPos pos : openLog) { + for (Direction dir : Direction.Plane.HORIZONTAL) { + if (!(ctx.world().getBlockState(pos.relative(dir)).getBlock() instanceof AirBlock)) { + continue; + } + Vec3 faceCenter = Vec3.atCenterOf(pos).add(Vec3.atLowerCornerOf(dir.getNormal()).scale(0.5)); + Optional rot = RotationUtils.reachableOffset(ctx, pos, faceCenter, ctx.playerController().getBlockReachDistance(), false); + if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isCocoa)) { + HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance()); + if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == dir) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + } + } for (BlockPos pos : bonemealable) { Optional rot = RotationUtils.reachable(ctx, pos); if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) { @@ -290,17 +336,26 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { goalz.add(new GoalBlock(pos.above())); } } + if (baritone.getInventoryBehavior().throwaway(false, this::isCocoa)) { + for (BlockPos pos : openLog) { + for (Direction direction : Direction.Plane.HORIZONTAL) { + if (ctx.world().getBlockState(pos.relative(direction)).getBlock() instanceof AirBlock) { + goalz.add(new GoalGetToBlock(pos.relative(direction))); + } + } + } + } if (baritone.getInventoryBehavior().throwaway(false, this::isBoneMeal)) { for (BlockPos pos : bonemealable) { goalz.add(new GoalBlock(pos)); } } for (Entity entity : ctx.entities()) { - if (entity instanceof ItemEntity && entity.isOnGround()) { + if (entity instanceof ItemEntity && entity.onGround()) { ItemEntity ei = (ItemEntity) entity; if (PICKUP_DROPPED.contains(ei.getItem().getItem())) { // +0.1 because of farmland's 0.9375 dummy height lol - goalz.add(new GoalBlock(new BlockPos(entity.position().x, entity.position().y + 0.1, entity.position().z))); + goalz.add(new GoalBlock(new BetterBlockPos(entity.position().x, entity.position().y + 0.1, entity.position().z))); } } } diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index 9c6a5dfb7..b123216a6 100644 --- a/src/main/java/baritone/process/FollowProcess.java +++ b/src/main/java/baritone/process/FollowProcess.java @@ -25,13 +25,13 @@ import baritone.api.process.IFollowProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.BetterBlockPos; import baritone.utils.BaritoneProcessHelper; -import net.minecraft.core.BlockPos; -import net.minecraft.world.entity.Entity; - import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; /** * Follow an entity @@ -60,7 +60,7 @@ private Goal towards(Entity following) { pos = following.blockPosition(); } else { GoalXZ g = GoalXZ.fromDirection(following.position(), Baritone.settings().followOffsetDirection.value, Baritone.settings().followOffsetDistance.value); - pos = new BlockPos(g.getX(), following.position().y, g.getZ()); + pos = new BetterBlockPos(g.getX(), following.position().y, g.getZ()); } return new GoalNear(pos, Baritone.settings().followRadius.value); } diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 5233b6109..1352232d4 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -154,14 +154,18 @@ public synchronized boolean blacklistClosest() { return !newBlacklist.isEmpty(); } - private Goal createGoal(BlockPos pos) { - if (walkIntoInsteadOfAdjacent(gettingTo.getBlock())) { - return new GoalTwoBlocks(pos); + // this is to signal to MineProcess that we don't care about the allowBreak setting + // it is NOT to be used to actually calculate a path + public class GetToBlockCalculationContext extends CalculationContext { + + public GetToBlockCalculationContext(boolean forUseOnAnotherThread) { + super(GetToBlockProcess.super.baritone, forUseOnAnotherThread); } - if (blockOnTopMustBeRemoved(gettingTo.getBlock()) && MovementHelper.isBlockNormalCube(baritone.bsi.get0(pos.above()))) { // TODO this should be the check for chest openability - return new GoalBlock(pos.above()); + + @Override + public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { + return 1; } - return new GoalGetToBlock(pos); } // safer than direct double comparison from distanceSq @@ -195,23 +199,19 @@ private synchronized void rescan(List known, CalculationContext contex knownLocations = positions; } - // this is to signal to MineProcess that we don't care about the allowBreak setting - // it is NOT to be used to actually calculate a path - public class GetToBlockCalculationContext extends CalculationContext { - - public GetToBlockCalculationContext(boolean forUseOnAnotherThread) { - super(GetToBlockProcess.super.baritone, forUseOnAnotherThread); + private Goal createGoal(BlockPos pos) { + if (walkIntoInsteadOfAdjacent(gettingTo.getBlock())) { + return new GoalTwoBlocks(pos); } - - @Override - public double breakCostMultiplierAt(int x, int y, int z, BlockState current) { - return 1; + if (blockOnTopMustBeRemoved(gettingTo.getBlock()) && MovementHelper.isBlockNormalCube(baritone.bsi.get0(pos.above()))) { // TODO this should be the check for chest openability + return new GoalBlock(pos.above()); } + return new GoalGetToBlock(pos); } private boolean rightClick() { for (BlockPos pos : knownLocations) { - Optional reachable = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); + Optional reachable = RotationUtils.reachable(ctx, pos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { baritone.getLookBehavior().updateTarget(reachable.get(), true); if (knownLocations.contains(ctx.getSelectedBlock().orElse(null))) { diff --git a/src/main/java/baritone/process/InventoryPauserProcess.java b/src/main/java/baritone/process/InventoryPauserProcess.java new file mode 100644 index 000000000..fc9f4735a --- /dev/null +++ b/src/main/java/baritone/process/InventoryPauserProcess.java @@ -0,0 +1,90 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.utils.BaritoneProcessHelper; + +public class InventoryPauserProcess extends BaritoneProcessHelper { + + boolean pauseRequestedLastTick; + boolean safeToCancelLastTick; + int ticksOfStationary; + + public InventoryPauserProcess(Baritone baritone) { + super(baritone); + } + + @Override + public boolean isActive() { + if (ctx.player() == null || ctx.world() == null) { + return false; + } + return true; + } + + private double motion() { + return ctx.player().getDeltaMovement().multiply(1, 0, 1).length(); + } + + private boolean stationaryNow() { + return motion() < 0.00001; + } + + public boolean stationaryForInventoryMove() { + pauseRequestedLastTick = true; + return safeToCancelLastTick && ticksOfStationary > 1; + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + //logDebug(pauseRequestedLastTick + " " + safeToCancelLastTick + " " + ticksOfStationary); + safeToCancelLastTick = isSafeToCancel; + if (pauseRequestedLastTick) { + pauseRequestedLastTick = false; + if (stationaryNow()) { + ticksOfStationary++; + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + ticksOfStationary = 0; + return new PathingCommand(null, PathingCommandType.DEFER); + } + + @Override + public void onLostControl() { + + } + + @Override + public String displayName0() { + return "inventory pauser"; + } + + @Override + public double priority() { + return 5.1; // slightly higher than backfill + } + + @Override + public boolean isTemporary() { + return true; + } +} diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 681beaeac..a185b5f53 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -19,28 +19,25 @@ import baritone.Baritone; import baritone.altoclef.AltoClefSettings; +import baritone.api.BaritoneAPI; import baritone.api.pathing.goals.*; import baritone.api.process.IMineProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.Rotation; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.cache.CachedChunk; -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.Trail; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.AirBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.FallingBlock; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; import java.util.*; @@ -54,6 +51,7 @@ * @author leijurv */ public final class MineProcess extends BaritoneProcessHelper implements IMineProcess { + private static final int ORE_LOCATIONS_COUNT = 64; private BlockOptionalMetaLookup filter; @@ -69,10 +67,15 @@ public MineProcess(Baritone baritone) { super(baritone); } - public static List searchWorld(CalculationContext ctx, BlockOptionalMetaLookup filter, int maxPerType, List alreadyKnown, List blacklist, List dropped) { + @Override + public boolean isActive() { + return filter != null; + } + + public static List searchWorld(CalculationContext ctx, BlockOptionalMetaLookup filter, int max, List alreadyKnown, List blacklist, List dropped) { List locs = new ArrayList<>(); List untracked = new ArrayList<>(); - int maxTotal = maxPerType * filter.blocks().size(); + int maxTotal = max * filter.blocks().size(); for (BlockOptionalMeta bom : filter.blocks()) { Block block = bom.getBlock(); if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { @@ -91,13 +94,13 @@ public static List searchWorld(CalculationContext ctx, BlockOptionalMe } } - locs = prune(ctx, locs, filter, maxPerType, blacklist, dropped); + locs = prune(ctx, locs, filter, max, blacklist, dropped); if (!untracked.isEmpty() || (Baritone.settings().extendCacheOnThreshold.value && locs.size() < maxTotal)) { - locs.addAll(WorldScanner.INSTANCE.scanChunkRadius( + locs.addAll(BaritoneAPI.getProvider().getWorldScanner().scanChunkRadius( ctx.getBaritone().getPlayerContext(), filter, - maxPerType, + max, 10, 32 )); // maxSearchRadius is NOT sq @@ -105,59 +108,7 @@ public static List searchWorld(CalculationContext ctx, BlockOptionalMe locs.addAll(alreadyKnown); - return prune(ctx, locs, filter, maxPerType, blacklist, dropped); - } - - private static List prune(CalculationContext ctx, List locs2, BlockOptionalMetaLookup filter, int maxPer, List blacklist, List dropped) { - dropped.removeIf(drop -> { - for (BlockPos pos : locs2) { - // No longer caring if we CAN break, we're just looking for a global search. - if (pos.distSqr(drop) <= 9 && filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ()))/* && MineProcess.plausibleToBreak(ctx, pos)*/) { - return true; - } - } - return false; - }); - HashMap countsPerBlock = new HashMap<>(); - List locs = locs2 - .stream() - .distinct() - - // remove any that are within loaded chunks that aren't actually what we want - .filter(pos -> !ctx.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ()) || filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos)) - - // remove any that are implausible to mine (encased in bedrock, or touching lava) - //.filter(pos -> MineProcess.plausibleToBreak(ctx, pos)) - - .filter(pos -> { - if (Baritone.settings().allowOnlyExposedOres.value) { - return isNextToAir(ctx, pos); - } else { - return true; - } - }) - - .filter(pos -> pos.getY() >= Baritone.settings().minYLevelWhileMining.value + ctx.world.dimensionType().minY()) - - .filter(pos -> !blacklist.contains(pos)) - - .sorted(Comparator.comparingDouble(ctx.getBaritone().getPlayerContext().player().blockPosition()::distSqr)) - .collect(Collectors.toList()); - - // Prune out a certain number per block type - locs = locs.stream() - .filter(pos -> { - Block b = ctx.getBlock(pos.getX(), pos.getY(), pos.getZ()); - int count = countsPerBlock.getOrDefault(b, 0); - if (count <= maxPer) { - countsPerBlock.put(b, count + 1); - return true; - } else { - return false; - } - }).collect(Collectors.toList()); - - return locs; + return prune(ctx, locs, filter, max, blacklist, dropped); } @@ -189,6 +140,11 @@ public String displayName0() { } private PathingCommand updateGoal() { + BlockOptionalMetaLookup filter = filterFilter(); + if (filter == null) { + return null; + } + boolean legit = Baritone.settings().legitMine.value; List locs = knownOreLocations; if (!locs.isEmpty()) { @@ -223,6 +179,7 @@ private PathingCommand updateGoal() { public boolean isInGoal(int x, int y, int z) { return false; } + @Override public double heuristic() { return Double.NEGATIVE_INFINITY; @@ -233,6 +190,7 @@ public double heuristic() { } private void rescan(List already, CalculationContext context) { + BlockOptionalMetaLookup filter = filterFilter(); if (filter == null) { return; } @@ -307,19 +265,44 @@ private Goal coalesce(BlockPos loc, List locs, CalculationContext cont return new GoalBlock(loc.below()); } - public static boolean isNextToAir(CalculationContext ctx, BlockPos pos) { - int radius = Baritone.settings().allowOnlyExposedOresDistance.value; - for (int dx = -radius; dx <= radius; dx++) { - for (int dy = -radius; dy <= radius; dy++) { - for (int dz = -radius; dz <= radius; dz++) { - if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) <= radius - && MovementHelper.isTransparent(ctx.getBlock(pos.getX() + dx, pos.getY() + dy, pos.getZ() + dz))) { - return true; - } - } - } + private static class GoalThreeBlocks extends GoalTwoBlocks { + + public GoalThreeBlocks(BlockPos pos) { + super(pos); + } + + @Override + public boolean isInGoal(int x, int y, int z) { + return x == this.x && (y == this.y || y == this.y - 1 || y == this.y - 2) && z == this.z; + } + + @Override + public double heuristic(int x, int y, int z) { + int xDiff = x - this.x; + int yDiff = y - this.y; + int zDiff = z - this.z; + return GoalBlock.calculate(xDiff, yDiff < -1 ? yDiff + 2 : yDiff == -1 ? 0 : yDiff, zDiff); + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode() * 393857768; + } + + @Override + public String toString() { + return String.format( + "GoalThreeBlocks{x=%s,y=%s,z=%s}", + SettingsUtil.maybeCensor(x), + SettingsUtil.maybeCensor(y), + SettingsUtil.maybeCensor(z) + ); } - return false; } public List droppedItemsScan() { @@ -340,6 +323,11 @@ public List droppedItemsScan() { } public static boolean plausibleToBreak(CalculationContext ctx, BlockPos pos) { + if (ctx.get(pos).getBlock() instanceof EndPortalFrameBlock || + ctx.get(pos).getBlock() instanceof EndPortalBlock || + ctx.get(pos).getBlock() == Blocks.LAVA) { + return true; + } if (MovementHelper.getMiningDurationTicks(ctx, pos.getX(), pos.getY(), pos.getZ(), ctx.bsi.get0(pos), true) >= COST_INF) { return false; } @@ -348,11 +336,18 @@ public static boolean plausibleToBreak(CalculationContext ctx, BlockPos pos) { return !(ctx.bsi.get0(pos.above()).getBlock() == Blocks.BEDROCK && ctx.bsi.get0(pos.below()).getBlock() == Blocks.BEDROCK); } - private void addNearby() { + private boolean addNearby() { List dropped = droppedItemsScan(); knownOreLocations.addAll(dropped); BlockPos playerFeet = ctx.playerFeet(); BlockStateInterface bsi = new BlockStateInterface(ctx); + + + BlockOptionalMetaLookup filter = filterFilter(); + if (filter == null) { + return false; + } + int searchDist = 10; double fakedBlockReachDistance = 20; // at least 10 * sqrt(3) with some extra space to account for positioning within the block for (int x = playerFeet.getX() - searchDist; x <= playerFeet.getX() + searchDist; x++) { @@ -362,7 +357,7 @@ private void addNearby() { // is an x-ray and it'll get caught if (filter.has(bsi.get0(x, y, z))) { BlockPos pos = new BlockPos(x, y, z); - if ((Baritone.settings().legitMineIncludeDiagonals.value && knownOreLocations.stream().anyMatch(ore -> ore.distSqr(pos) <= 2 /* sq means this is pytha dist <= sqrt(2) */)) || RotationUtils.reachable(ctx.player(), pos, fakedBlockReachDistance).isPresent()) { + if ((Baritone.settings().legitMineIncludeDiagonals.value && knownOreLocations.stream().anyMatch(ore -> ore.distSqr(pos) <= 2 /* sq means this is pytha dist <= sqrt(2) */)) || RotationUtils.reachable(ctx, pos, fakedBlockReachDistance).isPresent()) { knownOreLocations.add(pos); } } @@ -370,19 +365,68 @@ private void addNearby() { } } knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, filter, ORE_LOCATIONS_COUNT, blacklist, dropped); + return true; } - @Override - public boolean isActive() { - return filter != null; + private static List prune(CalculationContext ctx, List locs2, BlockOptionalMetaLookup filter, int max, List blacklist, List dropped) { + dropped.removeIf(drop -> { + for (BlockPos pos : locs2) { + if (pos.distSqr(drop) <= 9 && filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ())) && MineProcess.plausibleToBreak(ctx, pos)) { // TODO maybe drop also has to be supported? no lava below? + return true; + } + } + return false; + }); + List locs = locs2 + .stream() + .distinct() + + // remove any that are within loaded chunks that aren't actually what we want + .filter(pos -> !ctx.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ()) || filter.has(ctx.get(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos)) + + // remove any that are implausible to mine (encased in bedrock, or touching lava) + .filter(pos -> MineProcess.plausibleToBreak(ctx, pos)) + + .filter(pos -> { + if (Baritone.settings().allowOnlyExposedOres.value) { + return isNextToAir(ctx, pos); + } else { + return true; + } + }) + + .filter(pos -> pos.getY() >= Baritone.settings().minYLevelWhileMining.value + ctx.world.dimensionType().minY()) + + .filter(pos -> pos.getY() <= Baritone.settings().maxYLevelWhileMining.value) + + .filter(pos -> !blacklist.contains(pos)) + + .sorted(Comparator.comparingDouble(ctx.getBaritone().getPlayerContext().player().blockPosition()::distSqr)) + .collect(Collectors.toList()); + + if (locs.size() > max) { + return locs.subList(0, max); + } + return locs; } - @Override - public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { - if (Trail.getInstance().updateAndCheck()) { - return Trail.getInstance().getRunAwayCommand(); + public static boolean isNextToAir(CalculationContext ctx, BlockPos pos) { + int radius = Baritone.settings().allowOnlyExposedOresDistance.value; + for (int dx = -radius; dx <= radius; dx++) { + for (int dy = -radius; dy <= radius; dy++) { + for (int dz = -radius; dz <= radius; dz++) { + if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) <= radius + && MovementHelper.isTransparent(ctx.getBlock(pos.getX() + dx, pos.getY() + dy, pos.getZ() + dz))) { + return true; + } + } + } } + return false; + } + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (desiredQuantity > 0) { int curr = ctx.player().getInventory().items.stream() .filter(stack -> filter.has(stack)) @@ -411,11 +455,6 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { return null; } } - if (!Baritone.settings().allowBreak.value) { - logDirect("Unable to mine when allowBreak is false!"); - cancel(); - return null; - } // Wait for pause interactions if (AltoClefSettings.getInstance().isInteractionPaused()) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); @@ -428,7 +467,10 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { Baritone.getExecutor().execute(() -> rescan(curr, context)); } if (Baritone.settings().legitMine.value) { - addNearby(); + if (!addNearby()) { + cancel(); + return null; + } } Optional shaft = curr.stream() .filter(pos -> pos.getX() == ctx.playerFeet().getX() && pos.getZ() == ctx.playerFeet().getZ()) @@ -436,7 +478,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { .filter(pos -> !(BlockStateInterface.get(ctx, pos).getBlock() instanceof AirBlock)) // after breaking a block, it takes mineGoalUpdateInterval ticks for it to actually update this list =( .min(Comparator.comparingDouble(ctx.playerFeet()::distSqr)); baritone.getInputOverrideHandler().clearAllKeys(); - if (shaft.isPresent() && ctx.player().isOnGround()) { + if (shaft.isPresent() && ctx.player().onGround()) { BlockPos pos = shaft.get(); BlockState state = baritone.bsi.get0(pos); if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { @@ -461,26 +503,6 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { return command; } - private static class GoalThreeBlocks extends GoalTwoBlocks { - - public GoalThreeBlocks(BlockPos pos) { - super(pos); - } - - @Override - public boolean isInGoal(int x, int y, int z) { - return x == this.x && (y == this.y || y == this.y - 1 || y == this.y - 2) && z == this.z; - } - - @Override - public double heuristic(int x, int y, int z) { - int xDiff = x - this.x; - int yDiff = y - this.y; - int zDiff = z - this.z; - return GoalBlock.calculate(xDiff, yDiff < -1 ? yDiff + 2 : yDiff == -1 ? 0 : yDiff, zDiff); - } - } - @Override public void mineByName(int quantity, String... blocks) { mine(quantity, new BlockOptionalMetaLookup(blocks)); @@ -489,10 +511,8 @@ public void mineByName(int quantity, String... blocks) { @Override public void mine(int quantity, BlockOptionalMetaLookup filter) { this.filter = filter; - if (filter != null && !Baritone.settings().allowBreak.value) { - logDirect("Unable to mine when allowBreak is false!"); - this.mine(quantity, (BlockOptionalMetaLookup) null); - return; + if (this.filterFilter() == null) { + this.filter = null; } this.desiredQuantity = quantity; this.knownOreLocations = new ArrayList<>(); @@ -504,4 +524,22 @@ public void mine(int quantity, BlockOptionalMetaLookup filter) { rescan(new ArrayList<>(), new CalculationContext(baritone)); } } + + private BlockOptionalMetaLookup filterFilter() { + if (this.filter == null) { + return null; + } + if (!Baritone.settings().allowBreak.value) { + BlockOptionalMetaLookup f = new BlockOptionalMetaLookup(this.filter.blocks() + .stream() + .filter(e -> Baritone.settings().allowBreakAnyway.value.contains(e.getBlock())) + .toArray(BlockOptionalMeta[]::new)); + if (f.blocks().isEmpty()) { + logDirect("Unable to mine when allowBreak is false and target block is not in allowBreakAnyway!"); + return null; + } + return f; + } + return filter; + } } diff --git a/src/main/java/baritone/process/elytra/BlockStateOctreeInterface.java b/src/main/java/baritone/process/elytra/BlockStateOctreeInterface.java new file mode 100644 index 000000000..7db0e2d64 --- /dev/null +++ b/src/main/java/baritone/process/elytra/BlockStateOctreeInterface.java @@ -0,0 +1,54 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process.elytra; + +import dev.babbaj.pathfinder.NetherPathfinder; +import dev.babbaj.pathfinder.Octree; + +/** + * @author Brady + */ +public final class BlockStateOctreeInterface { + + private final NetherPathfinderContext context; + private final long contextPtr; + transient long chunkPtr; + + // Guarantee that the first lookup will fetch the context by setting MAX_VALUE + private int prevChunkX = Integer.MAX_VALUE; + private int prevChunkZ = Integer.MAX_VALUE; + + public BlockStateOctreeInterface(final NetherPathfinderContext context) { + this.context = context; + this.contextPtr = context.context; + } + + public boolean get0(final int x, final int y, final int z) { + if ((y | (127 - y)) < 0) { + return false; + } + final int chunkX = x >> 4; + final int chunkZ = z >> 4; + if (this.chunkPtr == 0 | ((chunkX ^ this.prevChunkX) | (chunkZ ^ this.prevChunkZ)) != 0) { + this.prevChunkX = chunkX; + this.prevChunkZ = chunkZ; + this.chunkPtr = NetherPathfinder.getOrCreateChunk(this.contextPtr, chunkX, chunkZ); + } + return Octree.getBlock(this.chunkPtr, x & 0xF, y & 0x7F, z & 0xF); + } +} diff --git a/src/main/java/baritone/process/elytra/ElytraBehavior.java b/src/main/java/baritone/process/elytra/ElytraBehavior.java new file mode 100644 index 000000000..8cdfd0e39 --- /dev/null +++ b/src/main/java/baritone/process/elytra/ElytraBehavior.java @@ -0,0 +1,1328 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.elytra; + +import baritone.Baritone; +import baritone.api.Settings; +import baritone.api.behavior.look.IAimProcessor; +import baritone.api.behavior.look.ITickableAimProcessor; +import baritone.api.event.events.*; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.*; +import baritone.api.utils.input.Input; +import baritone.pathing.movement.MovementHelper; +import baritone.process.ElytraProcess; +import baritone.utils.BlockStateInterface; +import baritone.utils.IRenderer; +import baritone.utils.PathRenderer; +import baritone.utils.accessor.IFireworkRocketEntity; +import it.unimi.dsi.fastutil.floats.FloatArrayList; +import it.unimi.dsi.fastutil.floats.FloatIterator; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +import java.awt.*; +import java.util.List; +import java.util.Queue; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.UnaryOperator; + +import static baritone.utils.BaritoneMath.fastCeil; +import static baritone.utils.BaritoneMath.fastFloor; + +public final class ElytraBehavior implements Helper { + private final Baritone baritone; + private final IPlayerContext ctx; + + // Render stuff + private final List> clearLines; + private final List> blockedLines; + private List simulationLine; + private BlockPos aimPos; + private List visiblePath; + + // :sunglasses: + public final NetherPathfinderContext context; + public final PathManager pathManager; + private final ElytraProcess process; + + /** + * Remaining cool-down ticks between firework usage + */ + private int remainingFireworkTicks; + + /** + * Remaining cool-down ticks after the player's position and rotation are reset by the server + */ + private int remainingSetBackTicks; + + public boolean landingMode; + + /** + * The most recent minimum number of firework boost ticks, equivalent to {@code 10 * (1 + Flight)} + *

+ * Updated every time a firework is automatically used + */ + private int minimumBoostTicks; + + private boolean deployedFireworkLastTick; + private final int[] nextTickBoostCounter; + + private BlockStateInterface bsi; + private final BlockStateOctreeInterface boi; + public final BetterBlockPos destination; + private final boolean appendDestination; + + private final ExecutorService solverExecutor; + private Future solver; + private Solution pendingSolution; + private boolean solveNextTick; + + private long timeLastCacheCull = 0L; + + // auto swap + private int invTickCountdown = 0; + private final Queue invTransactionQueue = new LinkedList<>(); + + public ElytraBehavior(Baritone baritone, ElytraProcess process, BlockPos destination, boolean appendDestination) { + this.baritone = baritone; + this.ctx = baritone.getPlayerContext(); + this.clearLines = new CopyOnWriteArrayList<>(); + this.blockedLines = new CopyOnWriteArrayList<>(); + this.pathManager = this.new PathManager(); + this.process = process; + this.destination = new BetterBlockPos(destination); + this.appendDestination = appendDestination; + this.solverExecutor = Executors.newSingleThreadExecutor(); + this.nextTickBoostCounter = new int[2]; + + this.context = new NetherPathfinderContext(Baritone.settings().elytraNetherSeed.value); + this.boi = new BlockStateOctreeInterface(context); + } + + public final class PathManager { + + public NetherPath path; + private boolean completePath; + private boolean recalculating; + + private int maxPlayerNear; + private int ticksNearUnchanged; + private int playerNear; + + public PathManager() { + // lol imagine initializing fields normally + this.clear(); + } + + public void tick() { + // Recalculate closest path node + this.updatePlayerNear(); + final int prevMaxNear = this.maxPlayerNear; + this.maxPlayerNear = Math.max(this.maxPlayerNear, this.playerNear); + + if (this.maxPlayerNear == prevMaxNear && ctx.player().isFallFlying()) { + this.ticksNearUnchanged++; + } else { + this.ticksNearUnchanged = 0; + } + + // Obstacles are more important than an incomplete path, handle those first. + this.pathfindAroundObstacles(); + this.attemptNextSegment(); + } + + public CompletableFuture pathToDestination() { + return this.pathToDestination(ctx.playerFeet()); + } + + public CompletableFuture pathToDestination(final BlockPos from) { + final long start = System.nanoTime(); + return this.path0(from, ElytraBehavior.this.destination, UnaryOperator.identity()) + .thenRun(() -> { + final double distance = this.path.get(0).distanceTo(this.path.get(this.path.size() - 1)); + if (this.completePath) { + logVerbose(String.format("Computed path (%.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); + } else { + logVerbose(String.format("Computed segment (Next %.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); + } + }) + .whenComplete((result, ex) -> { + this.recalculating = false; + if (ex != null) { + final Throwable cause = ex.getCause(); + if (cause instanceof PathCalculationException) { + logDirect("Failed to compute path to destination"); + } else { + logUnhandledException(cause); + } + } + }); + } + + public CompletableFuture pathRecalcSegment(final OptionalInt upToIncl) { + if (this.recalculating) { + throw new IllegalStateException("already recalculating"); + } + + this.recalculating = true; + final List after = upToIncl.isPresent() ? this.path.subList(upToIncl.getAsInt() + 1, this.path.size()) : Collections.emptyList(); + final boolean complete = this.completePath; + + return this.path0(ctx.playerFeet(), upToIncl.isPresent() ? this.path.get(upToIncl.getAsInt()) : ElytraBehavior.this.destination, segment -> segment.append(after.stream(), complete || (segment.isFinished() && !upToIncl.isPresent()))) + .whenComplete((result, ex) -> { + this.recalculating = false; + if (ex != null) { + final Throwable cause = ex.getCause(); + if (cause instanceof PathCalculationException) { + logDirect("Failed to recompute segment"); + } else { + logUnhandledException(cause); + } + } + }); + } + + public void pathNextSegment(final int afterIncl) { + if (this.recalculating) { + return; + } + + this.recalculating = true; + final List before = this.path.subList(0, afterIncl + 1); + final long start = System.nanoTime(); + final BetterBlockPos pathStart = this.path.get(afterIncl); + + this.path0(pathStart, ElytraBehavior.this.destination, segment -> segment.prepend(before.stream())) + .thenRun(() -> { + final int recompute = this.path.size() - before.size() - 1; + final double distance = this.path.get(0).distanceTo(this.path.get(recompute)); + + if (this.completePath) { + logVerbose(String.format("Computed path (%.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); + } else { + logVerbose(String.format("Computed segment (Next %.1f blocks in %.4f seconds)", distance, (System.nanoTime() - start) / 1e9d)); + } + }) + .whenComplete((result, ex) -> { + this.recalculating = false; + if (ex != null) { + final Throwable cause = ex.getCause(); + if (cause instanceof PathCalculationException) { + logDirect("Failed to compute next segment"); + if (ctx.player().distanceToSqr(pathStart.getCenter()) < 16 * 16) { + logVerbose("Player is near the segment start, therefore repeating this calculation is pointless. Marking as complete"); + completePath = true; + } + } else { + logUnhandledException(cause); + } + } + }); + } + + public void clear() { + this.path = NetherPath.emptyPath(); + this.completePath = true; + this.recalculating = false; + this.playerNear = 0; + this.ticksNearUnchanged = 0; + this.maxPlayerNear = 0; + } + + private void setPath(final UnpackedSegment segment) { + List path = segment.collect(); + if (ElytraBehavior.this.appendDestination) { + BlockPos dest = ElytraBehavior.this.destination; + BlockPos last = !path.isEmpty() ? path.get(path.size() - 1) : null; + if (last != null && ElytraBehavior.this.clearView(Vec3.atLowerCornerOf(dest), Vec3.atLowerCornerOf(last), false)) { + path.add(new BetterBlockPos(dest)); + } else { + logDirect("unable to land at " + ElytraBehavior.this.destination); + process.landingSpotIsBad(new BetterBlockPos(ElytraBehavior.this.destination)); + } + } + this.path = new NetherPath(path); + this.completePath = segment.isFinished(); + this.playerNear = 0; + this.ticksNearUnchanged = 0; + this.maxPlayerNear = 0; + } + + public NetherPath getPath() { + return this.path; + } + + public int getNear() { + return this.playerNear; + } + + // mickey resigned + private CompletableFuture path0(BlockPos src, BlockPos dst, UnaryOperator operator) { + return ElytraBehavior.this.context.pathFindAsync(src, dst) + .thenApply(UnpackedSegment::from) + .thenApply(operator) + .thenAcceptAsync(this::setPath, ctx.minecraft()::execute); + } + + private void pathfindAroundObstacles() { + if (this.recalculating) { + return; + } + + int rangeStartIncl = playerNear; + int rangeEndExcl = playerNear; + while (rangeEndExcl < path.size() && context.hasChunk(new ChunkPos(path.get(rangeEndExcl)))) { + rangeEndExcl++; + } + // rangeEndExcl now represents an index either not in the path, or just outside render distance + if (rangeStartIncl >= rangeEndExcl) { + // not loaded yet? + return; + } + final BetterBlockPos rangeStart = path.get(rangeStartIncl); + if (!ElytraBehavior.this.passable(rangeStart.x, rangeStart.y, rangeStart.z, false)) { + // we're in a wall + return; // previous iterations of this function SHOULD have fixed this by now :rage_cat: + } + + if (ElytraBehavior.this.process.state != ElytraProcess.State.LANDING && this.ticksNearUnchanged > 100) { + this.pathRecalcSegment(OptionalInt.of(rangeEndExcl - 1)) + .thenRun(() -> { + logVerbose("Recalculating segment, no progress in last 100 ticks"); + }); + this.ticksNearUnchanged = 0; + return; + } + + boolean canSeeAny = false; + for (int i = rangeStartIncl; i < rangeEndExcl - 1; i++) { + if (ElytraBehavior.this.clearView(ctx.playerFeetAsVec(), this.path.getVec(i), false) || ElytraBehavior.this.clearView(ctx.playerHead(), this.path.getVec(i), false)) { + canSeeAny = true; + } + if (!ElytraBehavior.this.clearView(this.path.getVec(i), this.path.getVec(i + 1), false)) { + // obstacle. where do we return to pathing? + // if the end of render distance is closer to goal, then that's fine, otherwise we'd be "digging our hole deeper" and making an already bad backtrack worse + OptionalInt rejoinMainPathAt; + if (this.path.get(rangeEndExcl - 1).distanceSq(ElytraBehavior.this.destination) < ctx.playerFeet().distanceSq(ElytraBehavior.this.destination)) { + rejoinMainPathAt = OptionalInt.of(rangeEndExcl - 1); // rejoin after current render distance + } else { + rejoinMainPathAt = OptionalInt.empty(); // large backtrack detected. ignore render distance, rejoin later on + } + + final BetterBlockPos blockage = this.path.get(i); + final double distance = ctx.playerFeet().distanceTo(this.path.get(rejoinMainPathAt.orElse(path.size() - 1))); + + final long start = System.nanoTime(); + this.pathRecalcSegment(rejoinMainPathAt) + .thenRun(() -> { + logVerbose(String.format("Recalculated segment around path blockage near %s %s %s (next %.1f blocks in %.4f seconds)", + SettingsUtil.maybeCensor(blockage.x), + SettingsUtil.maybeCensor(blockage.y), + SettingsUtil.maybeCensor(blockage.z), + distance, + (System.nanoTime() - start) / 1e9d + )); + }); + return; + } + } + if (!canSeeAny && rangeStartIncl < rangeEndExcl - 2 && process.state != ElytraProcess.State.GET_TO_JUMP) { + this.pathRecalcSegment(OptionalInt.of(rangeEndExcl - 1)).thenRun(() -> logVerbose("Recalculated segment since no path points were visible")); + } + } + + private void attemptNextSegment() { + if (this.recalculating) { + return; + } + + final int last = this.path.size() - 1; + if (!this.completePath && ctx.world().isLoaded(this.path.get(last))) { + this.pathNextSegment(last); + } + } + + public void updatePlayerNear() { + if (this.path.isEmpty()) { + return; + } + + int index = this.playerNear; + final BetterBlockPos pos = ctx.playerFeet(); + for (int i = index; i >= Math.max(index - 1000, 0); i -= 10) { + if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { + index = i; // intentional: this changes the bound of the loop + } + } + for (int i = index; i < Math.min(index + 1000, path.size()); i += 10) { + if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { + index = i; // intentional: this changes the bound of the loop + } + } + for (int i = index; i >= Math.max(index - 50, 0); i--) { + if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { + index = i; // intentional: this changes the bound of the loop + } + } + for (int i = index; i < Math.min(index + 50, path.size()); i++) { + if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { + index = i; // intentional: this changes the bound of the loop + } + } + this.playerNear = index; + } + + public boolean isComplete() { + return this.completePath; + } + } + + public void onRenderPass(RenderEvent event) { + + final Settings settings = Baritone.settings(); + if (this.visiblePath != null) { + PathRenderer.drawPath(event.getModelViewStack(), this.visiblePath, 0, Color.RED, false, 0, 0, 0.0D); + } + if (this.aimPos != null) { + PathRenderer.drawGoal(event.getModelViewStack(), ctx, new GoalBlock(this.aimPos), event.getPartialTicks(), Color.GREEN); + } + if (!this.clearLines.isEmpty() && settings.elytraRenderRaytraces.value) { + IRenderer.startLines(Color.GREEN, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); + for (Pair line : this.clearLines) { + IRenderer.emitLine(event.getModelViewStack(), line.first(), line.second()); + } + IRenderer.endLines(settings.renderPathIgnoreDepth.value); + } + if (!this.blockedLines.isEmpty() && Baritone.settings().elytraRenderRaytraces.value) { + IRenderer.startLines(Color.BLUE, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); + for (Pair line : this.blockedLines) { + IRenderer.emitLine(event.getModelViewStack(), line.first(), line.second()); + } + IRenderer.endLines(settings.renderPathIgnoreDepth.value); + } + if (this.simulationLine != null && Baritone.settings().elytraRenderSimulation.value) { + IRenderer.startLines(new Color(0x36CCDC), settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); + final Vec3 offset = ctx.player().getPosition(event.getPartialTicks()); + for (int i = 0; i < this.simulationLine.size() - 1; i++) { + final Vec3 src = this.simulationLine.get(i).add(offset); + final Vec3 dst = this.simulationLine.get(i + 1).add(offset); + IRenderer.emitLine(event.getModelViewStack(), src, dst); + } + IRenderer.endLines(settings.renderPathIgnoreDepth.value); + } + } + + public void onChunkEvent(ChunkEvent event) { + if (event.isPostPopulate() && this.context != null) { + final LevelChunk chunk = ctx.world().getChunk(event.getX(), event.getZ()); + this.context.queueForPacking(chunk); + } + } + + public void onBlockChange(BlockChangeEvent event) { + this.context.queueBlockUpdate(event); + } + + public void onReceivePacket(PacketEvent event) { + if (event.getPacket() instanceof ClientboundPlayerPositionPacket) { + ctx.minecraft().execute(() -> { + this.remainingSetBackTicks = Baritone.settings().elytraFireworkSetbackUseDelay.value; + }); + } + } + + public void pathTo() { + if (!Baritone.settings().elytraAutoJump.value || ctx.player().isFallFlying()) { + this.pathManager.pathToDestination(); + } + } + + public void destroy() { + if (this.solver != null) { + this.solver.cancel(true); + } + this.solverExecutor.shutdown(); + try { + while (!this.solverExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {} + } catch (InterruptedException e) { + e.printStackTrace(); + } + this.context.destroy(); + } + + public void repackChunks() { + ChunkSource chunkProvider = ctx.world().getChunkSource(); + + BetterBlockPos playerPos = ctx.playerFeet(); + + int playerChunkX = playerPos.getX() >> 4; + int playerChunkZ = playerPos.getZ() >> 4; + + int minX = playerChunkX - 40; + int minZ = playerChunkZ - 40; + int maxX = playerChunkX + 40; + int maxZ = playerChunkZ + 40; + + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + LevelChunk chunk = chunkProvider.getChunk(x, z, false); + + if (chunk != null && !chunk.isEmpty()) { + this.context.queueForPacking(chunk); + } + } + } + } + + public void onTick() { + synchronized (this.context.cullingLock) { + this.onTick0(); + } + final long now = System.currentTimeMillis(); + if ((now - this.timeLastCacheCull) / 1000 > Baritone.settings().elytraTimeBetweenCacheCullSecs.value) { + this.context.queueCacheCulling(ctx.player().chunkPosition().x, ctx.player().chunkPosition().z, Baritone.settings().elytraCacheCullDistance.value, this.boi); + this.timeLastCacheCull = now; + } + } + + private void onTick0() { + // Fetch the previous solution, regardless of if it's going to be used + this.pendingSolution = null; + if (this.solver != null) { + try { + this.pendingSolution = this.solver.get(); + } catch (Exception ignored) { + // it doesn't matter if get() fails since the solution can just be recalculated synchronously + } finally { + this.solver = null; + } + } + + tickInventoryTransactions(); + + // Certified mojang employee incident + if (this.remainingFireworkTicks > 0) { + this.remainingFireworkTicks--; + } + if (this.remainingSetBackTicks > 0) { + this.remainingSetBackTicks--; + } + if (!this.getAttachedFirework().isPresent()) { + this.minimumBoostTicks = 0; + } + + // Reset rendered elements + this.clearLines.clear(); + this.blockedLines.clear(); + this.visiblePath = null; + this.simulationLine = null; + this.aimPos = null; + + final List path = this.pathManager.getPath(); + if (path.isEmpty()) { + return; + } else if (this.destination == null) { + this.pathManager.clear(); + return; + } + + // ctx AND context???? :DDD + this.bsi = new BlockStateInterface(ctx); + this.pathManager.tick(); + + final int playerNear = this.pathManager.getNear(); + this.visiblePath = path.subList( + Math.max(playerNear - 30, 0), + Math.min(playerNear + 100, path.size()) + ); + } + + /** + * Called by {@link baritone.process.ElytraProcess#onTick(boolean, boolean)} when the process is in control and the player is flying + */ + public void tick() { + if (this.pathManager.getPath().isEmpty()) { + return; + } + + trySwapElytra(); + + if (ctx.player().horizontalCollision) { + logVerbose("hbonk"); + } + if (ctx.player().verticalCollision) { + logVerbose("vbonk"); + } + + final SolverContext solverContext = this.new SolverContext(false); + this.solveNextTick = true; + + // If there's no previously calculated solution to use, or the context used at the end of last tick doesn't match this tick + final Solution solution; + if (this.pendingSolution == null || !this.pendingSolution.context.equals(solverContext)) { + solution = this.solveAngles(solverContext); + } else { + solution = this.pendingSolution; + } + + if (this.deployedFireworkLastTick) { + this.nextTickBoostCounter[solverContext.boost.isBoosted() ? 1 : 0]++; + this.deployedFireworkLastTick = false; + } + + final boolean inLava = ctx.player().isInLava(); + if (inLava) { + baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + } + + if (solution == null) { + logVerbose("no solution"); + return; + } + + baritone.getLookBehavior().updateTarget(solution.rotation, false); + + if (!solution.solvedPitch) { + logVerbose("no pitch solution, probably gonna crash in a few ticks LOL!!!"); + return; + } else { + this.aimPos = new BetterBlockPos(solution.goingTo.x, solution.goingTo.y, solution.goingTo.z); + } + + this.tickUseFireworks( + solution.context.start, + solution.goingTo, + solution.context.boost.isBoosted(), + solution.forceUseFirework || inLava + ); + } + + public void onPostTick(TickEvent event) { + if (event.getType() == TickEvent.Type.IN && this.solveNextTick) { + // We're at the end of the tick, the player's position likely updated and the closest path node could've + // changed. Updating it now will avoid unnecessary recalculation on the main thread. + this.pathManager.updatePlayerNear(); + + final SolverContext context = this.new SolverContext(true); + this.solver = this.solverExecutor.submit(() -> this.solveAngles(context)); + this.solveNextTick = false; + } + } + + private Solution solveAngles(final SolverContext context) { + final NetherPath path = context.path; + final int playerNear = landingMode ? path.size() - 1 : context.playerNear; + final Vec3 start = context.start; + Solution solution = null; + + for (int relaxation = 0; relaxation < 3; relaxation++) { // try for a strict solution first, then relax more and more (if we're in a corner or near some blocks, it will have to relax its constraints a bit) + int[] heights = context.boost.isBoosted() ? new int[]{20, 10, 5, 0} : new int[]{0}; // attempt to gain height, if we can, so as not to waste the boost + int lookahead = relaxation == 0 ? 2 : 3; // ideally this would be expressed as a distance in blocks, rather than a number of voxel steps + //int minStep = Math.max(0, playerNear - relaxation); + int minStep = playerNear; + + for (int i = Math.min(playerNear + 20, path.size() - 1); i >= minStep; i--) { + final List> candidates = new ArrayList<>(); + for (int dy : heights) { + if (relaxation == 0 || i == minStep) { + // no interp + candidates.add(new Pair<>(path.getVec(i), dy)); + } else if (relaxation == 1) { + final double[] interps = new double[]{1.0, 0.75, 0.5, 0.25}; + for (double interp : interps) { + final Vec3 dest = interp == 1.0 + ? path.getVec(i) + : path.getVec(i).scale(interp).add(path.getVec(i - 1).scale(1.0 - interp)); + candidates.add(new Pair<>(dest, dy)); + } + } else { + // Create a point along the segment every block + final Vec3 delta = path.getVec(i).subtract(path.getVec(i - 1)); + final int steps = fastFloor(delta.length()); + final Vec3 step = delta.normalize(); + Vec3 stepped = path.getVec(i); + for (int interp = 0; interp < steps; interp++) { + candidates.add(new Pair<>(stepped, dy)); + stepped = stepped.subtract(step); + } + } + } + + for (final Pair candidate : candidates) { + final Integer augment = candidate.second(); + Vec3 dest = candidate.first().add(0, augment, 0); + if (landingMode) { + dest = dest.add(0.5, 0.5, 0.5); + } + + if (augment != 0) { + if (i + lookahead >= path.size()) { + continue; + } + if (start.distanceTo(dest) < 40) { + if (!this.clearView(dest, path.getVec(i + lookahead).add(0, augment, 0), false) + || !this.clearView(dest, path.getVec(i + lookahead), false)) { + // aka: don't go upwards if doing so would prevent us from being able to see the next position **OR** the modified next position + continue; + } + } else { + // but if it's far away, allow gaining altitude if we could lose it again by the time we get there + if (!this.clearView(dest, path.getVec(i), false)) { + continue; + } + } + } + + final double minAvoidance = Baritone.settings().elytraMinimumAvoidance.value; + final Double growth = relaxation == 2 ? null + : relaxation == 0 ? 2 * minAvoidance : minAvoidance; + + if (this.isHitboxClear(context, dest, growth)) { + // Yaw is trivial, just calculate the rotation required to face the destination + final float yaw = RotationUtils.calcRotationFromVec3d(start, dest, ctx.playerRotations()).getYaw(); + + final Pair pitch = this.solvePitch(context, dest, relaxation); + if (pitch == null) { + solution = new Solution(context, new Rotation(yaw, ctx.playerRotations().getPitch()), null, false, false); + continue; + } + + // A solution was found with yaw AND pitch, so just immediately return it. + return new Solution(context, new Rotation(yaw, pitch.first()), dest, true, pitch.second()); + } + } + } + } + return solution; + } + + private void tickUseFireworks(final Vec3 start, final Vec3 goingTo, final boolean isBoosted, final boolean forceUseFirework) { + if (this.remainingSetBackTicks > 0) { + logDebug("waiting for elytraFireworkSetbackUseDelay: " + this.remainingSetBackTicks); + return; + } + if (this.landingMode) { + return; + } + final boolean useOnDescend = !Baritone.settings().elytraConserveFireworks.value || ctx.player().position().y < goingTo.y + 5; + final double currentSpeed = new Vec3( + ctx.player().getDeltaMovement().x, + // ignore y component if we are BOTH below where we want to be AND descending + ctx.player().position().y < goingTo.y ? Math.max(0, ctx.player().getDeltaMovement().y) : ctx.player().getDeltaMovement().y, + ctx.player().getDeltaMovement().z + ).lengthSqr(); + + final double elytraFireworkSpeed = Baritone.settings().elytraFireworkSpeed.value; + if (this.remainingFireworkTicks <= 0 && (forceUseFirework || (!isBoosted + && useOnDescend + && (ctx.player().position().y < goingTo.y - 5 || start.distanceTo(new Vec3(goingTo.x + 0.5, ctx.player().position().y, goingTo.z + 0.5)) > 5) // UGH!!!!!!! + && currentSpeed < elytraFireworkSpeed * elytraFireworkSpeed)) + ) { + // Prioritize boosting fireworks over regular ones + // TODO: Take the minimum boost time into account? + if (!baritone.getInventoryBehavior().throwaway(true, ElytraBehavior::isBoostingFireworks) && + !baritone.getInventoryBehavior().throwaway(true, ElytraBehavior::isFireworks)) { + logDirect("no fireworks"); + return; + } + logVerbose("attempting to use firework" + (forceUseFirework ? " (forced)" : "")); + ctx.playerController().processRightClick(ctx.player(), ctx.world(), InteractionHand.MAIN_HAND); + this.minimumBoostTicks = 10 * (1 + getFireworkBoost(ctx.player().getItemInHand(InteractionHand.MAIN_HAND)).orElse(0)); + this.remainingFireworkTicks = 10; + this.deployedFireworkLastTick = true; + } + } + + private final class SolverContext { + + public final NetherPath path; + public final int playerNear; + public final Vec3 start; + public final Vec3 motion; + public final AABB boundingBox; + public final boolean ignoreLava; + public final FireworkBoost boost; + public final IAimProcessor aimProcessor; + + /** + * Creates a new SolverContext using the current state of the path, player, and firework boost at the time of + * construction. + * + * @param async Whether the computation is being done asynchronously at the end of a game tick. + */ + public SolverContext(boolean async) { + this.path = ElytraBehavior.this.pathManager.getPath(); + this.playerNear = ElytraBehavior.this.pathManager.getNear(); + + this.start = ctx.playerFeetAsVec(); + this.motion = ctx.playerMotion(); + this.boundingBox = ctx.player().getBoundingBox(); + this.ignoreLava = ctx.player().isInLava(); + + final Integer fireworkTicksExisted; + if (async && ElytraBehavior.this.deployedFireworkLastTick) { + final int[] counter = ElytraBehavior.this.nextTickBoostCounter; + fireworkTicksExisted = counter[1] > counter[0] ? 0 : null; + } else { + fireworkTicksExisted = ElytraBehavior.this.getAttachedFirework().map(e -> e.tickCount).orElse(null); + } + this.boost = new FireworkBoost(fireworkTicksExisted, ElytraBehavior.this.minimumBoostTicks); + + ITickableAimProcessor aim = ElytraBehavior.this.baritone.getLookBehavior().getAimProcessor().fork(); + if (async) { + // async computation is done at the end of a tick, advance by 1 to prepare for the next tick + aim.advance(1); + } + this.aimProcessor = aim; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || o.getClass() != SolverContext.class) { + return false; + } + + SolverContext other = (SolverContext) o; + return this.path == other.path // Contents aren't modified, just compare by reference + && this.playerNear == other.playerNear + && Objects.equals(this.start, other.start) + && Objects.equals(this.motion, other.motion) + && Objects.equals(this.boundingBox, other.boundingBox) + && this.ignoreLava == other.ignoreLava + && Objects.equals(this.boost, other.boost); + } + } + + private static final class FireworkBoost { + + private final Integer fireworkTicksExisted; + private final int minimumBoostTicks; + private final int maximumBoostTicks; + + /** + * @param fireworkTicksExisted The ticksExisted of the attached firework entity, or {@code null} if no entity. + * @param minimumBoostTicks The minimum number of boost ticks that the attached firework entity, if any, will + * provide. + */ + public FireworkBoost(final Integer fireworkTicksExisted, final int minimumBoostTicks) { + this.fireworkTicksExisted = fireworkTicksExisted; + + // this.lifetime = 10 * i + this.rand.nextInt(6) + this.rand.nextInt(7); + this.minimumBoostTicks = minimumBoostTicks; + this.maximumBoostTicks = minimumBoostTicks + 11; + } + + public boolean isBoosted() { + return this.fireworkTicksExisted != null; + } + + /** + * @return The guaranteed number of remaining ticks with boost + */ + public int getGuaranteedBoostTicks() { + return this.isBoosted() ? Math.max(0, this.minimumBoostTicks - this.fireworkTicksExisted) : 0; + } + + /** + * @return The maximum number of remaining ticks with boost + */ + public int getMaximumBoostTicks() { + return this.isBoosted() ? Math.max(0, this.maximumBoostTicks - this.fireworkTicksExisted) : 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || o.getClass() != FireworkBoost.class) { + return false; + } + + FireworkBoost other = (FireworkBoost) o; + if (!this.isBoosted() && !other.isBoosted()) { + return true; + } + + return Objects.equals(this.fireworkTicksExisted, other.fireworkTicksExisted) + && this.minimumBoostTicks == other.minimumBoostTicks + && this.maximumBoostTicks == other.maximumBoostTicks; + } + } + + private static final class PitchResult { + + public final float pitch; + public final double dot; + public final List steps; + + public PitchResult(float pitch, double dot, List steps) { + this.pitch = pitch; + this.dot = dot; + this.steps = steps; + } + } + + private static final class Solution { + + public final SolverContext context; + public final Rotation rotation; + public final Vec3 goingTo; + public final boolean solvedPitch; + public final boolean forceUseFirework; + + public Solution(SolverContext context, Rotation rotation, Vec3 goingTo, boolean solvedPitch, boolean forceUseFirework) { + this.context = context; + this.rotation = rotation; + this.goingTo = goingTo; + this.solvedPitch = solvedPitch; + this.forceUseFirework = forceUseFirework; + } + } + + public static boolean isFireworks(final ItemStack itemStack) { + if (itemStack.getItem() != Items.FIREWORK_ROCKET) { + return false; + } + // If it has NBT data, make sure it won't cause us to explode. + final CompoundTag compound = itemStack.getTagElement("Fireworks"); + return compound == null || !compound.getAllKeys().contains("Explosions"); + } + + private static boolean isBoostingFireworks(final ItemStack itemStack) { + return getFireworkBoost(itemStack).isPresent(); + } + + private static OptionalInt getFireworkBoost(final ItemStack itemStack) { + if (isFireworks(itemStack)) { + final CompoundTag compound = itemStack.getTagElement("Fireworks"); + if (compound != null && compound.getAllKeys().contains("Flight")) { + return OptionalInt.of(compound.getByte("Flight")); + } + } + return OptionalInt.empty(); + } + + private Optional getAttachedFirework() { + return ctx.entitiesStream() + .filter(x -> x instanceof FireworkRocketEntity) + .filter(x -> Objects.equals(((IFireworkRocketEntity) x).getBoostedEntity(), ctx.player())) + .map(x -> (FireworkRocketEntity) x) + .findFirst(); + } + + private boolean isHitboxClear(final SolverContext context, final Vec3 dest, final Double growAmount) { + final Vec3 start = context.start; + final boolean ignoreLava = context.ignoreLava; + + if (!this.clearView(start, dest, ignoreLava)) { + return false; + } + if (growAmount == null) { + return true; + } + + final AABB bb = context.boundingBox.inflate(growAmount); + + final double ox = dest.x - start.x; + final double oy = dest.y - start.y; + final double oz = dest.z - start.z; + + final double[] src = new double[]{ + bb.minX, bb.minY, bb.minZ, + bb.minX, bb.minY, bb.maxZ, + bb.minX, bb.maxY, bb.minZ, + bb.minX, bb.maxY, bb.maxZ, + bb.maxX, bb.minY, bb.minZ, + bb.maxX, bb.minY, bb.maxZ, + bb.maxX, bb.maxY, bb.minZ, + bb.maxX, bb.maxY, bb.maxZ, + }; + final double[] dst = new double[]{ + bb.minX + ox, bb.minY + oy, bb.minZ + oz, + bb.minX + ox, bb.minY + oy, bb.maxZ + oz, + bb.minX + ox, bb.maxY + oy, bb.minZ + oz, + bb.minX + ox, bb.maxY + oy, bb.maxZ + oz, + bb.maxX + ox, bb.minY + oy, bb.minZ + oz, + bb.maxX + ox, bb.minY + oy, bb.maxZ + oz, + bb.maxX + ox, bb.maxY + oy, bb.minZ + oz, + bb.maxX + ox, bb.maxY + oy, bb.maxZ + oz, + }; + + // Use non-batching method without early failure + if (Baritone.settings().elytraRenderHitboxRaytraces.value) { + boolean clear = true; + for (int i = 0; i < 8; i++) { + final Vec3 s = new Vec3(src[i * 3], src[i * 3 + 1], src[i * 3 + 2]); + final Vec3 d = new Vec3(dst[i * 3], dst[i * 3 + 1], dst[i * 3 + 2]); + // Don't forward ignoreLava since the batch call doesn't care about it + if (!this.clearView(s, d, false)) { + clear = false; + } + } + return clear; + } + + return this.context.raytrace(8, src, dst, NetherPathfinderContext.Visibility.ALL); + } + + public boolean clearView(Vec3 start, Vec3 dest, boolean ignoreLava) { + final boolean clear; + if (!ignoreLava) { + // if start == dest then the cpp raytracer dies + clear = start.equals(dest) || this.context.raytrace(start, dest); + } else { + clear = ctx.world().clip(new ClipContext(start, dest, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, ctx.player())).getType() == HitResult.Type.MISS; + } + + if (Baritone.settings().elytraRenderRaytraces.value) { + (clear ? this.clearLines : this.blockedLines).add(new Pair<>(start, dest)); + } + return clear; + } + + private static FloatArrayList pitchesToSolveFor(final float goodPitch, final boolean desperate) { + final float minPitch = desperate ? -90 : Math.max(goodPitch - Baritone.settings().elytraPitchRange.value, -89); + final float maxPitch = desperate ? 90 : Math.min(goodPitch + Baritone.settings().elytraPitchRange.value, 89); + + final FloatArrayList pitchValues = new FloatArrayList(fastCeil(maxPitch - minPitch) + 1); + for (float pitch = goodPitch; pitch <= maxPitch; pitch++) { + pitchValues.add(pitch); + } + for (float pitch = goodPitch - 1; pitch >= minPitch; pitch--) { + pitchValues.add(pitch); + } + + return pitchValues; + } + + @FunctionalInterface + private interface IntTriFunction { + T apply(int first, int second, int third); + } + + private static final class IntTriple { + public final int first; + public final int second; + public final int third; + + public IntTriple(int first, int second, int third) { + this.first = first; + this.second = second; + this.third = third; + } + } + + private Pair solvePitch(final SolverContext context, final Vec3 goal, final int relaxation) { + final boolean desperate = relaxation == 2; + final float goodPitch = RotationUtils.calcRotationFromVec3d(context.start, goal, ctx.playerRotations()).getPitch(); + final FloatArrayList pitches = pitchesToSolveFor(goodPitch, desperate); + + final IntTriFunction solve = (ticks, ticksBoosted, ticksBoostDelay) -> + this.solvePitch(context, goal, relaxation, pitches.iterator(), ticks, ticksBoosted, ticksBoostDelay); + + final List tests = new ArrayList<>(); + + if (context.boost.isBoosted()) { + final int guaranteed = context.boost.getGuaranteedBoostTicks(); + if (guaranteed == 0) { + // uncertain when boost will run out + final int lookahead = Math.max(4, 10 - context.boost.getMaximumBoostTicks()); + tests.add(new IntTriple(lookahead, 1, 0)); + } else if (guaranteed <= 5) { + // boost will run out within 5 ticks + tests.add(new IntTriple(guaranteed + 5, guaranteed, 0)); + } else { + // there's plenty of guaranteed boost + tests.add(new IntTriple(guaranteed + 1, guaranteed, 0)); + } + } + + // Standard test, assume (not) boosted for entire duration + final int ticks = desperate ? 3 : context.boost.isBoosted() ? Math.max(5, context.boost.getGuaranteedBoostTicks()) : Baritone.settings().elytraSimulationTicks.value; + tests.add(new IntTriple(ticks, context.boost.isBoosted() ? ticks : 0, 0)); + + final Optional result = tests.stream() + .map(i -> solve.apply(i.first, i.second, i.third)) + .filter(Objects::nonNull) + .findFirst(); + if (result.isPresent()) { + return new Pair<>(result.get().pitch, false); + } + + // If we used a firework would we be able to get out of the current situation??? perhaps + if (desperate) { + final List testsBoost = new ArrayList<>(); + testsBoost.add(new IntTriple(ticks, 10, 3)); + testsBoost.add(new IntTriple(ticks, 10, 2)); + testsBoost.add(new IntTriple(ticks, 10, 1)); + + final Optional resultBoost = testsBoost.stream() + .map(i -> solve.apply(i.first, i.second, i.third)) + .filter(Objects::nonNull) + .findFirst(); + if (resultBoost.isPresent()) { + return new Pair<>(resultBoost.get().pitch, true); + } + } + + return null; + } + + private PitchResult solvePitch(final SolverContext context, final Vec3 goal, final int relaxation, + final FloatIterator pitches, final int ticks, final int ticksBoosted, + final int ticksBoostDelay) { + // we are at a certain velocity, but we have a target velocity + // what pitch would get us closest to our target velocity? + // yaw is easy so we only care about pitch + + final Vec3 goalDelta = goal.subtract(context.start); + final Vec3 goalDirection = goalDelta.normalize(); + + final Deque bestResults = new ArrayDeque<>(); + + while (pitches.hasNext()) { + final float pitch = pitches.nextFloat(); + final List displacement = this.simulate( + context, + goalDelta, + pitch, + ticks, + ticksBoosted, + ticksBoostDelay + ); + if (displacement == null) { + continue; + } + final Vec3 last = displacement.get(displacement.size() - 1); + double goodness = goalDirection.dot(last.normalize()); + if (landingMode) { + goodness = -goalDelta.subtract(last).length(); + } + final PitchResult bestSoFar = bestResults.peek(); + if (bestSoFar == null || goodness > bestSoFar.dot) { + bestResults.push(new PitchResult(pitch, goodness, displacement)); + } + } + + outer: + for (final PitchResult result : bestResults) { + if (relaxation < 2) { + // Ensure that the goal is visible along the entire simulated path + // Reverse order iteration since the last position is most likely to fail + for (int i = result.steps.size() - 1; i >= 1; i--) { + if (!clearView(context.start.add(result.steps.get(i)), goal, context.ignoreLava)) { + continue outer; + } + } + } else { + // Ensure that the goal is visible from the final position + if (!clearView(context.start.add(result.steps.get(result.steps.size() - 1)), goal, context.ignoreLava)) { + continue; + } + } + + this.simulationLine = result.steps; + return result; + } + return null; + } + + private List simulate(final SolverContext context, final Vec3 goalDelta, final float pitch, final int ticks, + final int ticksBoosted, final int ticksBoostDelay) { + final ITickableAimProcessor aimProcessor = context.aimProcessor.fork(); + Vec3 delta = goalDelta; + Vec3 motion = context.motion; + AABB hitbox = context.boundingBox; + List displacement = new ArrayList<>(ticks + 1); + displacement.add(Vec3.ZERO); + int remainingTicksBoosted = ticksBoosted; + + for (int i = 0; i < ticks; i++) { + final double cx = hitbox.minX + (hitbox.maxX - hitbox.minX) * 0.5D; + final double cz = hitbox.minZ + (hitbox.maxZ - hitbox.minZ) * 0.5D; + if (delta.lengthSqr() < 1) { + break; + } + final Rotation rotation = aimProcessor.nextRotation( + RotationUtils.calcRotationFromVec3d(Vec3.ZERO, delta, ctx.playerRotations()).withPitch(pitch) + ); + final Vec3 lookDirection = RotationUtils.calcLookDirectionFromRotation(rotation); + + motion = step(motion, lookDirection, rotation.getPitch()); + delta = delta.subtract(motion); + + // Collision box while the player is in motion, with additional padding for safety + final AABB inMotion = hitbox.inflate(motion.x, motion.y, motion.z).inflate(0.01); + + int xmin = fastFloor(inMotion.minX); + int xmax = fastCeil(inMotion.maxX); + int ymin = fastFloor(inMotion.minY); + int ymax = fastCeil(inMotion.maxY); + int zmin = fastFloor(inMotion.minZ); + int zmax = fastCeil(inMotion.maxZ); + for (int x = xmin; x < xmax; x++) { + for (int y = ymin; y < ymax; y++) { + for (int z = zmin; z < zmax; z++) { + if (!this.passable(x, y, z, context.ignoreLava)) { + return null; + } + } + } + } + + hitbox = hitbox.move(motion); + displacement.add(displacement.get(displacement.size() - 1).add(motion)); + + if (i >= ticksBoostDelay && remainingTicksBoosted-- > 0) { + // See EntityFireworkRocket + motion = motion.add( + lookDirection.x * 0.1 + (lookDirection.x * 1.5 - motion.x) * 0.5, + lookDirection.y * 0.1 + (lookDirection.y * 1.5 - motion.y) * 0.5, + lookDirection.z * 0.1 + (lookDirection.z * 1.5 - motion.z) * 0.5 + ); + } + } + + return displacement; + } + + private static Vec3 step(final Vec3 motion, final Vec3 lookDirection, final float pitch) { + double motionX = motion.x; + double motionY = motion.y; + double motionZ = motion.z; + + float pitchRadians = pitch * RotationUtils.DEG_TO_RAD_F; + double pitchBase2 = Math.sqrt(lookDirection.x * lookDirection.x + lookDirection.z * lookDirection.z); + double flatMotion = Math.sqrt(motionX * motionX + motionZ * motionZ); + double thisIsAlwaysOne = lookDirection.length(); + float pitchBase3 = Mth.cos(pitchRadians); + //System.out.println("always the same lol " + -pitchBase + " " + pitchBase3); + //System.out.println("always the same lol " + Math.abs(pitchBase3) + " " + pitchBase2); + //System.out.println("always 1 lol " + thisIsAlwaysOne); + pitchBase3 = (float) ((double) pitchBase3 * (double) pitchBase3 * Math.min(1, thisIsAlwaysOne / 0.4)); + motionY += -0.08 + (double) pitchBase3 * 0.06; + if (motionY < 0 && pitchBase2 > 0) { + double speedModifier = motionY * -0.1 * (double) pitchBase3; + motionY += speedModifier; + motionX += lookDirection.x * speedModifier / pitchBase2; + motionZ += lookDirection.z * speedModifier / pitchBase2; + } + if (pitchRadians < 0) { // if you are looking down (below level) + double anotherSpeedModifier = flatMotion * (double) (-Mth.sin(pitchRadians)) * 0.04; + motionY += anotherSpeedModifier * 3.2; + motionX -= lookDirection.x * anotherSpeedModifier / pitchBase2; + motionZ -= lookDirection.z * anotherSpeedModifier / pitchBase2; + } + if (pitchBase2 > 0) { // this is always true unless you are looking literally straight up (let's just say the bot will never do that) + motionX += (lookDirection.x / pitchBase2 * flatMotion - motionX) * 0.1; + motionZ += (lookDirection.z / pitchBase2 * flatMotion - motionZ) * 0.1; + } + motionX *= 0.99f; + motionY *= 0.98f; + motionZ *= 0.99f; + //System.out.println(motionX + " " + motionY + " " + motionZ); + + return new Vec3(motionX, motionY, motionZ); + } + + private boolean passable(int x, int y, int z, boolean ignoreLava) { + if (ignoreLava) { + final BlockState state = this.bsi.get0(x, y, z); + return state.getBlock() instanceof AirBlock || MovementHelper.isLava(state); + } else { + return !this.boi.get0(x, y, z); + } + } + + private void tickInventoryTransactions() { + if (invTickCountdown <= 0) { + Runnable r = invTransactionQueue.poll(); + if (r != null) { + r.run(); + invTickCountdown = Baritone.settings().ticksBetweenInventoryMoves.value; + } + } + if (invTickCountdown > 0) invTickCountdown--; + } + + private void queueWindowClick(int windowId, int slotId, int button, ClickType type) { + invTransactionQueue.add(() -> ctx.playerController().windowClick(windowId, slotId, button, type, ctx.player())); + } + + private int findGoodElytra() { + NonNullList invy = ctx.player().getInventory().items; + for (int i = 0; i < invy.size(); i++) { + ItemStack slot = invy.get(i); + if (slot.getItem() == Items.ELYTRA && (slot.getItem().getMaxDamage() - slot.getDamageValue()) > Baritone.settings().elytraMinimumDurability.value) { + return i; + } + } + return -1; + } + + private void trySwapElytra() { + if (!Baritone.settings().elytraAutoSwap.value || !invTransactionQueue.isEmpty()) { + return; + } + + ItemStack chest = ctx.player().getItemBySlot(EquipmentSlot.CHEST); + if (chest.getItem() != Items.ELYTRA + || chest.getItem().getMaxDamage() - chest.getDamageValue() > Baritone.settings().elytraMinimumDurability.value) { + return; + } + + int goodElytraSlot = findGoodElytra(); + if (goodElytraSlot != -1) { + final int CHEST_SLOT = 6; + final int slotId = goodElytraSlot < 9 ? goodElytraSlot + 36 : goodElytraSlot; + queueWindowClick(ctx.player().inventoryMenu.containerId, slotId, 0, ClickType.PICKUP); + queueWindowClick(ctx.player().inventoryMenu.containerId, CHEST_SLOT, 0, ClickType.PICKUP); + queueWindowClick(ctx.player().inventoryMenu.containerId, slotId, 0, ClickType.PICKUP); + } + } + + void logVerbose(String message) { + if (Baritone.settings().elytraChatSpam.value) { + logDebug(message); + } + } +} diff --git a/src/main/java/baritone/process/elytra/NetherPath.java b/src/main/java/baritone/process/elytra/NetherPath.java new file mode 100644 index 000000000..73531de4d --- /dev/null +++ b/src/main/java/baritone/process/elytra/NetherPath.java @@ -0,0 +1,65 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.elytra; + +import baritone.api.utils.BetterBlockPos; +import net.minecraft.world.phys.Vec3; + +import java.util.AbstractList; +import java.util.Collections; +import java.util.List; + +/** + * @author Brady + */ +public final class NetherPath extends AbstractList { + + private static final NetherPath EMPTY_PATH = new NetherPath(Collections.emptyList()); + + private final List backing; + + NetherPath(List backing) { + this.backing = backing; + } + + @Override + public BetterBlockPos get(int index) { + return this.backing.get(index); + } + + @Override + public int size() { + return this.backing.size(); + } + + /** + * @return The last position in the path, or {@code null} if empty + */ + public BetterBlockPos getLast() { + return this.isEmpty() ? null : this.backing.get(this.backing.size() - 1); + } + + public Vec3 getVec(int index) { + final BetterBlockPos pos = this.get(index); + return new Vec3(pos.x, pos.y, pos.z); + } + + public static NetherPath emptyPath() { + return EMPTY_PATH; + } +} diff --git a/src/main/java/baritone/process/elytra/NetherPathfinderContext.java b/src/main/java/baritone/process/elytra/NetherPathfinderContext.java new file mode 100644 index 000000000..aa9f4965a --- /dev/null +++ b/src/main/java/baritone/process/elytra/NetherPathfinderContext.java @@ -0,0 +1,238 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 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.elytra; + +import baritone.Baritone; +import baritone.api.event.events.BlockChangeEvent; +import baritone.utils.accessor.IPalettedContainer; +import dev.babbaj.pathfinder.NetherPathfinder; +import dev.babbaj.pathfinder.Octree; +import dev.babbaj.pathfinder.PathSegment; +import net.minecraft.core.BlockPos; +import net.minecraft.util.BitStorage; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.phys.Vec3; + +import java.lang.ref.SoftReference; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author Brady + */ +public final class NetherPathfinderContext { + + private static final BlockState AIR_BLOCK_STATE = Blocks.AIR.defaultBlockState(); + // This lock must be held while there are active pointers to chunks in java, + // but we just hold it for the entire tick so we don't have to think much about it. + public final Object cullingLock = new Object(); + + // Visible for access in BlockStateOctreeInterface + final long context; + private final long seed; + private final ExecutorService executor; + + public NetherPathfinderContext(long seed) { + this.context = NetherPathfinder.newContext(seed); + this.seed = seed; + this.executor = Executors.newSingleThreadExecutor(); + } + + public boolean hasChunk(ChunkPos pos) { + return NetherPathfinder.hasChunkFromJava(this.context, pos.x, pos.z); + } + + public void queueCacheCulling(int chunkX, int chunkZ, int maxDistanceBlocks, BlockStateOctreeInterface boi) { + this.executor.execute(() -> { + synchronized (this.cullingLock) { + boi.chunkPtr = 0L; + NetherPathfinder.cullFarChunks(this.context, chunkX, chunkZ, maxDistanceBlocks); + } + }); + } + + public void queueForPacking(final LevelChunk chunkIn) { + final SoftReference ref = new SoftReference<>(chunkIn); + this.executor.execute(() -> { + // TODO: Prioritize packing recent chunks and/or ones that the path goes through, + // and prune the oldest chunks per chunkPackerQueueMaxSize + final LevelChunk chunk = ref.get(); + if (chunk != null) { + long ptr = NetherPathfinder.getOrCreateChunk(this.context, chunk.getPos().x, chunk.getPos().z); + writeChunkData(chunk, ptr); + } + }); + } + + public void queueBlockUpdate(BlockChangeEvent event) { + this.executor.execute(() -> { + ChunkPos chunkPos = event.getChunkPos(); + long ptr = NetherPathfinder.getChunkPointer(this.context, chunkPos.x, chunkPos.z); + if (ptr == 0) return; // this shouldn't ever happen + event.getBlocks().forEach(pair -> { + BlockPos pos = pair.first(); + if (pos.getY() >= 128) return; + boolean isSolid = pair.second() != AIR_BLOCK_STATE; + Octree.setBlock(ptr, pos.getX() & 15, pos.getY(), pos.getZ() & 15, isSolid); + }); + }); + } + + public CompletableFuture pathFindAsync(final BlockPos src, final BlockPos dst) { + return CompletableFuture.supplyAsync(() -> { + final PathSegment segment = NetherPathfinder.pathFind( + this.context, + src.getX(), src.getY(), src.getZ(), + dst.getX(), dst.getY(), dst.getZ(), + true, + false, + 10000, + !Baritone.settings().elytraPredictTerrain.value + ); + if (segment == null) { + throw new PathCalculationException("Path calculation failed"); + } + return segment; + }, this.executor); + } + + /** + * Performs a raytrace from the given start position to the given end position, returning {@code true} if there is + * visibility between the two points. + * + * @param startX The start X coordinate + * @param startY The start Y coordinate + * @param startZ The start Z coordinate + * @param endX The end X coordinate + * @param endY The end Y coordinate + * @param endZ The end Z coordinate + * @return {@code true} if there is visibility between the points + */ + public boolean raytrace(final double startX, final double startY, final double startZ, + final double endX, final double endY, final double endZ) { + return NetherPathfinder.isVisible(this.context, NetherPathfinder.CACHE_MISS_SOLID, startX, startY, startZ, endX, endY, endZ); + } + + /** + * Performs a raytrace from the given start position to the given end position, returning {@code true} if there is + * visibility between the two points. + * + * @param start The starting point + * @param end The ending point + * @return {@code true} if there is visibility between the points + */ + public boolean raytrace(final Vec3 start, final Vec3 end) { + return NetherPathfinder.isVisible(this.context, NetherPathfinder.CACHE_MISS_SOLID, start.x, start.y, start.z, end.x, end.y, end.z); + } + + public boolean raytrace(final int count, final double[] src, final double[] dst, final int visibility) { + switch (visibility) { + case Visibility.ALL: + return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, false) == -1; + case Visibility.NONE: + return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, true) == -1; + case Visibility.ANY: + return NetherPathfinder.isVisibleMulti(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, true) != -1; + default: + throw new IllegalArgumentException("lol"); + } + } + + public void raytrace(final int count, final double[] src, final double[] dst, final boolean[] hitsOut, final double[] hitPosOut) { + NetherPathfinder.raytrace(this.context, NetherPathfinder.CACHE_MISS_SOLID, count, src, dst, hitsOut, hitPosOut); + } + + public void cancel() { + NetherPathfinder.cancel(this.context); + } + + public void destroy() { + this.cancel(); + // Ignore anything that was queued up, just shutdown the executor + this.executor.shutdownNow(); + + try { + while (!this.executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {} + } catch (InterruptedException e) { + e.printStackTrace(); + } + + NetherPathfinder.freeContext(this.context); + } + + public long getSeed() { + return this.seed; + } + + private static void writeChunkData(LevelChunk chunk, long ptr) { + try { + LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); + for (int y0 = 0; y0 < 8; y0++) { + final LevelChunkSection extendedblockstorage = chunkInternalStorageArray[y0]; + if (extendedblockstorage == null) { + continue; + } + final PalettedContainer bsc = extendedblockstorage.getStates(); + final int airId = ((IPalettedContainer) bsc).getPalette().idFor(AIR_BLOCK_STATE); + // pasted from FasterWorldScanner + final BitStorage array = ((IPalettedContainer) bsc).getStorage(); + if (array == null) continue; + final long[] longArray = array.getRaw(); + final int arraySize = array.getSize(); + int bitsPerEntry = array.getBits(); + long maxEntryValue = (1L << bitsPerEntry) - 1L; + + final int yReal = y0 << 4; + for (int i = 0, idx = 0; i < longArray.length && idx < arraySize; ++i) { + long l = longArray[i]; + for (int offset = 0; offset <= (64 - bitsPerEntry) && idx < arraySize; offset += bitsPerEntry, ++idx) { + int value = (int) ((l >> offset) & maxEntryValue); + int x = (idx & 15); + int y = yReal + (idx >> 8); + int z = ((idx >> 4) & 15); + Octree.setBlock(ptr, x, y, z, value != airId); + } + } + } + Octree.setIsFromJava(ptr); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static final class Visibility { + + public static final int ALL = 0; + public static final int NONE = 1; + public static final int ANY = 2; + + private Visibility() {} + } + + public static boolean isSupported() { + return NetherPathfinder.isThisSystemSupported(); + } +} diff --git a/src/main/java/baritone/process/elytra/NullElytraProcess.java b/src/main/java/baritone/process/elytra/NullElytraProcess.java new file mode 100644 index 000000000..07d5fde0e --- /dev/null +++ b/src/main/java/baritone/process/elytra/NullElytraProcess.java @@ -0,0 +1,90 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process.elytra; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.IElytraProcess; +import baritone.api.process.PathingCommand; +import baritone.utils.BaritoneProcessHelper; +import net.minecraft.core.BlockPos; + +/** + * @author Brady + */ +public final class NullElytraProcess extends BaritoneProcessHelper implements IElytraProcess { + + public NullElytraProcess(Baritone baritone) { + super(baritone); + } + + @Override + public void repackChunks() { + throw new UnsupportedOperationException("Called repackChunks() on NullElytraBehavior"); + } + + @Override + public BlockPos currentDestination() { + return null; + } + + @Override + public void pathTo(BlockPos destination) { + throw new UnsupportedOperationException("Called pathTo() on NullElytraBehavior"); + } + + @Override + public void pathTo(Goal destination) { + throw new UnsupportedOperationException("Called pathTo() on NullElytraBehavior"); + } + + @Override + public void resetState() { + + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + throw new UnsupportedOperationException("Called onTick on NullElytraProcess"); + } + + @Override + public void onLostControl() { + + } + + @Override + public String displayName0() { + return "NullElytraProcess"; + } + + @Override + public boolean isLoaded() { + return false; + } + + @Override + public boolean isSafeToCancel() { + return true; + } +} diff --git a/src/main/java/baritone/process/elytra/PathCalculationException.java b/src/main/java/baritone/process/elytra/PathCalculationException.java new file mode 100644 index 000000000..682ddd296 --- /dev/null +++ b/src/main/java/baritone/process/elytra/PathCalculationException.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.process.elytra; + +/** + * @author Brady + */ +public final class PathCalculationException extends RuntimeException { + + public PathCalculationException(final String message) { + super(message); + } +} diff --git a/src/main/java/baritone/process/elytra/UnpackedSegment.java b/src/main/java/baritone/process/elytra/UnpackedSegment.java new file mode 100644 index 000000000..e50ab3235 --- /dev/null +++ b/src/main/java/baritone/process/elytra/UnpackedSegment.java @@ -0,0 +1,83 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process.elytra; + +import baritone.api.utils.BetterBlockPos; +import dev.babbaj.pathfinder.PathSegment; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Brady + */ +public final class UnpackedSegment { + + private final Stream path; + private final boolean finished; + + public UnpackedSegment(Stream path, boolean finished) { + this.path = path; + this.finished = finished; + } + + public UnpackedSegment append(Stream other, boolean otherFinished) { + // The new segment is only finished if the one getting added on is + return new UnpackedSegment(Stream.concat(this.path, other), otherFinished); + } + + public UnpackedSegment prepend(Stream other) { + return new UnpackedSegment(Stream.concat(other, this.path), this.finished); + } + + public List collect() { + final List path = this.path.collect(Collectors.toList()); + + // Remove backtracks + final Map positionFirstSeen = new HashMap<>(); + for (int i = 0; i < path.size(); i++) { + BetterBlockPos pos = path.get(i); + if (positionFirstSeen.containsKey(pos)) { + int j = positionFirstSeen.get(pos); + while (i > j) { + path.remove(i); + i--; + } + } else { + positionFirstSeen.put(pos, i); + } + } + + return path; + } + + public boolean isFinished() { + return this.finished; + } + + public static UnpackedSegment from(final PathSegment segment) { + return new UnpackedSegment( + Arrays.stream(segment.packed).mapToObj(BetterBlockPos::deserializeFromLong), + segment.finished + ); + } +} diff --git a/src/main/java/baritone/selection/Selection.java b/src/main/java/baritone/selection/Selection.java index d4b115de4..69c77d668 100644 --- a/src/main/java/baritone/selection/Selection.java +++ b/src/main/java/baritone/selection/Selection.java @@ -37,7 +37,7 @@ public Selection(BetterBlockPos pos1, BetterBlockPos pos2) { max.z - min.z + 1 ); - this.aabb = new AABB(this.min, this.max.offset(1, 1, 1)); + this.aabb = new AABB(this.min); } @Override diff --git a/src/main/java/baritone/selection/SelectionManager.java b/src/main/java/baritone/selection/SelectionManager.java index 05d45ee0e..150bd9d17 100644 --- a/src/main/java/baritone/selection/SelectionManager.java +++ b/src/main/java/baritone/selection/SelectionManager.java @@ -4,10 +4,9 @@ import baritone.api.selection.ISelection; import baritone.api.selection.ISelectionManager; import baritone.api.utils.BetterBlockPos; -import net.minecraft.core.Direction; - import java.util.LinkedList; import java.util.ListIterator; +import net.minecraft.core.Direction; public class SelectionManager implements ISelectionManager { diff --git a/src/main/java/baritone/selection/SelectionRenderer.java b/src/main/java/baritone/selection/SelectionRenderer.java index 90c4bca10..4250f4085 100644 --- a/src/main/java/baritone/selection/SelectionRenderer.java +++ b/src/main/java/baritone/selection/SelectionRenderer.java @@ -24,27 +24,27 @@ public static void renderSelections(PoseStack stack, ISelection[] selections) { boolean ignoreDepth = settings.renderSelectionIgnoreDepth.value; float lineWidth = settings.selectionLineWidth.value; - if (!settings.renderSelection.value) { + if (!settings.renderSelection.value || selections.length == 0) { return; } IRenderer.startLines(settings.colorSelection.value, opacity, lineWidth, ignoreDepth); for (ISelection selection : selections) { - IRenderer.drawAABB(stack, selection.aabb(), SELECTION_BOX_EXPANSION); + IRenderer.emitAABB(stack, selection.aabb(), SELECTION_BOX_EXPANSION); } if (settings.renderSelectionCorners.value) { IRenderer.glColor(settings.colorSelectionPos1.value, opacity); for (ISelection selection : selections) { - IRenderer.drawAABB(stack, new AABB(selection.pos1(), selection.pos1().offset(1, 1, 1))); + IRenderer.emitAABB(stack, new AABB(selection.pos1())); } IRenderer.glColor(settings.colorSelectionPos2.value, opacity); for (ISelection selection : selections) { - IRenderer.drawAABB(stack, new AABB(selection.pos2(), selection.pos2().offset(1, 1, 1))); + IRenderer.emitAABB(stack, new AABB(selection.pos2())); } } diff --git a/src/main/java/baritone/utils/BaritoneMath.java b/src/main/java/baritone/utils/BaritoneMath.java new file mode 100644 index 000000000..be546f248 --- /dev/null +++ b/src/main/java/baritone/utils/BaritoneMath.java @@ -0,0 +1,37 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils; + +/** + * @author Brady + */ +public final class BaritoneMath { + + private static final double FLOOR_DOUBLE_D = 1_073_741_824.0; + private static final int FLOOR_DOUBLE_I = 1_073_741_824; + + private BaritoneMath() {} + + public static int fastFloor(final double v) { + return (int) (v + FLOOR_DOUBLE_D) - FLOOR_DOUBLE_I; + } + + public static int fastCeil(final double v) { + return FLOOR_DOUBLE_I - (int) (FLOOR_DOUBLE_D - v); + } +} diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index 5edc1439b..2d209c721 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -17,7 +17,6 @@ package baritone.utils; -import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import net.minecraft.world.InteractionHand; import net.minecraft.world.phys.BlockHitResult; @@ -27,7 +26,7 @@ * @author Brady * @since 8/25/2018 */ -public final class BlockBreakHelper implements Helper { +public final class BlockBreakHelper { private final IPlayerContext ctx; private boolean didBreakLastTick; diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index fb8cba397..93b0c4408 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -18,14 +18,13 @@ package baritone.utils; import baritone.Baritone; -import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; -public class BlockPlaceHelper implements Helper { +public class BlockPlaceHelper { private final IPlayerContext ctx; private int rightClickTimer; diff --git a/src/main/java/baritone/utils/BlockStateInterface.java b/src/main/java/baritone/utils/BlockStateInterface.java index 735736732..095694db9 100644 --- a/src/main/java/baritone/utils/BlockStateInterface.java +++ b/src/main/java/baritone/utils/BlockStateInterface.java @@ -23,7 +23,6 @@ import baritone.cache.WorldData; import baritone.utils.accessor.IClientChunkProvider; import baritone.utils.pathing.BetterWorldBorder; -import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; @@ -42,51 +41,43 @@ */ public class BlockStateInterface { - private static final BlockState AIR = Blocks.AIR.defaultBlockState(); - protected final Level world; private final ClientChunkCache provider; + private final WorldData worldData; + protected final Level world; public final BlockPos.MutableBlockPos isPassableBlockPos; public final BlockGetter access; public final BetterWorldBorder worldBorder; private LevelChunk prev = null; - private final WorldData worldData; + private CachedRegion prevCached = null; private final boolean useTheRealWorld; - private CachedRegion prevCached = null; + + private static final BlockState AIR = Blocks.AIR.defaultBlockState(); public BlockStateInterface(IPlayerContext ctx) { this(ctx, false); } public BlockStateInterface(IPlayerContext ctx, boolean copyLoadedChunks) { - this(ctx.world(), (WorldData) ctx.worldData(), copyLoadedChunks); - } - - public BlockStateInterface(Level world, WorldData worldData, boolean copyLoadedChunks) { - this.world = world; + this.world = ctx.world(); this.worldBorder = new BetterWorldBorder(world.getWorldBorder()); - this.worldData = worldData; + this.worldData = (WorldData) ctx.worldData(); if (copyLoadedChunks) { this.provider = ((IClientChunkProvider) world.getChunkSource()).createThreadSafeCopy(); } else { this.provider = (ClientChunkCache) world.getChunkSource(); } this.useTheRealWorld = !Baritone.settings().pathThroughCachedOnly.value; - if (!Minecraft.getInstance().isSameThread()) { + if (!ctx.minecraft().isSameThread()) { throw new IllegalStateException(); } this.isPassableBlockPos = new BlockPos.MutableBlockPos(); this.access = new BlockStateInterfaceAccessWrapper(this); } - // get the block at x,y,z from this chunk WITHOUT creating a single blockpos object - public static BlockState getFromChunk(LevelChunk chunk, int x, int y, int z) { - LevelChunkSection section = chunk.getSections()[y >> 4]; - if (section.hasOnlyAir()) { - return AIR; - } - return section.getBlockState(x & 15, y & 15, z & 15); + public boolean worldContainsLoadedChunk(int blockX, int blockZ) { + return provider.hasChunk(blockX >> 4, blockZ >> 4); } public static Block getBlock(IPlayerContext ctx, BlockPos pos) { // won't be called from the pathing thread because the pathing thread doesn't make a single blockpos pog @@ -141,7 +132,7 @@ public BlockState get0(int x, int y, int z) { // Mickey resigned prevCached = region; cached = region; } - BlockState type = cached.getBlock(x & 511, y, z & 511); + BlockState type = cached.getBlock(x & 511, y + world.dimensionType().minY(), z & 511); if (type == null) { return AIR; } @@ -173,7 +164,12 @@ public boolean isLoaded(int x, int z) { return prevRegion.isCached(x & 511, z & 511); } - public boolean worldContainsLoadedChunk(int blockX, int blockZ) { - return provider.hasChunk(blockX >> 4, blockZ >> 4); + // get the block at x,y,z from this chunk WITHOUT creating a single blockpos object + public static BlockState getFromChunk(LevelChunk chunk, int x, int y, int z) { + LevelChunkSection section = chunk.getSections()[y >> 4]; + if (section.hasOnlyAir()) { + return AIR; + } + return section.getBlockState(x & 15, y & 15, z & 15); } } diff --git a/src/main/java/baritone/utils/BlockStateInterfaceAccessWrapper.java b/src/main/java/baritone/utils/BlockStateInterfaceAccessWrapper.java index 126e457c4..19b04b454 100644 --- a/src/main/java/baritone/utils/BlockStateInterfaceAccessWrapper.java +++ b/src/main/java/baritone/utils/BlockStateInterfaceAccessWrapper.java @@ -17,14 +17,13 @@ package baritone.utils; +import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; -import javax.annotation.Nullable; - /** * @author Brady * @since 11/5/2019 diff --git a/src/main/java/baritone/utils/GuiClick.java b/src/main/java/baritone/utils/GuiClick.java index f4f1c064b..113450d1c 100644 --- a/src/main/java/baritone/utils/GuiClick.java +++ b/src/main/java/baritone/utils/GuiClick.java @@ -22,15 +22,16 @@ import baritone.api.pathing.goals.GoalBlock; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.ClickEvent; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ClipContext; import net.minecraft.world.phys.AABB; @@ -44,7 +45,6 @@ import java.util.Collections; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; -import static org.lwjgl.opengl.GL11.*; public class GuiClick extends Screen implements Helper { @@ -63,7 +63,7 @@ public boolean isPauseScreen() { } @Override - public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) { + public void render(GuiGraphics stack, int mouseX, int mouseY, float partialTicks) { double mx = mc.mouseHandler.xpos(); double my = mc.mouseHandler.ypos(); @@ -77,7 +77,7 @@ public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) /// Vec3 viewerPos = new Vec3(PathRenderer.posX(), PathRenderer.posY(), PathRenderer.posZ()); LocalPlayer player = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().player(); - HitResult result = player.level.clip(new ClipContext(near.add(viewerPos), far.add(viewerPos), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)); + HitResult result = player.level().clip(new ClipContext(near.add(viewerPos), far.add(viewerPos), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)); if (result != null && result.getType() == HitResult.Type.BLOCK) { currentMouseOver = ((BlockHitResult) result).getBlockPos(); System.out.println("currentMouseOver = " + currentMouseOver); @@ -128,22 +128,11 @@ public void onRender(PoseStack modelViewStack, Matrix4f projectionMatrix) { // drawSingleSelectionBox WHEN? PathRenderer.drawManySelectionBoxes(modelViewStack, e, Collections.singletonList(currentMouseOver), Color.CYAN); if (clickStart != null && !clickStart.equals(currentMouseOver)) { - RenderSystem.enableBlend(); - RenderSystem.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); - //TODO: check - IRenderer.glColor(Color.RED, 0.4F); - RenderSystem.lineWidth(Baritone.settings().pathRenderLineWidthPixels.value); - RenderSystem.disableTexture(); - RenderSystem.depthMask(false); - RenderSystem.disableDepthTest(); + IRenderer.startLines(Color.RED, Baritone.settings().pathRenderLineWidthPixels.value, true); BetterBlockPos a = new BetterBlockPos(currentMouseOver); BetterBlockPos b = new BetterBlockPos(clickStart); - IRenderer.drawAABB(modelViewStack, new AABB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); - RenderSystem.enableDepthTest(); - - RenderSystem.depthMask(true); - RenderSystem.enableTexture(); - RenderSystem.disableBlend(); + IRenderer.emitAABB(modelViewStack, new AABB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); + IRenderer.endLines(true); } } } @@ -165,7 +154,7 @@ private Vec3 toWorld(double x, double y, double z) { return null; } - pos.mul(1 / pos.w()); + pos.mul(1/pos.w()); return new Vec3(pos.x(), pos.y(), pos.z()); } } diff --git a/src/main/java/baritone/utils/IRenderer.java b/src/main/java/baritone/utils/IRenderer.java index 9858cc259..7f6065b72 100644 --- a/src/main/java/baritone/utils/IRenderer.java +++ b/src/main/java/baritone/utils/IRenderer.java @@ -19,22 +19,26 @@ import baritone.api.BaritoneAPI; import baritone.api.Settings; -import baritone.api.utils.Helper; import baritone.utils.accessor.IEntityRenderManager; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix3f; import org.joml.Matrix4f; import java.awt.*; -import static org.lwjgl.opengl.GL11.*; - public interface IRenderer { Tesselator tessellator = Tesselator.getInstance(); BufferBuilder buffer = tessellator.getBuilder(); - IEntityRenderManager renderManager = (IEntityRenderManager) Helper.mc.getEntityRenderDispatcher(); + IEntityRenderManager renderManager = (IEntityRenderManager) Minecraft.getInstance().getEntityRenderDispatcher(); + TextureManager textureManager = Minecraft.getInstance().getTextureManager(); Settings settings = BaritoneAPI.getSettings(); float[] color = new float[]{1.0F, 1.0F, 1.0F, 255.0F}; @@ -49,15 +53,23 @@ static void glColor(Color color, float alpha) { static void startLines(Color color, float alpha, float lineWidth, boolean ignoreDepth) { RenderSystem.enableBlend(); - RenderSystem.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + RenderSystem.blendFuncSeparate( + GlStateManager.SourceFactor.SRC_ALPHA, + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, + GlStateManager.SourceFactor.ONE, + GlStateManager.DestFactor.ZERO + ); glColor(color, alpha); RenderSystem.lineWidth(lineWidth); - RenderSystem.disableTexture(); RenderSystem.depthMask(false); + RenderSystem.disableCull(); if (ignoreDepth) { RenderSystem.disableDepthTest(); } + RenderSystem.setShader(GameRenderer::getRendertypeLinesShader); + buffer.begin(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL); } static void startLines(Color color, float lineWidth, boolean ignoreDepth) { @@ -65,52 +77,80 @@ static void startLines(Color color, float lineWidth, boolean ignoreDepth) { } static void endLines(boolean ignoredDepth) { + tessellator.end(); if (ignoredDepth) { RenderSystem.enableDepthTest(); } + RenderSystem.enableCull(); RenderSystem.depthMask(true); - RenderSystem.enableTexture(); RenderSystem.disableBlend(); } - static void drawAABB(PoseStack stack, AABB aabb) { + static void emitLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2) { + final double dx = x2 - x1; + final double dy = y2 - y1; + final double dz = z2 - z1; + + final double invMag = 1.0 / Math.sqrt(dx * dx + dy * dy + dz * dz); + final float nx = (float) (dx * invMag); + final float ny = (float) (dy * invMag); + final float nz = (float) (dz * invMag); + + emitLine(stack, x1, y1, z1, x2, y2, z2, nx, ny, nz); + } + + static void emitLine(PoseStack stack, + double x1, double y1, double z1, + double x2, double y2, double z2, + double nx, double ny, double nz) { + emitLine(stack, + (float) x1, (float) y1, (float) z1, + (float) x2, (float) y2, (float) z2, + (float) nx, (float) ny, (float) nz + ); + } + + static void emitLine(PoseStack stack, + float x1, float y1, float z1, + float x2, float y2, float z2, + float nx, float ny, float nz) { + final Matrix4f matrix4f = stack.last().pose(); + final Matrix3f normal = stack.last().normal(); + + buffer.vertex(matrix4f, x1, y1, z1).color(color[0], color[1], color[2], color[3]).normal(normal, nx, ny, nz).endVertex(); + buffer.vertex(matrix4f, x2, y2, z2).color(color[0], color[1], color[2], color[3]).normal(normal, nx, ny, nz).endVertex(); + } + + static void emitAABB(PoseStack stack, AABB aabb) { AABB toDraw = aabb.move(-renderManager.renderPosX(), -renderManager.renderPosY(), -renderManager.renderPosZ()); - Matrix4f matrix4f = stack.last().pose(); - //TODO: check - buffer.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR); // bottom - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); + emitLine(stack, toDraw.minX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.minY, toDraw.minZ, 1.0, 0.0, 0.0); + emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.minY, toDraw.maxZ, 0.0, 0.0, 1.0); + emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.minY, toDraw.maxZ, -1.0, 0.0, 0.0); + emitLine(stack, toDraw.minX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.minY, toDraw.minZ, 0.0, 0.0, -1.0); // top - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); + emitLine(stack, toDraw.minX, toDraw.maxY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.minZ, 1.0, 0.0, 0.0); + emitLine(stack, toDraw.maxX, toDraw.maxY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.maxZ, 0.0, 0.0, 1.0); + emitLine(stack, toDraw.maxX, toDraw.maxY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.maxZ, -1.0, 0.0, 0.0); + emitLine(stack, toDraw.minX, toDraw.maxY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.minZ, 0.0, 0.0, -1.0); // corners - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.maxX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.minY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) toDraw.minX, (float) toDraw.maxY, (float) toDraw.maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - tessellator.end(); + emitLine(stack, toDraw.minX, toDraw.minY, toDraw.minZ, toDraw.minX, toDraw.maxY, toDraw.minZ, 0.0, 1.0, 0.0); + emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.minZ, toDraw.maxX, toDraw.maxY, toDraw.minZ, 0.0, 1.0, 0.0); + emitLine(stack, toDraw.maxX, toDraw.minY, toDraw.maxZ, toDraw.maxX, toDraw.maxY, toDraw.maxZ, 0.0, 1.0, 0.0); + emitLine(stack, toDraw.minX, toDraw.minY, toDraw.maxZ, toDraw.minX, toDraw.maxY, toDraw.maxZ, 0.0, 1.0, 0.0); } - static void drawAABB(PoseStack stack, AABB aabb, double expand) { - drawAABB(stack, aabb.inflate(expand, expand, expand)); + static void emitAABB(PoseStack stack, AABB aabb, double expand) { + emitAABB(stack, aabb.inflate(expand, expand, expand)); } + + static void emitLine(PoseStack stack, Vec3 start, Vec3 end) { + double vpX = renderManager.renderPosX(); + double vpY = renderManager.renderPosY(); + double vpZ = renderManager.renderPosZ(); + emitLine(stack, start.x - vpX, start.y - vpY, start.z - vpZ, end.x - vpX, end.y - vpY, end.z - vpZ); + } + } diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index d97012a18..7b9b7bc1a 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -24,7 +24,6 @@ import baritone.api.utils.IInputOverrideHandler; import baritone.api.utils.input.Input; import baritone.behavior.Behavior; -import net.minecraft.client.Minecraft; import net.minecraft.client.player.KeyboardInput; import java.util.HashMap; @@ -105,7 +104,7 @@ public final void onTick(TickEvent event) { } } else { if (ctx.player().input.getClass() == PlayerMovementInput.class) { // allow other movement inputs that aren't this one, e.g. for a freecam - ctx.player().input = new KeyboardInput(Minecraft.getInstance().options); + ctx.player().input = new KeyboardInput(ctx.minecraft().options); } } // only set it if it was previously incorrect @@ -113,7 +112,7 @@ public final void onTick(TickEvent event) { } private boolean inControl() { - for (Input input : new Input[]{Input.MOVE_FORWARD, Input.MOVE_BACK, Input.MOVE_LEFT, Input.MOVE_RIGHT, Input.SNEAK}) { + for (Input input : new Input[]{Input.MOVE_FORWARD, Input.MOVE_BACK, Input.MOVE_LEFT, Input.MOVE_RIGHT, Input.SNEAK, Input.JUMP}) { if (isInputForcedDown(input)) { return true; } diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index 7de02a08e..d22a0058a 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -19,17 +19,14 @@ import baritone.api.BaritoneAPI; import baritone.api.event.events.RenderEvent; -import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.*; import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.behavior.PathingBehavior; import baritone.pathing.path.PathExecutor; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.renderer.blockentity.BeaconRenderer; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; @@ -40,26 +37,23 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import org.joml.Matrix4f; import java.awt.*; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import static org.lwjgl.opengl.GL11.*; - /** * @author Brady * @since 8/9/2018 */ -public final class PathRenderer implements IRenderer, Helper { +public final class PathRenderer implements IRenderer { private static final ResourceLocation TEXTURE_BEACON_BEAM = new ResourceLocation("textures/entity/beacon_beam.png"); - private PathRenderer() { - } + private PathRenderer() {} public static double posX() { return renderManager.renderPosX(); @@ -74,31 +68,27 @@ public static double posZ() { } public static void render(RenderEvent event, PathingBehavior behavior) { - float partialTicks = event.getPartialTicks(); - Goal goal = behavior.getGoal(); - if (Helper.mc.screen instanceof GuiClick) { - ((GuiClick) Helper.mc.screen).onRender(event.getModelViewStack(), event.getProjectionMatrix()); + final IPlayerContext ctx = behavior.ctx; + if (ctx.world() == null) { + return; + } + if (ctx.minecraft().screen instanceof GuiClick) { + ((GuiClick) ctx.minecraft().screen).onRender(event.getModelViewStack(), event.getProjectionMatrix()); } - DimensionType thisPlayerDimension = behavior.baritone.getPlayerContext().world().dimensionType(); - DimensionType currentRenderViewDimension = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world().dimensionType(); + final float partialTicks = event.getPartialTicks(); + final Goal goal = behavior.getGoal(); + + final DimensionType thisPlayerDimension = ctx.world().dimensionType(); + final DimensionType currentRenderViewDimension = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world().dimensionType(); if (thisPlayerDimension != currentRenderViewDimension) { // this is a path for a bot in a different dimension, don't render it return; } - Entity renderView = Helper.mc.getCameraEntity(); - - if (renderView.level != BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world()) { - System.out.println("I have no idea what's going on"); - System.out.println("The primary baritone is in a different world than the render view entity"); - System.out.println("Not rendering the path"); - return; - } - if (goal != null && settings.renderGoal.value) { - drawDankLitGoalBox(event.getModelViewStack(), renderView, goal, partialTicks, settings.colorGoalBox.value); + drawGoal(event.getModelViewStack(), ctx, goal, partialTicks, settings.colorGoalBox.value); } if (!settings.renderPath.value) { @@ -108,9 +98,9 @@ public static void render(RenderEvent event, PathingBehavior behavior) { PathExecutor current = behavior.getCurrent(); // this should prevent most race conditions? PathExecutor next = behavior.getNext(); // like, now it's not possible for current!=null to be true, then suddenly false because of another thread if (current != null && settings.renderSelectionBoxes.value) { - drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toBreak(), settings.colorBlocksToBreak.value); - drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toPlace(), settings.colorBlocksToPlace.value); - drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toWalkInto(), settings.colorBlocksToWalkInto.value); + drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toBreak(), settings.colorBlocksToBreak.value); + drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toPlace(), settings.colorBlocksToPlace.value); + drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), current.toWalkInto(), settings.colorBlocksToWalkInto.value); } //drawManySelectionBoxes(player, Collections.singletonList(behavior.pathStart()), partialTicks, Color.WHITE); @@ -118,33 +108,36 @@ public static void render(RenderEvent event, PathingBehavior behavior) { // Render the current path, if there is one if (current != null && current.getPath() != null) { int renderBegin = Math.max(current.getPosition() - 3, 0); - drawPath(event.getModelViewStack(), current.getPath(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20); + drawPath(event.getModelViewStack(), current.getPath().positions(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20); } if (next != null && next.getPath() != null) { - drawPath(event.getModelViewStack(), next.getPath(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20); + drawPath(event.getModelViewStack(), next.getPath().positions(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20); } // If there is a path calculation currently running, render the path calculation process behavior.getInProgress().ifPresent(currentlyRunning -> { currentlyRunning.bestPathSoFar().ifPresent(p -> { - drawPath(event.getModelViewStack(), p, 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20); + drawPath(event.getModelViewStack(), p.positions(), 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20); }); currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> { - drawPath(event.getModelViewStack(), mr, 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20); - drawManySelectionBoxes(event.getModelViewStack(), renderView, Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value); + drawPath(event.getModelViewStack(), mr.positions(), 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20); + drawManySelectionBoxes(event.getModelViewStack(), ctx.player(), Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value); }); }); } - public static void drawPath(PoseStack stack, IPath path, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { + public static void drawPath(PoseStack stack, List positions, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) { + drawPath(stack, positions, startIndex, color, fadeOut, fadeStart0, fadeEnd0, 0.5D); + } + + public static void drawPath(PoseStack stack, List positions, int startIndex, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0, double offset) { IRenderer.startLines(color, settings.pathRenderLineWidthPixels.value, settings.renderPathIgnoreDepth.value); int fadeStart = fadeStart0 + startIndex; int fadeEnd = fadeEnd0 + startIndex; - List positions = path.positions(); for (int i = startIndex, next; i < positions.size() - 1; i = next) { BetterBlockPos start = positions.get(i); BetterBlockPos end = positions.get(next = i + 1); @@ -174,32 +167,37 @@ public static void drawPath(PoseStack stack, IPath path, int startIndex, Color c IRenderer.glColor(color, alpha); } - drawLine(stack, start.x, start.y, start.z, end.x, end.y, end.z); - - tessellator.end(); + emitPathLine(stack, start.x, start.y, start.z, end.x, end.y, end.z, offset); } IRenderer.endLines(settings.renderPathIgnoreDepth.value); } - - public static void drawLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2) { - Matrix4f matrix4f = stack.last().pose(); + private static void emitPathLine(PoseStack stack, double x1, double y1, double z1, double x2, double y2, double z2, double offset) { + final double extraOffset = offset + 0.03D; double vpX = posX(); double vpY = posY(); double vpZ = posZ(); boolean renderPathAsFrickinThingy = !settings.renderPathAsLine.value; - //TODO: check - buffer.begin(renderPathAsFrickinThingy ? VertexFormat.Mode.DEBUG_LINE_STRIP : VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR); - buffer.vertex(matrix4f, (float) (x1 + 0.5D - vpX), (float) (y1 + 0.5D - vpY), (float) (z1 + 0.5D - vpZ)).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) (x2 + 0.5D - vpX), (float) (y2 + 0.5D - vpY), (float) (z2 + 0.5D - vpZ)).color(color[0], color[1], color[2], color[3]).endVertex(); - + IRenderer.emitLine(stack, + x1 + offset - vpX, y1 + offset - vpY, z1 + offset - vpZ, + x2 + offset - vpX, y2 + offset - vpY, z2 + offset - vpZ + ); if (renderPathAsFrickinThingy) { - buffer.vertex(matrix4f, (float) (x2 + 0.5D - vpX), (float) (y2 + 0.53D - vpY), (float) (z2 + 0.5D - vpZ)).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) (x1 + 0.5D - vpX), (float) (y1 + 0.53D - vpY), (float) (z1 + 0.5D - vpZ)).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) (x1 + 0.5D - vpX), (float) (y1 + 0.5D - vpY), (float) (z1 + 0.5D - vpZ)).color(color[0], color[1], color[2], color[3]).endVertex(); + IRenderer.emitLine(stack, + x2 + offset - vpX, y2 + offset - vpY, z2 + offset - vpZ, + x2 + offset - vpX, y2 + extraOffset - vpY, z2 + offset - vpZ + ); + IRenderer.emitLine(stack, + x2 + offset - vpX, y2 + extraOffset - vpY, z2 + offset - vpZ, + x1 + offset - vpX, y1 + extraOffset - vpY, z1 + offset - vpZ + ); + IRenderer.emitLine(stack, + x1 + offset - vpX, y1 + extraOffset - vpY, z1 + offset - vpZ, + x1 + offset - vpX, y1 + offset - vpY, z1 + offset - vpZ + ); } } @@ -211,16 +209,20 @@ public static void drawManySelectionBoxes(PoseStack stack, Entity player, Collec positions.forEach(pos -> { BlockState state = bsi.get0(pos); - VoxelShape shape = state.getShape(player.level, pos); + VoxelShape shape = state.getShape(player.level(), pos); AABB toDraw = shape.isEmpty() ? Shapes.block().bounds() : shape.bounds(); toDraw = toDraw.move(pos); - IRenderer.drawAABB(stack, toDraw, .002D); + IRenderer.emitAABB(stack, toDraw, .002D); }); IRenderer.endLines(settings.renderSelectionBoxesIgnoreDepth.value); } - public static void drawDankLitGoalBox(PoseStack stack, Entity player, Goal goal, float partialTicks, Color color) { + public static void drawGoal(PoseStack stack, IPlayerContext ctx, Goal goal, float partialTicks, Color color) { + drawGoal(stack, ctx, goal, partialTicks, color, true); + } + + private static void drawGoal(PoseStack stack, IPlayerContext ctx, Goal goal, float partialTicks, Color color, boolean setupRender) { double renderPosX = posX(); double renderPosY = posY(); double renderPosZ = posZ(); @@ -252,14 +254,15 @@ public static void drawDankLitGoalBox(PoseStack stack, Entity player, Goal goal, y2 -= 0.5; maxY--; } + drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } else if (goal instanceof GoalXZ) { GoalXZ goalPos = (GoalXZ) goal; + minY = ctx.world().getMinBuildHeight(); + maxY = ctx.world().getMaxBuildHeight(); if (settings.renderGoalXZBeacon.value) { - glPushAttrib(GL_LIGHTING_BIT); - //TODO: check - Helper.mc.getTextureManager().bindForSetup(TEXTURE_BEACON_BEAM); + textureManager.bindForSetup(TEXTURE_BEACON_BEAM); if (settings.renderGoalIgnoreDepth.value) { RenderSystem.disableDepthTest(); } @@ -270,13 +273,13 @@ public static void drawDankLitGoalBox(PoseStack stack, Entity player, Goal goal, //TODO: check BeaconRenderer.renderBeaconBeam( stack, - mc.renderBuffers().bufferSource(), + ctx.minecraft().renderBuffers().bufferSource(), TEXTURE_BEACON_BEAM, settings.renderGoalAnimated.value ? partialTicks : 0, 1.0F, - settings.renderGoalAnimated.value ? player.level.getGameTime() : 0, - 0, - 256, + settings.renderGoalAnimated.value ? ctx.world().getGameTime() : 0, + (int) minY, + (int) maxY, color.getColorComponents(null), // Arguments filled by the private method lol @@ -289,8 +292,6 @@ public static void drawDankLitGoalBox(PoseStack stack, Entity player, Goal goal, if (settings.renderGoalIgnoreDepth.value) { RenderSystem.enableDepthTest(); } - - glPopAttrib(); return; } @@ -301,61 +302,65 @@ public static void drawDankLitGoalBox(PoseStack stack, Entity player, Goal goal, y1 = 0; y2 = 0; - minY = 0 - renderPosY; - maxY = 256 - renderPosY; + minY -= renderPosY; + maxY -= renderPosY; + drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } else if (goal instanceof GoalComposite) { + // Simple way to determine if goals can be batched, without having some sort of GoalRenderer + boolean batch = Arrays.stream(((GoalComposite) goal).goals()).allMatch(IGoalRenderPos.class::isInstance); + + if (batch) { + IRenderer.startLines(color, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); + } for (Goal g : ((GoalComposite) goal).goals()) { - drawDankLitGoalBox(stack, player, g, partialTicks, color); + drawGoal(stack, ctx, g, partialTicks, color, !batch); + } + if (batch) { + IRenderer.endLines(settings.renderGoalIgnoreDepth.value); } - return; } else if (goal instanceof GoalInverted) { - drawDankLitGoalBox(stack, player, ((GoalInverted) goal).origin, partialTicks, settings.colorInvertedGoalBox.value); - return; + drawGoal(stack, ctx, ((GoalInverted) goal).origin, partialTicks, settings.colorInvertedGoalBox.value); } else if (goal instanceof GoalYLevel) { GoalYLevel goalpos = (GoalYLevel) goal; - minX = player.position().x - settings.yLevelBoxSize.value - renderPosX; - minZ = player.position().z - settings.yLevelBoxSize.value - renderPosZ; - maxX = player.position().x + settings.yLevelBoxSize.value - renderPosX; - maxZ = player.position().z + settings.yLevelBoxSize.value - renderPosZ; + minX = ctx.player().position().x - settings.yLevelBoxSize.value - renderPosX; + minZ = ctx.player().position().z - settings.yLevelBoxSize.value - renderPosZ; + maxX = ctx.player().position().x + settings.yLevelBoxSize.value - renderPosX; + maxZ = ctx.player().position().z + settings.yLevelBoxSize.value - renderPosZ; minY = ((GoalYLevel) goal).level - renderPosY; maxY = minY + 2; y1 = 1 + y + goalpos.level - renderPosY; y2 = 1 - y + goalpos.level - renderPosY; - } else { - return; + drawDankLitGoalBox(stack, color, minX, maxX, minZ, maxZ, minY, maxY, y1, y2, setupRender); } + } - IRenderer.startLines(color, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); + private static void drawDankLitGoalBox(PoseStack stack, Color colorIn, double minX, double maxX, double minZ, double maxZ, double minY, double maxY, double y1, double y2, boolean setupRender) { + if (setupRender) { + IRenderer.startLines(colorIn, settings.goalRenderLineWidthPixels.value, settings.renderGoalIgnoreDepth.value); + } renderHorizontalQuad(stack, minX, maxX, minZ, maxZ, y1); renderHorizontalQuad(stack, minX, maxX, minZ, maxZ, y2); - Matrix4f matrix4f = stack.last().pose(); - buffer.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR); - buffer.vertex(matrix4f, (float) minX, (float) minY, (float) minZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) minX, (float) maxY, (float) minZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) minY, (float) minZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) maxY, (float) minZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) minY, (float) maxZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) maxY, (float) maxZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) minX, (float) minY, (float) maxZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - buffer.vertex(matrix4f, (float) minX, (float) maxY, (float) maxZ).color(IRenderer.color[0], IRenderer.color[1], IRenderer.color[2], IRenderer.color[3]).endVertex(); - tessellator.end(); - - IRenderer.endLines(settings.renderGoalIgnoreDepth.value); + for (double y = minY; y < maxY; y += 16) { + double max = Math.min(maxY, y + 16); + IRenderer.emitLine(stack, minX, y, minZ, minX, max, minZ, 0.0, 1.0, 0.0); + IRenderer.emitLine(stack, maxX, y, minZ, maxX, max, minZ, 0.0, 1.0, 0.0); + IRenderer.emitLine(stack, maxX, y, maxZ, maxX, max, maxZ, 0.0, 1.0, 0.0); + IRenderer.emitLine(stack, minX, y, maxZ, minX, max, maxZ, 0.0, 1.0, 0.0); + } + + if (setupRender) { + IRenderer.endLines(settings.renderGoalIgnoreDepth.value); + } } private static void renderHorizontalQuad(PoseStack stack, double minX, double maxX, double minZ, double maxZ, double y) { if (y != 0) { - Matrix4f matrix4f = stack.last().pose(); - //TODO: check - buffer.begin(VertexFormat.Mode.DEBUG_LINE_STRIP, DefaultVertexFormat.POSITION_COLOR); - buffer.vertex(matrix4f, (float) minX, (float) y, (float) minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) y, (float) minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) maxX, (float) y, (float) maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) minX, (float) y, (float) maxZ).color(color[0], color[1], color[2], color[3]).endVertex(); - buffer.vertex(matrix4f, (float) minX, (float) y, (float) minZ).color(color[0], color[1], color[2], color[3]).endVertex(); - tessellator.end(); + IRenderer.emitLine(stack, minX, y, minZ, maxX, y, minZ, 1.0, 0.0, 0.0); + IRenderer.emitLine(stack, maxX, y, minZ, maxX, y, maxZ, 0.0, 0.0, 1.0); + IRenderer.emitLine(stack, maxX, y, maxZ, minX, y, maxZ, -1.0, 0.0, 0.0); + IRenderer.emitLine(stack, minX, y, maxZ, minX, y, minZ, 0.0, 0.0, -1.0); } } } diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index 637d515f8..3566cd23a 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -99,6 +99,8 @@ public void preTick() { // get rid of the in progress stuff from the last process } switch (command.commandType) { + case SET_GOAL_AND_PAUSE: + p.secretInternalSetGoalAndPath(command); case REQUEST_PAUSE: p.requestPause(); break; @@ -107,10 +109,6 @@ public void preTick() { p.cancelSegmentIfSafe(); break; case FORCE_REVALIDATE_GOAL_AND_PATH: - if (!p.isPathing() && !p.getInProgress().isPresent()) { - p.secretInternalSetGoalAndPath(command); - } - break; case REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { p.secretInternalSetGoalAndPath(command); @@ -119,7 +117,7 @@ public void preTick() { case SET_GOAL_AND_PATH: // now this i can do if (command.goal != null) { - baritone.getPathingBehavior().secretInternalSetGoalAndPath(command); + p.secretInternalSetGoalAndPath(command); } break; default: @@ -160,7 +158,7 @@ public boolean forceRevalidate(Goal newGoal) { if (newGoal.isInGoal(current.getPath().getDest())) { return false; } - return !newGoal.toString().equals(current.getPath().getGoal().toString()); + return !newGoal.equals(current.getPath().getGoal()); } return false; } diff --git a/src/main/java/baritone/utils/ToolSet.java b/src/main/java/baritone/utils/ToolSet.java index 1156f13f5..ab1eeebb0 100644 --- a/src/main/java/baritone/utils/ToolSet.java +++ b/src/main/java/baritone/utils/ToolSet.java @@ -93,36 +93,8 @@ private int getMaterialCost(ItemStack itemStack) { } } - /** - * Calculates how long would it take to mine the specified block given the best tool - * in this toolset is used. A negative value is returned if the specified block is unbreakable. - * - * @param item the item to mine it with - * @param state the blockstate to be mined - * @return how long it would take in ticks - */ - public static double calculateSpeedVsBlock(ItemStack item, BlockState state) { - float hardness = state.getDestroySpeed(null, null); - if (hardness < 0) { - return -1; - } - - float speed = item.getDestroySpeed(state); - if (speed > 1) { - int effLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, item); - if (effLevel > 0 && !item.isEmpty()) { - speed += effLevel * effLevel + 1; - } - } - if (AltoClefSettings.getInstance().shouldForceUseTool(state, item)) { - return Double.POSITIVE_INFINITY; - } - speed /= hardness; - if (!state.requiresCorrectToolForDrops() || (!item.isEmpty() && item.isCorrectToolForDrops(state))) { - return speed / 30; - } else { - return speed / 100; - } + public boolean hasSilkTouch(ItemStack stack) { + return EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0; } /** @@ -193,14 +165,42 @@ private double getBestDestructionTime(Block b) { return calculateSpeedVsBlock(stack, b.defaultBlockState()) * avoidanceMultiplier(b); } - public boolean hasSilkTouch(ItemStack stack) { - return EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0; - } - private double avoidanceMultiplier(Block b) { return Baritone.settings().blocksToAvoidBreaking.value.contains(b) ? Baritone.settings().avoidBreakingMultiplier.value : 1; } + /** + * Calculates how long would it take to mine the specified block given the best tool + * in this toolset is used. A negative value is returned if the specified block is unbreakable. + * + * @param item the item to mine it with + * @param state the blockstate to be mined + * @return how long it would take in ticks + */ + public static double calculateSpeedVsBlock(ItemStack item, BlockState state) { + float hardness = state.getDestroySpeed(null, null); + if (hardness < 0) { + return -1; + } + + float speed = item.getDestroySpeed(state); + if (speed > 1) { + int effLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, item); + if (effLevel > 0 && !item.isEmpty()) { + speed += effLevel * effLevel + 1; + } + } + if (AltoClefSettings.getInstance().shouldForceUseTool(state, item)) { + return Double.POSITIVE_INFINITY; + } + speed /= hardness; + if (!state.requiresCorrectToolForDrops() || (!item.isEmpty() && item.isCorrectToolForDrops(state))) { + return speed / 30; + } else { + return speed / 100; + } + } + /** * Calculates any modifier to breaking time based on status effects. * diff --git a/src/main/java/baritone/utils/Trail.java b/src/main/java/baritone/utils/Trail.java deleted file mode 100644 index e6a497fbe..000000000 --- a/src/main/java/baritone/utils/Trail.java +++ /dev/null @@ -1,180 +0,0 @@ -package baritone.utils; - -import baritone.api.BaritoneAPI; -import baritone.api.behavior.IPathingBehavior; -import baritone.api.pathing.goals.GoalXZ; -import baritone.api.pathing.path.IPathExecutor; -import baritone.api.process.PathingCommand; -import baritone.api.process.PathingCommandType; -import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.IPlayerContext; -import baritone.api.utils.RandomSpotNearby; -import net.minecraft.core.Vec3i; -import net.minecraft.world.level.ChunkPos; - -import java.util.*; - -/** - * @author Meloweh - */ -public final class Trail { - private final static int VECTOR_LIMIT = 350; - private final static int CROSS_LIMIT = 10; - private final static int CHUNK_TAIL_MAX = 4 * 4; - private final static Trail trail = new Trail(); - private final Queue traversedChunks; - private final IPlayerContext player; - private BetterBlockPos prev; - private boolean pathing = false; - private BetterBlockPos stuckPos; - private RandomSpotNearby randSpot = new RandomSpotNearby(16d); - private BetterBlockPos targetPos; - - public Trail() { - this.player = BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext(); - this.traversedChunks = new ArrayDeque<>(); - } - - public static Trail getInstance() { - return trail; - } - - private static boolean isSet(final Object o) { - return o != null; - } - - private static boolean isNull(final Object o) { - return !isSet(o); - } - - private static BetterBlockPos getCurrent() { - final IPathingBehavior p = BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior(); - if (isNull(p)) return null; - final IPathExecutor c = p.getCurrent(); - if (isNull(c)) return null; - final List bbp = c.getPath().positions(); - if (isNull(bbp)) return null; - final int posIndex = c.getPosition(); - if (c.getPath().positions().size() <= posIndex) return null; - final BetterBlockPos pos = c.getPath().positions().get(posIndex); - return pos; - } - - private boolean equalPos(final BetterBlockPos pos1, final BetterBlockPos pos2) { - if (isNull(pos1) || isNull(pos2)) return false; - return pos1.getX() == pos2.getX() && pos1.getY() == pos2.getY() && pos1.getZ() == pos2.getZ(); - } - - private boolean equalChunk(final ChunkPos c1, final ChunkPos c2) { - return c1.x == c2.x && c1.z == c2.z; - } - - public boolean passedLimits() { - return traversedChunks.stream().anyMatch(e -> - e.crossings.entrySet().stream().anyMatch(a -> a.getValue() > CROSS_LIMIT)); - } - - private int getVectorCount() { - return traversedChunks.stream().mapToInt(e -> e.crossings.size()).sum(); - } - - private boolean passedVectorCountLimit() { - return getVectorCount() > VECTOR_LIMIT; - } - - private Optional getRefFromTraversedChunks(final ChunkPos pos) { - return traversedChunks.stream().filter(e -> equalChunk(e.chunkPos, pos)).findFirst(); - } - - public void reset() { - traversedChunks.clear(); - prev = null; - pathing = false; - stuckPos = null; - System.out.println("target null"); - targetPos = null; - } - - private Pack removeOutsiders() { - if (traversedChunks.size() > CHUNK_TAIL_MAX) return traversedChunks.poll(); - if (passedVectorCountLimit()) traversedChunks.clear(); - return null; - } - - public boolean reactivateRunAway() { - final BetterBlockPos pos = getCurrent(); - if (isNull(pos)) return false; - stuckPos = pos; - System.out.println("target set"); - targetPos = randSpot.next(stuckPos); - pathing = true; - return true; - } - - public boolean isRunAwayActive() { - return pathing; - } - - public boolean updateAndCheck() { - tick(); - - if (!passedLimits()) { - return false; - } - - final BetterBlockPos curr = getCurrent(); - if (isNull(curr)) return false; - - if (targetPos != null && curr.closerThan(new Vec3i(targetPos.getX(), curr.getY(), targetPos.getZ()), 3d)) { - reset(); - return false; - } - if (!pathing) { - reactivateRunAway(); - } - - return true; - } - - public PathingCommand getRunAwayCommand() { - if (targetPos == null) { - throw new IllegalStateException("targetPos is null"); - } - return new PathingCommand(new GoalXZ(targetPos), PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); - } - - private int addOrInc(final Pack pack, final BetterBlockPos blockPos, final boolean shouldAddPack) { - final Optional optRefBlockPos = pack.crossings.keySet().stream().filter(e -> equalPos(e, blockPos)).findFirst(); - final BetterBlockPos refBlockPos = (optRefBlockPos.isEmpty()) ? blockPos : optRefBlockPos.get(); - final int counter = (optRefBlockPos.isEmpty()) ? 1 : pack.crossings.get(refBlockPos) + 1; - pack.crossings.put(refBlockPos, counter); - if (shouldAddPack) traversedChunks.add(pack); - return counter; - } - - //TODO tick() may be called multiple times in one tick. This shouldn't be limited to one possible update per tick. - public void tick() { - final BetterBlockPos curr = getCurrent(); - if (isNull(curr) || equalPos(prev, curr)) return; - prev = curr; - final ChunkPos chunkPos = this.player.world().getChunk(curr).getPos(); - final Optional optPack = getRefFromTraversedChunks(chunkPos); - final Pack pack = optPack.isEmpty() ? new Pack(chunkPos) : optPack.get(); - final int counts = addOrInc(pack, curr, optPack.isEmpty()); - removeOutsiders(); - - //debug - //System.out.println(curr.getX() + ":" + curr.getY() + ":" + curr.getZ() + " Counts: " - // + counts + " Size: " + getVectorCount()); - } - - private class Pack { - public ChunkPos chunkPos; - public Map crossings; - - public Pack(final ChunkPos chunkPos) { - this.chunkPos = chunkPos; - this.crossings = new HashMap<>(); - } - } -} diff --git a/src/main/java/baritone/utils/accessor/IChunkArray.java b/src/main/java/baritone/utils/accessor/IChunkArray.java index dc627bd34..4c76b44fe 100644 --- a/src/main/java/baritone/utils/accessor/IChunkArray.java +++ b/src/main/java/baritone/utils/accessor/IChunkArray.java @@ -17,9 +17,8 @@ package baritone.utils.accessor; -import net.minecraft.world.level.chunk.LevelChunk; - import java.util.concurrent.atomic.AtomicReferenceArray; +import net.minecraft.world.level.chunk.LevelChunk; public interface IChunkArray { void copyFrom(IChunkArray other); diff --git a/src/main/java/baritone/utils/accessor/IFireworkRocketEntity.java b/src/main/java/baritone/utils/accessor/IFireworkRocketEntity.java new file mode 100644 index 000000000..36690dbc1 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IFireworkRocketEntity.java @@ -0,0 +1,25 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.accessor; + +import net.minecraft.world.entity.LivingEntity; + +public interface IFireworkRocketEntity { + + LivingEntity getBoostedEntity(); +} diff --git a/src/main/java/baritone/utils/accessor/IPalettedContainer.java b/src/main/java/baritone/utils/accessor/IPalettedContainer.java new file mode 100644 index 000000000..c7281080d --- /dev/null +++ b/src/main/java/baritone/utils/accessor/IPalettedContainer.java @@ -0,0 +1,36 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.accessor; + +import net.minecraft.util.BitStorage; +import net.minecraft.world.level.chunk.Palette; + +public interface IPalettedContainer { + + Palette getPalette(); + + BitStorage getStorage(); + + + public interface IData { + + Palette getPalette(); + + BitStorage getStorage(); + } +} diff --git a/src/main/java/baritone/utils/pathing/Avoidance.java b/src/main/java/baritone/utils/pathing/Avoidance.java index 61229cce4..0b4b95843 100644 --- a/src/main/java/baritone/utils/pathing/Avoidance.java +++ b/src/main/java/baritone/utils/pathing/Avoidance.java @@ -21,16 +21,15 @@ import baritone.api.utils.BetterBlockPos; import baritone.api.utils.IPlayerContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.monster.EnderMan; import net.minecraft.world.entity.monster.Spider; import net.minecraft.world.entity.monster.ZombifiedPiglin; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - public class Avoidance { private final int centerX; diff --git a/src/main/java/baritone/utils/player/PrimaryPlayerContext.java b/src/main/java/baritone/utils/player/BaritonePlayerContext.java similarity index 55% rename from src/main/java/baritone/utils/player/PrimaryPlayerContext.java rename to src/main/java/baritone/utils/player/BaritonePlayerContext.java index 0e9060379..4ae89945f 100644 --- a/src/main/java/baritone/utils/player/PrimaryPlayerContext.java +++ b/src/main/java/baritone/utils/player/BaritonePlayerContext.java @@ -17,13 +17,12 @@ package baritone.utils.player; -import baritone.api.BaritoneAPI; +import baritone.Baritone; import baritone.api.cache.IWorldData; -import baritone.api.utils.Helper; -import baritone.api.utils.IPlayerContext; -import baritone.api.utils.IPlayerController; -import baritone.api.utils.RayTraceUtils; +import baritone.api.utils.*; +import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.phys.HitResult; @@ -33,28 +32,52 @@ * @author Brady * @since 11/12/2018 */ -public enum PrimaryPlayerContext implements IPlayerContext, Helper { +public final class BaritonePlayerContext implements IPlayerContext { - INSTANCE; + private final Baritone baritone; + private final Minecraft mc; + private final IPlayerController playerController; + + public BaritonePlayerContext(Baritone baritone, Minecraft mc) { + this.baritone = baritone; + this.mc = mc; + this.playerController = new BaritonePlayerController(mc); + } + + @Override + public Minecraft minecraft() { + return this.mc; + } @Override public LocalPlayer player() { - return mc.player; + return this.mc.player; } @Override public IPlayerController playerController() { - return PrimaryPlayerController.INSTANCE; + return this.playerController; } @Override public Level world() { - return mc.level; + return this.mc.level; } @Override public IWorldData worldData() { - return BaritoneAPI.getProvider().getPrimaryBaritone().getWorldProvider().getCurrentWorld(); + return this.baritone.getWorldProvider().getCurrentWorld(); + } + + @Override + public BetterBlockPos viewerPos() { + final Entity entity = this.mc.getCameraEntity(); + return entity == null ? this.playerFeet() : BetterBlockPos.from(entity.blockPosition()); + } + + @Override + public Rotation playerRotations() { + return this.baritone.getLookBehavior().getEffectiveRotation().orElseGet(IPlayerContext.super::playerRotations); } @Override diff --git a/src/main/java/baritone/utils/player/PrimaryPlayerController.java b/src/main/java/baritone/utils/player/BaritonePlayerController.java similarity index 91% rename from src/main/java/baritone/utils/player/PrimaryPlayerController.java rename to src/main/java/baritone/utils/player/BaritonePlayerController.java index 70cc5a014..42ba49052 100644 --- a/src/main/java/baritone/utils/player/PrimaryPlayerController.java +++ b/src/main/java/baritone/utils/player/BaritonePlayerController.java @@ -17,9 +17,10 @@ package baritone.utils.player; -import baritone.api.utils.Helper; import baritone.api.utils.IPlayerController; import baritone.utils.accessor.IPlayerControllerMP; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -38,9 +39,13 @@ * @author Brady * @since 12/14/2018 */ -public enum PrimaryPlayerController implements IPlayerController, Helper { +public final class BaritonePlayerController implements IPlayerController { - INSTANCE; + private final Minecraft mc; + + public BaritonePlayerController(Minecraft mc) { + this.mc = mc; + } @Override public void syncHeldItem() { diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java index 48c35b80c..4f71aea64 100644 --- a/src/main/java/baritone/utils/schematic/MapArtSchematic.java +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -19,11 +19,10 @@ import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.MaskSchematic; -import net.minecraft.world.level.block.AirBlock; -import net.minecraft.world.level.block.state.BlockState; - import java.util.OptionalInt; import java.util.function.Predicate; +import net.minecraft.world.level.block.AirBlock; +import net.minecraft.world.level.block.state.BlockState; public class MapArtSchematic extends MaskSchematic { diff --git a/src/main/java/baritone/utils/schematic/StaticSchematic.java b/src/main/java/baritone/utils/schematic/StaticSchematic.java index 61618fbe1..5a110281b 100644 --- a/src/main/java/baritone/utils/schematic/StaticSchematic.java +++ b/src/main/java/baritone/utils/schematic/StaticSchematic.java @@ -19,9 +19,8 @@ import baritone.api.schematic.AbstractSchematic; import baritone.api.schematic.IStaticSchematic; -import net.minecraft.world.level.block.state.BlockState; - import java.util.List; +import net.minecraft.world.level.block.state.BlockState; /** * Default implementation of {@link IStaticSchematic} diff --git a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java index ca3dad029..94a01a157 100644 --- a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java +++ b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java @@ -22,13 +22,14 @@ import baritone.utils.schematic.format.defaults.LitematicaSchematic; import baritone.utils.schematic.format.defaults.MCEditSchematic; import baritone.utils.schematic.format.defaults.SpongeSchematic; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtAccounter; import org.apache.commons.io.FilenameUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; /** * Default implementations of {@link ISchematicFormat} @@ -44,7 +45,7 @@ public enum DefaultSchematicFormats implements ISchematicFormat { MCEDIT("schematic") { @Override public IStaticSchematic parse(InputStream input) throws IOException { - return new MCEditSchematic(NbtIo.readCompressed(input)); + return new MCEditSchematic(NbtIo.readCompressed(input, NbtAccounter.unlimitedHeap())); } }, @@ -56,7 +57,7 @@ public IStaticSchematic parse(InputStream input) throws IOException { SPONGE("schem") { @Override public IStaticSchematic parse(InputStream input) throws IOException { - CompoundTag nbt = NbtIo.readCompressed(input); + CompoundTag nbt = NbtIo.readCompressed(input, NbtAccounter.unlimitedHeap()); int version = nbt.getInt("Version"); switch (version) { case 1: @@ -74,7 +75,7 @@ public IStaticSchematic parse(InputStream input) throws IOException { LITEMATICA("litematic") { @Override public IStaticSchematic parse(InputStream input) throws IOException { - CompoundTag nbt = NbtIo.readCompressed(input); + CompoundTag nbt = NbtIo.readCompressed(input, NbtAccounter.unlimitedHeap()); int version = nbt.getInt("Version"); switch (version) { case 4: //1.12 diff --git a/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java new file mode 100644 index 000000000..65cdec058 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java @@ -0,0 +1,348 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.schematic.format.defaults; + +import baritone.utils.schematic.StaticSchematic; +import net.minecraft.core.Registry; +import net.minecraft.core.Vec3i; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import org.apache.commons.lang3.Validate; + +import javax.annotation.Nullable; +import java.util.Optional; + +/** + * Based on EmersonDove's work + * ... + * + * @author rycbar + * @since 22.09.2022 + */ +public final class LitematicaSchematic extends StaticSchematic { + private final Vec3i offsetMinCorner; + private final CompoundTag nbt; + + /** + * @param nbtTagCompound a decompressed file stream aka nbt data. + * @param rotated if the schematic is rotated by 90°. + */ + public LitematicaSchematic(CompoundTag nbtTagCompound, boolean rotated) { + this.nbt = nbtTagCompound; + this.offsetMinCorner = new Vec3i(getMinOfSchematic("x"), getMinOfSchematic("y"), getMinOfSchematic("z")); + this.y = Math.abs(nbt.getCompound("Metadata").getCompound("EnclosingSize").getInt("y")); + + if (rotated) { + this.x = Math.abs(nbt.getCompound("Metadata").getCompound("EnclosingSize").getInt("z")); + this.z = Math.abs(nbt.getCompound("Metadata").getCompound("EnclosingSize").getInt("x")); + } else { + this.x = Math.abs(nbt.getCompound("Metadata").getCompound("EnclosingSize").getInt("x")); + this.z = Math.abs(nbt.getCompound("Metadata").getCompound("EnclosingSize").getInt("z")); + } + this.states = new BlockState[this.x][this.z][this.y]; + fillInSchematic(); + } + + /** + * @return Array of subregion names. + */ + private static String[] getRegions(CompoundTag nbt) { + return nbt.getCompound("Regions").getAllKeys().toArray(new String[0]); + } + + /** + * Gets both ends from a region box for a given axis and returns the lower one. + * + * @param s axis that should be read. + * @return the lower coord of the requested axis. + */ + private static int getMinOfSubregion(CompoundTag nbt, String subReg, String s) { + int a = nbt.getCompound("Regions").getCompound(subReg).getCompound("Position").getInt(s); + int b = nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt(s); + if (b < 0) { + b++; + } + return Math.min(a, a + b); + + } + + /** + * @param blockStatePalette List of all different block types used in the schematic. + * @return Array of BlockStates. + */ + private static BlockState[] getBlockList(ListTag blockStatePalette) { + BlockState[] blockList = new BlockState[blockStatePalette.size()]; + + for (int i = 0; i < blockStatePalette.size(); i++) { + Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation((((CompoundTag) blockStatePalette.get(i)).getString("Name")))); + CompoundTag properties = ((CompoundTag) blockStatePalette.get(i)).getCompound("Properties"); + + blockList[i] = getBlockState(block, properties); + } + return blockList; + } + + /** + * @param block block. + * @param properties List of Properties the block has. + * @return A blockState. + */ + private static BlockState getBlockState(Block block, CompoundTag properties) { + BlockState blockState = block.defaultBlockState(); + + for (Object key : properties.getAllKeys().toArray()) { + Property property = block.getStateDefinition().getProperty((String) key); + String propertyValue = properties.getString((String) key); + if (property != null) { + blockState = setPropertyValue(blockState, property, propertyValue); + } + } + return blockState; + } + + /** + * @author Emerson + */ + private static > BlockState setPropertyValue(BlockState state, Property property, String value) { + Optional parsed = property.getValue(value); + if (parsed.isPresent()) { + return state.setValue(property, parsed.get()); + } else { + throw new IllegalArgumentException("Invalid value for property " + property); + } + } + + /** + * @param amountOfBlockTypes amount of block types in the schematic. + * @return amount of bits used to encode a block. + */ + private static int getBitsPerBlock(int amountOfBlockTypes) { + return (int) Math.max(2, Math.ceil(Math.log(amountOfBlockTypes) / Math.log(2))); + } + + /** + * Calculates the volume of the subregion. As size can be a negative value we take the absolute value of the + * multiplication as the volume still holds a positive amount of blocks. + * + * @return the volume of the subregion. + */ + private static long getVolume(CompoundTag nbt, String subReg) { + return Math.abs( + nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("x") * + nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("y") * + nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("z")); + } + + /** + * @return array of Long values. + */ + private static long[] getBlockStates(CompoundTag nbt, String subReg) { + return nbt.getCompound("Regions").getCompound(subReg).getLongArray("BlockStates"); + } + + /** + * Subregion don't have to be the same size as the enclosing size of the schematic. If they are smaller we check here if the current block is part of the subregion. + * + * @param x coord of the block relative to the minimum corner. + * @param y coord of the block relative to the minimum corner. + * @param z coord of the block relative to the minimum corner. + * @return if the current block is part of the subregion. + */ + private static boolean inSubregion(CompoundTag nbt, String subReg, int x, int y, int z) { + return x >= 0 && y >= 0 && z >= 0 && + x < Math.abs(nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("x")) && + y < Math.abs(nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("y")) && + z < Math.abs(nbt.getCompound("Regions").getCompound(subReg).getCompound("Size").getInt("z")); + } + + /** + * @param s axis. + * @return the lowest coordinate of that axis of the schematic. + */ + private int getMinOfSchematic(String s) { + int n = Integer.MAX_VALUE; + for (String subReg : getRegions(nbt)) { + n = Math.min(n, getMinOfSubregion(nbt, subReg, s)); + } + return n; + } + + /** + * reads the file data. + */ + private void fillInSchematic() { + for (String subReg : getRegions(nbt)) { + ListTag usedBlockTypes = nbt.getCompound("Regions").getCompound(subReg).getList("BlockStatePalette", 10); + BlockState[] blockList = getBlockList(usedBlockTypes); + + int bitsPerBlock = getBitsPerBlock(usedBlockTypes.size()); + long regionVolume = getVolume(nbt, subReg); + long[] blockStateArray = getBlockStates(nbt, subReg); + + LitematicaBitArray bitArray = new LitematicaBitArray(bitsPerBlock, regionVolume, blockStateArray); + + writeSubregionIntoSchematic(nbt, subReg, blockList, bitArray); + } + } + + /** + * Writes the file data in to the IBlockstate array. + * + * @param blockList list with the different block types used in the schematic. + * @param bitArray bit array that holds the placement pattern. + */ + private void writeSubregionIntoSchematic(CompoundTag nbt, String subReg, BlockState[] blockList, LitematicaBitArray bitArray) { + Vec3i offsetSubregion = new Vec3i(getMinOfSubregion(nbt, subReg, "x"), getMinOfSubregion(nbt, subReg, "y"), getMinOfSubregion(nbt, subReg, "z")); + int index = 0; + for (int y = 0; y < this.y; y++) { + for (int z = 0; z < this.z; z++) { + for (int x = 0; x < this.x; x++) { + if (inSubregion(nbt, subReg, x, y, z)) { + this.states[x - (offsetMinCorner.getX() - offsetSubregion.getX())][z - (offsetMinCorner.getZ() - offsetSubregion.getZ())][y - (offsetMinCorner.getY() - offsetSubregion.getY())] = blockList[bitArray.getAt(index)]; + index++; + } + } + } + } + } + + /** + * @return offset from the schematic origin to the minimum Corner as a Vec3i. + */ + public Vec3i getOffsetMinCorner() { + return offsetMinCorner; + } + + /** + * @return x size of the schematic. + */ + public int getX() { + return this.x; + } + + /** + * @return y size of the schematic. + */ + public int getY() { + return this.y; + } + + /** + * @return z size of the schematic. + */ + public int getZ() { + return this.z; + } + + /** + * @param x position relative to the minimum corner of the schematic. + * @param y position relative to the minimum corner of the schematic. + * @param z position relative to the minimum corner of the schematic. + * @param blockState new blockstate of the block at this position. + */ + public void setDirect(int x, int y, int z, BlockState blockState) { + this.states[x][z][y] = blockState; + } + + /** + * @param rotated if the schematic is rotated by 90°. + * @return a copy of the schematic. + */ + public LitematicaSchematic getCopy(boolean rotated) { + return new LitematicaSchematic(nbt, rotated); + } + + /** + * @author maruohon + * Class from the Litematica mod by maruohon + * Usage under LGPLv3 with the permission of the author. + * ... + */ + private static class LitematicaBitArray { + /** + * The long array that is used to store the data for this BitArray. + */ + private final long[] longArray; + /** + * Number of bits a single entry takes up + */ + private final int bitsPerEntry; + /** + * The maximum value for a single entry. This also works as a bitmask for a single entry. + * For instance, if bitsPerEntry were 5, this value would be 31 (ie, {@code 0b00011111}). + */ + private final long maxEntryValue; + /** + * Number of entries in this array (not the length of the long array that internally backs this array) + */ + private final long arraySize; + + public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) { + Validate.inclusiveBetween(1L, 32L, bitsPerEntryIn); + this.arraySize = arraySizeIn; + this.bitsPerEntry = bitsPerEntryIn; + this.maxEntryValue = (1L << bitsPerEntryIn) - 1L; + + if (longArrayIn != null) { + this.longArray = longArrayIn; + } else { + this.longArray = new long[(int) (roundUp(arraySizeIn * (long) bitsPerEntryIn, 64L) / 64L)]; + } + } + + public static long roundUp(long number, long interval) { + int sign = 1; + if (interval == 0) { + return 0; + } else if (number == 0) { + return interval; + } else { + if (number < 0) { + sign = -1; + } + + long i = number % (interval * sign); + return i == 0 ? number : number + (interval * sign) - i; + } + } + + public int getAt(long index) { + Validate.inclusiveBetween(0L, this.arraySize - 1L, index); + long startOffset = index * (long) this.bitsPerEntry; + int startArrIndex = (int) (startOffset >> 6); // startOffset / 64 + int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6); + int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64 + + if (startArrIndex == endArrIndex) { + return (int) (this.longArray[startArrIndex] >>> startBitOffset & this.maxEntryValue); + } else { + int endOffset = 64 - startBitOffset; + return (int) ((this.longArray[startArrIndex] >>> startBitOffset | this.longArray[endArrIndex] << endOffset) & this.maxEntryValue); + } + } + + public long size() { + return this.arraySize; + } + } +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/schematic/format/defaults/MCEditSchematic.java b/src/main/java/baritone/utils/schematic/format/defaults/MCEditSchematic.java index 98203dbca..3018021a5 100644 --- a/src/main/java/baritone/utils/schematic/format/defaults/MCEditSchematic.java +++ b/src/main/java/baritone/utils/schematic/format/defaults/MCEditSchematic.java @@ -18,6 +18,7 @@ package baritone.utils.schematic.format.defaults; import baritone.utils.schematic.StaticSchematic; +import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/baritone/utils/schematic/format/defaults/SpongeSchematic.java b/src/main/java/baritone/utils/schematic/format/defaults/SpongeSchematic.java index c53ee781a..12163cbff 100644 --- a/src/main/java/baritone/utils/schematic/format/defaults/SpongeSchematic.java +++ b/src/main/java/baritone/utils/schematic/format/defaults/SpongeSchematic.java @@ -20,6 +20,12 @@ import baritone.utils.schematic.StaticSchematic; import baritone.utils.type.VarInt; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; @@ -27,12 +33,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * @author Brady * @since 12/27/2019 @@ -105,13 +105,19 @@ private SerializedBlockState(ResourceLocation resourceLocation, Map> BlockState setPropertyValue(BlockState state, Property property, String value) { - Optional parsed = property.getValue(value); - if (parsed.isPresent()) { - return state.setValue(property, parsed.get()); - } else { - throw new IllegalArgumentException("Invalid value for property " + property); + private BlockState deserialize() { + if (this.blockState == null) { + Block block = BuiltInRegistries.BLOCK.get(this.resourceLocation); + this.blockState = block.defaultBlockState(); + + this.properties.keySet().stream().sorted(String::compareTo).forEachOrdered(key -> { + Property property = block.getStateDefinition().getProperty(key); + if (property != null) { + this.blockState = setPropertyValue(this.blockState, property, this.properties.get(key)); + } + }); } + return this.blockState; } private static SerializedBlockState getFromString(String s) { @@ -140,19 +146,13 @@ private static SerializedBlockState getFromString(String s) { } } - private BlockState deserialize() { - if (this.blockState == null) { - Block block = BuiltInRegistries.BLOCK.get(this.resourceLocation); - this.blockState = block.defaultBlockState(); - - this.properties.keySet().stream().sorted(String::compareTo).forEachOrdered(key -> { - Property property = block.getStateDefinition().getProperty(key); - if (property != null) { - this.blockState = setPropertyValue(this.blockState, property, this.properties.get(key)); - } - }); + private static > BlockState setPropertyValue(BlockState state, Property property, String value) { + Optional parsed = property.getValue(value); + if (parsed.isPresent()) { + return state.setValue(property, parsed.get()); + } else { + throw new IllegalArgumentException("Invalid value for property " + property); } - return this.blockState; } } } diff --git a/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java b/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java new file mode 100644 index 000000000..3b05c23ef --- /dev/null +++ b/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java @@ -0,0 +1,215 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils.schematic.litematica; + +import baritone.utils.schematic.format.defaults.LitematicaSchematic; +import fi.dy.masa.litematica.Litematica; +import fi.dy.masa.litematica.data.DataManager; +import net.minecraft.core.Vec3i; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; + +import java.io.File; + +/** + * Helper class that provides access or processes data related to Litmatica schematics. + * + * @author rycbar + * @since 28.09.2022 + */ +public final class LitematicaHelper { + + /** + * @return if Litmatica is installed. + */ + public static boolean isLitematicaPresent() { + try { + Class.forName(Litematica.class.getName()); + return true; + } catch (ClassNotFoundException | NoClassDefFoundError ex) { + return false; + } + } + + /** + * @return if there are loaded schematics. + */ + public static boolean hasLoadedSchematic() { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().size() > 0; + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the name of the requested schematic. + */ + public static String getName(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getName(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the world coordinates of the schematic origin. This can but does not have to be the minimum corner. + */ + public static Vec3i getOrigin(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getOrigin(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return Filepath of the schematic file. + */ + public static File getSchematicFile(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getSchematicFile(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return rotation of the schematic placement. + */ + public static Rotation getRotation(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getRotation(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the mirroring of the schematic placement. + */ + public static Mirror getMirror(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getMirror(); + } + + /** + * @param schematic original schematic. + * @param i index of the Schematic in the schematic placement list. + * @return the minimum corner coordinates of the schematic, after the original schematic got rotated and mirrored. + */ + public static Vec3i getCorrectedOrigin(LitematicaSchematic schematic, int i) { + int x = LitematicaHelper.getOrigin(i).getX(); + int y = LitematicaHelper.getOrigin(i).getY(); + int z = LitematicaHelper.getOrigin(i).getZ(); + int mx = schematic.getOffsetMinCorner().getX(); + int my = schematic.getOffsetMinCorner().getY(); + int mz = schematic.getOffsetMinCorner().getZ(); + int sx = (schematic.getX() - 1) * -1; + int sz = (schematic.getZ() - 1) * -1; + + Vec3i correctedOrigin; + Mirror mirror = LitematicaHelper.getMirror(i); + Rotation rotation = LitematicaHelper.getRotation(i); + + //todo there has to be a better way to do this but i cant finde it atm + switch (mirror) { + case FRONT_BACK: + case LEFT_RIGHT: + switch ((mirror.ordinal() * 2 + rotation.ordinal()) % 4) { + case 1: + correctedOrigin = new Vec3i(x + (sz - mz), y + my, z + (sx - mx)); + break; + case 2: + correctedOrigin = new Vec3i(x + mx, y + my, z + (sz - mz)); + break; + case 3: + correctedOrigin = new Vec3i(x + mz, y + my, z + mx); + break; + default: + correctedOrigin = new Vec3i(x + (sx - mx), y + my, z + mz); + break; + } + break; + default: + switch (rotation) { + case CLOCKWISE_90: + correctedOrigin = new Vec3i(x + (sz - mz), y + my, z + mx); + break; + case CLOCKWISE_180: + correctedOrigin = new Vec3i(x + (sx - mx), y + my, z + (sz - mz)); + break; + case COUNTERCLOCKWISE_90: + correctedOrigin = new Vec3i(x + mz, y + my, z + (sx - mx)); + break; + default: + correctedOrigin = new Vec3i(x + mx, y + my, z + mz); + break; + } + } + return correctedOrigin; + } + + /** + * @param in the xyz offsets of the block relative to the schematic minimum corner. + * @param sizeX size of the schematic in the x-axis direction. + * @param sizeZ size of the schematic in the z-axis direction. + * @param mirror the mirroring of the schematic placement. + * @return the corresponding xyz coordinates after mirroring them according to the given mirroring. + */ + public static Vec3i doMirroring(Vec3i in, int sizeX, int sizeZ, Mirror mirror) { + int xOut = in.getX(); + int zOut = in.getZ(); + if (mirror == Mirror.LEFT_RIGHT) { + zOut = sizeZ - in.getZ(); + } else if (mirror == Mirror.FRONT_BACK) { + xOut = sizeX - in.getX(); + } + return new Vec3i(xOut, in.getY(), zOut); + } + + /** + * @param in the xyz offsets of the block relative to the schematic minimum corner. + * @param sizeX size of the schematic in the x-axis direction. + * @param sizeZ size of the schematic in the z-axis direction. + * @return the corresponding xyz coordinates after rotation them 90° clockwise. + */ + public static Vec3i rotate(Vec3i in, int sizeX, int sizeZ) { + return new Vec3i(sizeX - (sizeX - sizeZ) - in.getZ(), in.getY(), in.getX()); + } + + /** + * IDFK this just grew and it somehow works. If you understand how, pls tell me. + * + * @param schemIn give in the original schematic. + * @param i index of the Schematic in the schematic placement list. + * @return get it out rotated and mirrored. + */ + public static LitematicaSchematic blackMagicFuckery(LitematicaSchematic schemIn, int i) { + LitematicaSchematic tempSchem = schemIn.getCopy(LitematicaHelper.getRotation(i).ordinal() % 2 == 1); + for (int yCounter = 0; yCounter < schemIn.getY(); yCounter++) { + for (int zCounter = 0; zCounter < schemIn.getZ(); zCounter++) { + for (int xCounter = 0; xCounter < schemIn.getX(); xCounter++) { + Vec3i xyzHolder = new Vec3i(xCounter, yCounter, zCounter); + xyzHolder = LitematicaHelper.doMirroring(xyzHolder, schemIn.getX() - 1, schemIn.getZ() - 1, LitematicaHelper.getMirror(i)); + for (int turns = 0; turns < LitematicaHelper.getRotation(i).ordinal(); turns++) { + if ((turns % 2) == 0) { + xyzHolder = LitematicaHelper.rotate(xyzHolder, schemIn.getX() - 1, schemIn.getZ() - 1); + } else { + xyzHolder = LitematicaHelper.rotate(xyzHolder, schemIn.getZ() - 1, schemIn.getX() - 1); + } + } + BlockState state = schemIn.getDirect(xCounter, yCounter, zCounter); + try { + state = state.mirror(LitematicaHelper.getMirror(i)).rotate(LitematicaHelper.getRotation(i)); + } catch (NullPointerException e) { + //nothing to worry about it's just a hole in the schematic. + } + tempSchem.setDirect(xyzHolder.getX(), xyzHolder.getY(), xyzHolder.getZ(), state); + } + } + } + return tempSchem; + } +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java index 76319c33e..7905c74aa 100644 --- a/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicAdapter.java @@ -19,11 +19,10 @@ import baritone.api.schematic.IStaticSchematic; import com.github.lunatrius.schematica.client.world.SchematicWorld; +import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; -import java.util.List; - public final class SchematicAdapter implements IStaticSchematic { private final SchematicWorld schematic; diff --git a/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java index a13714a7d..35b11c8e1 100644 --- a/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java +++ b/src/main/java/baritone/utils/schematic/schematica/SchematicaHelper.java @@ -22,7 +22,6 @@ import com.github.lunatrius.schematica.proxy.ClientProxy; import net.minecraft.core.BlockPos; import net.minecraft.util.Tuple; - import java.util.Optional; public enum SchematicaHelper { diff --git a/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java index e3304d030..eb3c034e8 100644 --- a/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java +++ b/src/schematica_api/java/com/github/lunatrius/schematica/proxy/CommonProxy.java @@ -17,5 +17,4 @@ package com.github.lunatrius.schematica.proxy; -public abstract class CommonProxy { -} +public abstract class CommonProxy {} diff --git a/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java b/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java new file mode 100644 index 000000000..c10d5b38e --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java @@ -0,0 +1,21 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package fi.dy.masa.litematica; + +public class Litematica { +} \ No newline at end of file diff --git a/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java b/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java new file mode 100644 index 000000000..4941e7cf3 --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java @@ -0,0 +1,33 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package fi.dy.masa.litematica.data; + +import fi.dy.masa.litematica.schematic.placement.SchematicPlacementManager; + +public class DataManager { + public static final DataManager INSTANCE = new DataManager(); + private final SchematicPlacementManager schematicPlacementManager = new SchematicPlacementManager(); + + private static DataManager getInstance() { + return INSTANCE; + } + + public static SchematicPlacementManager getSchematicPlacementManager() { + return getInstance().schematicPlacementManager; + } +} \ No newline at end of file diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java new file mode 100644 index 000000000..a267553a9 --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.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 fi.dy.masa.litematica.schematic.placement; + +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + +public class SchematicPlacement extends SchematicPlacementUnloaded { + private Rotation rotation; + private Mirror mirror; + + public Rotation getRotation() { + return this.rotation; + } + + public Mirror getMirror() { + return this.mirror; + } + +} \ No newline at end of file diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java new file mode 100644 index 000000000..ab60e27fa --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java @@ -0,0 +1,31 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package fi.dy.masa.litematica.schematic.placement; + +import java.util.ArrayList; +import java.util.List; + +public class SchematicPlacementManager { + private final List schematicPlacements = new ArrayList<>(); + + //in case of a java.lang.NoSuchMethodError try change the name of this method to getAllSchematicPlacements() + //there are inconsistencies in the litematica mod about the naming of this method + public List getAllSchematicsPlacements() { + return schematicPlacements; + } +} \ No newline at end of file diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java new file mode 100644 index 000000000..9e705dc7b --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java @@ -0,0 +1,43 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package fi.dy.masa.litematica.schematic.placement; + +import net.minecraft.core.BlockPos; + +import javax.annotation.Nullable; +import java.io.File; + +public class SchematicPlacementUnloaded { + protected String name = "?"; + @Nullable + protected File schematicFile; + protected BlockPos origin = BlockPos.ZERO; + + public String getName() { + return this.name; + } + + @Nullable + public File getSchematicFile() { + return this.schematicFile; + } + + public BlockPos getOrigin() { + return this.origin; + } +} \ No newline at end of file diff --git a/src/test/java/baritone/pathing/goals/GoalGetToBlockTest.java b/src/test/java/baritone/pathing/goals/GoalGetToBlockTest.java index 2f3521297..64a6a6fb7 100644 --- a/src/test/java/baritone/pathing/goals/GoalGetToBlockTest.java +++ b/src/test/java/baritone/pathing/goals/GoalGetToBlockTest.java @@ -18,12 +18,12 @@ package baritone.pathing.goals; import baritone.api.pathing.goals.GoalGetToBlock; -import net.minecraft.core.BlockPos; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import net.minecraft.core.BlockPos; import static org.junit.Assert.assertTrue; diff --git a/tweaker/build.gradle b/tweaker/build.gradle index 5696fee5a..9f053fb88 100644 --- a/tweaker/build.gradle +++ b/tweaker/build.gradle @@ -17,14 +17,13 @@ import baritone.gradle.task.CreateDistTask import baritone.gradle.task.ProguardTask - //import baritone.gradle.task.TweakerJsonAssembler plugins { - id "com.github.johnrengelman.shadow" version "7.0.0" + id "com.github.johnrengelman.shadow" version "8.0.0" } -minecraft { +unimined.minecraft { runs.client = { mainClass = "net.minecraft.launchwrapper.Launch" args.addAll(["--tweakClass", "baritone.launch.BaritoneTweaker"]) @@ -48,7 +47,6 @@ dependencies { implementation "org.ow2.asm:asm-util:9.3" implementation "org.ow2.asm:asm-analysis:9.3" - implementation 'com.github.ImpactDevelopment:SimpleTweaker:1.2' implementation('net.minecraft:launchwrapper:of-2.3') { exclude module: 'lwjgl' @@ -68,18 +66,18 @@ dependencies { shadowJar { configurations = [project.configurations.shadowCommon] - classifier "dev-shadow" + archiveClassifier.set "dev-shadow" } remapJar { inputFile.set shadowJar.archiveFile dependsOn shadowJar - classifier null + archiveClassifier.set null } jar { - classifier "dev" + archiveClassifier.set "dev" preserveFileTimestamps = false reproducibleFileOrder = true @@ -98,7 +96,6 @@ jar { task proguard(type: ProguardTask) { url 'https://github.com/Guardsquare/proguard/releases/download/v7.2.1/proguard-7.2.1.zip' extract 'proguard-7.2.1/lib/proguard.jar' - compType "OFFICIAL" } task createDist(type: CreateDistTask, dependsOn: proguard) diff --git a/tweaker/src/main/java/baritone/launch/BaritoneTweaker.java b/tweaker/src/main/java/baritone/launch/BaritoneTweaker.java new file mode 100644 index 000000000..02e269506 --- /dev/null +++ b/tweaker/src/main/java/baritone/launch/BaritoneTweaker.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.launch; + +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.util.List; + +/** + * @author Brady + * @since 7/31/2018 + */ +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(obfuscation); + + Mixins.addConfiguration("mixins.baritone-meteor.json"); + } +}