diff --git a/.editorconfig b/.editorconfig index 7358e5747..500d22627 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,34 +1,44 @@ [*] -charset=utf-8 -end_of_line=lf -insert_final_newline=true -indent_style=space -indent_size=4 +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 ij_any_block_comment_add_space = false ij_any_block_comment_at_first_column = false ij_any_line_comment_at_first_column = false ij_any_line_comment_add_space = true [*.tiny] -indent_style=tab +indent_style = tab [*.bat] -end_of_line=crlf +end_of_line = crlf [*.yml] -indent_size=2 +indent_size = 2 [*.patch] -trim_trailing_whitespace=false +trim_trailing_whitespace = false [*.java] ij_continuation_indent_size = 4 ij_java_class_count_to_use_import_on_demand = 999999 ij_java_insert_inner_class_imports = false ij_java_names_count_to_use_import_on_demand = 999999 -ij_java_imports_layout = *,|,$* +ij_java_imports_layout = *, |, $* ij_java_generate_final_locals = true ij_java_generate_final_parameters = true - -[test-plugin/**/*.java] +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_method_parameters_right_paren_on_new_line = true ij_java_use_fq_class_names = false +ij_java_class_names_in_javadoc = 1 + +[purpur-server/src/minecraft/java/**/*.java] +ij_java_use_fq_class_names = true + +[purpur-server/src/minecraft/resources/data/**/*.json] +indent_size = 2 + +[paper-api/src/generated/java/**/*.java] +ij_java_imports_layout = $*,|,* diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9399b666c..f2c695646 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,22 +11,22 @@ jobs: runs-on: ubuntu-latest if: "!contains(github.event.commits[0].message, '[ci-skip]')" steps: - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v3.6.0 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: temurin - java-version: 17 + java-version: 21 cache: 'gradle' + - uses: gradle/actions/setup-gradle@v4 - name: Configure Git run: git config --global user.email "no-reply@github.com" && git config --global user.name "Github Actions" - name: Apply Patches - run: ./gradlew applyPatches --no-daemon --stacktrace + run: ./gradlew applyAllPatches --stacktrace - name: Build - run: ./gradlew build --no-daemon --stacktrace + run: ./gradlew build --stacktrace - name: Rebuild on Failure if: ${{ failure() }} run: | - ./gradlew clean cleanCache - ./gradlew applyPatches --no-daemon --stacktrace - ./gradlew build --no-daemon --stacktrace + ./gradlew clean cleanCache --refresh-dependencies + ./gradlew applyAllPatches --stacktrace + ./gradlew build --stacktrace diff --git a/.gitignore b/.gitignore index dcb2438fd..7b23ae323 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ hs_err_pid* # Intellij /.idea/* !/.idea/runConfigurations +!/.idea/icon.svg *.iml *.ipr *.iws @@ -50,10 +51,12 @@ manifest.mf *~ # other stuff -run/ +/run -build-data/ -Purpur-API -Purpur-MojangAPI -Purpur-Server +/purpur-server/build.gradle.kts +/purpur-server/src/minecraft +/paper-server +/purpur-api/build.gradle.kts +/paper-api *.jar +test-plugin.settings.gradle.kts diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 000000000..5cd62e1fe --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml b/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml index ef359abab..9d466168e 100644 --- a/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml +++ b/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml @@ -1,11 +1,11 @@ - \ No newline at end of file diff --git a/.idea/runConfigurations/Upstream_Paper__applyPatches_.xml b/.idea/runConfigurations/Upstream_Paper__applyAllPatches_.xml similarity index 78% rename from .idea/runConfigurations/Upstream_Paper__applyPatches_.xml rename to .idea/runConfigurations/Upstream_Paper__applyAllPatches_.xml index e3255f71d..ef348a313 100644 --- a/.idea/runConfigurations/Upstream_Paper__applyPatches_.xml +++ b/.idea/runConfigurations/Upstream_Paper__applyAllPatches_.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/.idea/runConfigurations/Upstream_Paper__build_.xml b/.idea/runConfigurations/Upstream_Paper__build_.xml index 195cdef25..b3491f8c0 100644 --- a/.idea/runConfigurations/Upstream_Paper__build_.xml +++ b/.idea/runConfigurations/Upstream_Paper__build_.xml @@ -20,7 +20,7 @@ false false - - + \ No newline at end of file diff --git a/.idea/runConfigurations/Upstream_Paper__createReobfPaperclipJar_.xml b/.idea/runConfigurations/Upstream_Paper__createMojmapPaperclipJar_.xml similarity index 82% rename from .idea/runConfigurations/Upstream_Paper__createReobfPaperclipJar_.xml rename to .idea/runConfigurations/Upstream_Paper__createMojmapPaperclipJar_.xml index 8911b6115..4145189fc 100644 --- a/.idea/runConfigurations/Upstream_Paper__createReobfPaperclipJar_.xml +++ b/.idea/runConfigurations/Upstream_Paper__createMojmapPaperclipJar_.xml @@ -1,5 +1,5 @@ - + - + \ No newline at end of file diff --git a/.idea/runConfigurations/Upstream_Paper__rebuildAllServerPatches_.xml b/.idea/runConfigurations/Upstream_Paper__rebuildAllServerPatches_.xml new file mode 100644 index 000000000..7e0110e69 --- /dev/null +++ b/.idea/runConfigurations/Upstream_Paper__rebuildAllServerPatches_.xml @@ -0,0 +1,26 @@ + + + + + + + true + true + false + false + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Upstream_Paper__rebuildPatches_.xml b/.idea/runConfigurations/Upstream_Paper__rebuildPaperApiPatches_.xml similarity index 78% rename from .idea/runConfigurations/Upstream_Paper__rebuildPatches_.xml rename to .idea/runConfigurations/Upstream_Paper__rebuildPaperApiPatches_.xml index 0188aeb0f..f8c41e541 100644 --- a/.idea/runConfigurations/Upstream_Paper__rebuildPatches_.xml +++ b/.idea/runConfigurations/Upstream_Paper__rebuildPaperApiPatches_.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 118d9bc9f..28a81a540 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,35 +4,29 @@ Purpur is happy you're willing to contribute to our projects. We are usually very lenient with all submitted PRs, but there are still some guidelines you can follow to make the approval process go more smoothly. -## Use a Personal Fork and not Organization +## Use a Personal Fork and not an Organization Purpur will routinely modify your PR, whether it's a quick rebase or to take care of any minor nitpicks we might have. Often, it's better for us to solve these -problems for you than make you go back and forth trying to fix it yourself. +problems for you than make you go back and forth trying to fix them yourself. Unfortunately, if you use an organization for your PR, it prevents Purpur from -modifying it. This requires us to manually merge your PR, resulting in us -closing the PR instead of marking it as merged. - -We much prefer to have PRs show as merged, so please do not use repositories -on organizations for PRs. - -See for more information on the -issue. +modifying it. To avoid this, please do not use repositories on organizations +for PRs. ## Requirements -To get started with PRing changes, you'll need the following software, most of +To get started with making changes, you'll need the following software, most of which can be obtained in (most) package managers such as `apt` (Debian / Ubuntu; you will most likely use this for WSL), `homebrew` (macOS / Linux), and more: - `git` (package `git` everywhere); -- A Java 16 or later JDK (packages vary, use Google/DuckDuckGo/etc.). +- A Java 21 or later JDK (packages vary, use Google/DuckDuckGo/etc.). - [Adoptium](https://adoptium.net/) has builds for most operating systems. - - Purpur requires JDK 16 to build, however makes use of Gradle's + - Purpur requires JDK 21 to build, however, makes use of Gradle's [Toolchains](https://docs.gradle.org/current/userguide/toolchains.html) - feature to allow building with only JRE 8 or later installed. (Gradle will - automatically provision JDK 16 for compilation if it cannot find an existing + feature to allow building with only JRE 11 or later installed. (Gradle will + automatically provision JDK 21 for compilation if it cannot find an existing install). If you're on Windows, check @@ -42,77 +36,94 @@ If you're compiling with Docker, you can use Adoptium's [`eclipse-temurin`](https://hub.docker.com/_/eclipse-temurin/) images like so: ```console -# docker run -it -v "$(pwd)":/data --rm eclipse-temurin:16.0.2_7-jdk bash +# docker run -it -v "$(pwd)":/data --rm eclipse-temurin:21.0.5_11-jdk bash Pulling image... root@abcdefg1234:/# javac -version -javac 16.0.2 +javac 21.0.5 ``` ## Understanding Patches -Purpur is mostly patches and extensions to Paper/Spigot. These patches/extensions are -split into different directories which target certain parts of the code. These -directories are: +Unlike the Purpur API and its implementation, modifications to Paper and Minecraft source files +are done through patches. These patches/extensions are split into three different sets of two +categories, which are formatted like so: -- `Purpur-API` - Modifications to `Paper-API`; -- `Purpur-Server` - Modifications to `Paper-Server`. +Under `purpur-api`: -Because the entire structure is based on patches and git, a basic understanding +- `paper-patches` (applies to the `paper-server/` git repo) + - `sources`: Per-file patches to Paper API classes; + - `features`: Larger feature patches that modify multiple Paper API classes. + +Under `purpur-server`: + +- `minecraft-patches` (applies to the `purpur-server//` git repo) + - `sources`: Per-file patches to Minecraft classes; + - `resources`: Per-file patches to Minecraft data files; + - `features`: Larger feature patches that modify multiple classes. +- `paper-patches` + - `sources`: Per-file patches to Paper Server classes; + - `features`: Larger feature patches that modify multiple Paper Server classes. + +Because this entire structure is based on patches and git, a basic understanding of how to use git is required. A basic tutorial can be found here: . Assuming you have already forked the repository: 1. Clone your fork to your local machine; -2. Type `./gradlew applyPatches` in a terminal to apply the changes from upstream. - On Windows, leave out the `./` at the beginning for all `gradlew` commands; -3. cd into `Purpur-Server` for server changes, and `Purpur-API` for API changes. - +2. Type `./gradlew applyAllPatches` in a terminal to apply the patches to both paper and minecraft classes. + On Windows, remove `./` from the beginning of `gradlew` commands; +3. cd into `purpur-server` for server changes, `purpur-api` for API changes, + `paper-api` for Paper API changes, and `paper-server` for Paper Server changes. -`Purpur-Server` and `Purpur-API` aren't git repositories in the traditional sense: +`purpur-server/src/minecraft/java` and `purpur-server/src/minecraft/java/resources` are not git repositories in the traditional sense. +Its initial commits are the decompiled and deobfuscated Minecraft source and resource files. The per-file +patches are applied on top of these files as a single, large commit, which is then followed +by the individual feature-patch commits. `paper-api/` and `paper-server/` +follow the same concept; each paper "project" has its own git repository that also includes it's own feature and per-file patches. -- `base` points to the unmodified source before Purpur patches have been applied. -- Each commit after `base` is a patch. +## Understanding the Gradle Tasks -## Adding Patches +It can be overwhelming when you look over all the available gradle tasks, but it's pretty straightforward. +Each "project" that includes a local git repository has the following available gradle tasks attached to it: -Adding patches to Purpur is very simple: +- `./gradlew apply[project]FeaturePatches` - Applies [project] feature patches +- `./gradlew apply[project]FilePatches` - Applies [project] file patches +- `./gradlew apply[project]Patches` - Applies all [project] patches +- `./gradlew fixup[project]FilePatches` - Puts the currently tracked source changes into the [project] file patches commit +- `./gradlew rebuild[project]FeaturePatches` - Rebuilds [project] feature patches +- `./gradlew rebuild[project]FilePatches` - Rebuilds [project] file patches +- `./gradlew rebuild[project]Patches` - Rebuilds all [project] patches -1. Modify `Purpur-Server` and/or `Purpur-API` with the appropriate changes; -1. Type `git add .` inside these directories to add your changes; -1. Run `git commit` with the desired patch message; -1. Run `./gradlew rebuildPatches` in the main directory to convert your commit into a new - patch; -1. PR the generated patch file(s) back to this repository. +Some additional useful tasks are listed below: -Your commit will be converted into a patch that you can then PR into Purpur. +- `./gradlew applyAllPatches` - Applies all patches defined in the paperweight-patcher project and the server project. (equivalent to running `applyPaperPatches` and then `applyAllServerPatches` in a second Gradle invocation) +- `./gradlew applyAllServerFeaturePatches` - Applies all Minecraft and upstream server feature patches (equivalent to `applyMinecraftFeaturePatches applyServerFeaturePatches`) +- `./gradlew applyAllServerFilePatches` - Applies all Minecraft and upstream server file patches (equivalent to `applyMinecraftFilePatches applyServerFilePatches`) +- `./gradlew applyAllServerPatches` - Applies all Minecraft and upstream server patches (equivalent to `applyMinecraftPatches applyServerPatches`) -> ❗ Please note that if you have some specific implementation detail you'd like -> to document, you should do so in the patch message *or* in comments. +- `./gradlew rebuildPaperSingleFilePatches` - Fixups and rebuilds all paper single-file patches. This is how you'd make changes to the `build.gradle.kts` files located under `purpur-api` and `purpur-server` -## Modifying Patches +## Modifying (per-file) patches -Modifying previous patches is a bit more complex: +This is generally what you need to do when editing files. Updating our +per-file patches is as easy as: +1. Making your changes; +2. Running `./gradlew fixup[project]FilePatches` in the root directory; +3. If nothing went wrong, rebuilding patches with `./gradlew rebuild[project]FilePatches`; -### Method 1 +### Resolving rebase conflicts +If you run into conflicts while running `fixup[project]FilePatches`, you need to go a more +manual route: This method works by temporarily resetting your `HEAD` to the desired commit to edit it using `git rebase`. -> ❗ While in the middle of an edit, you will not be able to compile unless you -> *also* reset the opposing module(s) to a related commit. In the API's case, -> you must reset the Server, and reset the API if you're editing the Server. -> Note also that either module _may_ not compile when doing so. This is not -> ideal nor intentional, but it happens. Feel free to fix this in a PR to us! - -1. If you have changes you are working on, type `git stash` to store them for +0. If you have changes you are working on, type `git stash` to store them for later; - You can type `git stash pop` to get them back at any point. -1. Type `git rebase -i base`; +1. cd into the relevant local git repo and run `git rebase -i base`; - It should show something like [this](https://gist.github.com/zachbr/21e92993cb99f62ffd7905d7b02f3159) in the text editor you get. @@ -120,31 +131,47 @@ edit it using `git rebase`. If you don't know how to use `vim` and don't want to learn, enter `:q!` and press enter. Before redoing this step, do `export EDITOR=nano` for an easier editor to use. -1. Replace `pick` with `edit` for the commit/patch you want to modify, and +1. Replace `pick` with `edit` for the commit/patch you want to modify (in this + case the very first commit, `purpur File Patches`), and "save" the changes; - - Only do this for **one** commit at a time. 1. Make the changes you want to make to the patch; -1. Type `git add .` to add your changes; -1. Type `git commit --amend` to commit; - - **Make sure to add `--amend`** or else a new patch will be created. - - You can also modify the commit message and author here. -1. Type `git rebase --continue` to finish rebasing; -1. Type `./gradlew rebuildPatches` in the root directory; - - This will modify the appropriate patches based on your commits. -1. PR your modified patch file(s) back to this repository. +1. Run `git add .` to add your changes; +1. Run `git commit --amend` to commit; +1. Run `git rebase --continue` to finish rebasing; +1. Run `./gradlew rebuild[project]FilePatches` in the root directory; -### Method 2 - Fixup commits +## Adding larger feature patches -If you are simply editing a more recent commit or your change is small, simply -making the change at HEAD and then moving the commit after you have tested it -may be easier. +Feature patches are exclusively used for large-scale changes that are hard to +track and maintain and that can be optionally dropped, such as the more involved +optimizations we have. This makes it easier to update Purpur during Minecraft updates, +since we can temporarily drop these patches and reapply them later. -This method has the benefit of being able to compile to test your change without -messing with your HEADs. +There is only a very small chance that you will have to use this system, but adding +such patches is very simple: + +1. Modify the relevant local git repo with the appropriate changes; +1. Run `git add .` inside that directory to add your changes; +1. Run `git commit` with the desired patch message; +1. Run `./gradlew rebuild[project]FeaturePatches` in the root directory. + +Your commit will be converted into a patch that you can then PR into Purpur. + +> ❗ Please note that if you have some specific implementation detail you'd like +> to document, you should do so in the patch message *or* in comments. + +## Modifying larger feature patches + +One way of modifying feature patches is to reset to the patch commit and follow +the instructions from the [rebase section](#resolving-rebase-conflicts). If you +are sure there won't be any conflicts from later patches, you can also use the +fixup method. + +### Fixup method #### Manual method -1. Make your change while at HEAD; +1. Make your changes; 1. Make a temporary commit. You don't need to make a message for this; 1. Type `git rebase -i base`, move (cut) your temporary commit and move it under the line of the patch you wish to modify; @@ -153,42 +180,41 @@ messing with your HEADs. message. 1. `s`/`squash`: Merge your changes into the patch and use your commit message and subject. -1. Type `./gradlew rebuildPatches` in the root directory; +1. Run `./gradlew rebuild[project]Patches` in the root directory; - This will modify the appropriate patches based on your commits. -1. PR your modified patch file(s) back to this repository. #### Automatic method -1. Make your change while at HEAD; -1. Make a fixup commit. `git commit -a --fixup `; +1. Make your changes; +1. Make a fixup commit: `git commit -a --fixup `; + - If you want to modify a per-file patch, use `git commit -a --fixup file` - You can also use `--squash` instead of `--fixup` if you want the commit message to also be changed. - You can get the hash by looking at `git log` or `git blame`; your IDE can assist you too. - Alternatively, if you only know the name of the patch, you can do `git commit -a --fixup "Subject of Patch name"`. -1. Rebase with autosquash: `git rebase --autosquash -i base`. +1. Rebase with autosquash: `git rebase -i --autosquash base`. This will automatically move your fixup commit to the right place, and you just need to "save" the changes. -1. Type `./gradlew rebuildPatches` in the root directory; - - This will modify the appropriate patches based on your commits. -1. PR your modified patch file(s) back to this repository. +1. Run `./gradlew rebuild[project]Patches` in the root directory. This will modify the + appropriate patches based on your commits. ## Rebasing PRs -Steps to rebase a PR to include the latest changes from `master`. -These steps assume the `origin` remote is your fork of this repository and `upstream` is the official Purpur repository. +Steps to rebase a PR to include the latest changes from `main`. +These steps assume the `origin` remote is your fork of this repository and `upstream` is the official PurpurMC repository. -1. Pull the latest changes from upstreams master: `git checkout master && git pull upstream master`. -1. Checkout feature/fix branch and rebase on master: `git checkout patch-branch && git rebase master`. +1. Fetch the latest changes from upstream's main: `git fetch upstream`. +1. Checkout your feature/fix branch and rebase on main: `git switch patch-branch && git rebase upstream/main`. 1. Apply updated patches: `./gradlew applyPatches`. 1. If there are conflicts, fix them. -1. If your PR creates new patches instead of modifying exist ones, in both the `Purpur-Server` and `Purpur-API` directories, ensure your newly-created patch is the last commit by either: +1. If your PR creates new feature patches instead of modifying existing ones, ensure your newly-created patch is the last commit by either: * Renaming the patch file with a large 4-digit number in front (e.g. 9999-Patch-to-add-some-new-stuff.patch), and re-applying patches. * Running `git rebase --interactive base` and moving the commits to the end. 1. Rebuild patches: `./gradlew rebuildPatches`. 1. Commit modified patches. -1. Force push changes: `git push --force`. +1. Force push changes: `git push --force`. Make sure you're not deleting any of your commits or changes here! ## PR Policy @@ -203,41 +229,121 @@ when making and submitting changes. ## Formatting -All modifications to non-Purpur files should be marked. +All modifications to Minecraft files and Paper files should be marked. For historical reasons, +API and API-implementation contain a lot of these too, but they are no longer +required. -- Multi-line changes start with `// Purpur start` and end with `// Purpur end`; -- You can put a comment with an explanation if it isn't obvious, like this: - `// Purpur start - reason`. +- You need to add a comment with a short and identifiable description of the patch: + `// Purpur start - ` - The comments should generally be about the reason the change was made, what it was before, or what the change is. - - Multi-line messages should start with `// Purpur start` and use `/* Multi - line message here */` for the message itself. -- One-line changes should have `// Purpur` or `// Purpur - reason`. + - After the general commit description, you can add additional information either + after a `;` or in the next line. +- Multi-line changes start with `// Purpur start - ` and end + with `// Purpur end - `. +- One-line changes should have `// Purpur - ` at the end of the line. + +> [!NOTE] +> These comments are incredibly important to be able to keep track of changes +> across files and to remember what they are for, even a decade into the future. Here's an example of how to mark changes by Purpur: ```java -entity.getWorld().dontbeStupid(); // Purpur - was beStupid() which is bad +entity.getWorld().dontBeStupid(); // Purpur - Move away from beStupid() entity.getFriends().forEach(Entity::explode); -entity.a(); -entity.b(); -// Purpur start - use plugin-set spawn +entity.updateFriends(); + +// Purpur start - Use plugin-set spawn // entity.getWorld().explode(entity.getWorld().getSpawn()); -Location spawnLocation = ((CraftWorld)entity.getWorld()).getSpawnLocation(); +Location spawnLocation = ((CraftWorld) entity.getWorld()).getSpawnLocation(); entity.getWorld().explode(new BlockPosition(spawnLocation.getX(), spawnLocation.getY(), spawnLocation.getZ())); -// Purpur end +// Purpur end - Use plugin-set spawn ``` -We generally follow usual Java style (aka. Oracle style), or what is programmed +We generally follow the usual Java style (aka. Oracle style), or what is programmed into most IDEs and formatters by default. There are a few notes, however: - It is fine to go over 80 lines as long as it doesn't hurt readability. There are exceptions, especially in Spigot-related files - When in doubt or the code around your change is in a clearly different style, use the same style as the surrounding code. +- Usage of the `var` keyword is discouraged, as it makes reading patch files a + lot harder and can lead to confusion during updates due to changed return types. + The only exception to this is if a line would otherwise be way too long/filled with + hard to parse generics in a case where the base type itself is already obvious. +### Imports +When adding new imports to a Minecraft or Paper class, use the fully qualified class name +instead of adding a new import to the top of the file. If you are using a type a significant number of times, you +can add an import with a comment. However, if it's only used a couple of times, the FQN is preferred to prevent future +patch conflicts in the import section of the file. + + +```java +import net.minecraft.server.MinecraftServer; +// don't add import here, use FQN like below + +public class SomeVanillaClass { + public final org.bukkit.Location newLocation; // Purpur - add location +} +``` + +### Nullability annotations + +We are in the process of switching nullability annotation libraries, so you might need to use one or the other: + +**For classes we add**: Fields, method parameters and return types that are nullable should be marked via the +`@Nullable` annotation from `org.jspecify.annotations`. Whenever you create a new class, add `@NullMarked`, meaning types +are assumed to be non-null by default. For less obvious placing such as on generics or arrays, see the [JSpecify docs](https://jspecify.dev/docs/user-guide/). + +**For other classes**: Keep using both `@Nullable` and `@NotNull` from `org.jetbrains.annotations`. These +will be replaced later. + +### API checks + +When performing API-related checks where an exception needs to be thrown under specific conditions, you should use the `Preconditions` class. + +#### Checking Method Arguments +To validate method arguments, use `Preconditions#checkArgument`. This will throw an `IllegalArgumentException` if the condition is not met. +> Don't use Preconditions#checkNotNull, as it throws a NullPointerException, which makes it harder to determine whether the error was caused by an internal issue or invalid arguments. + +ex: +```java +@Override +public void sendMessage(Player player, Component message) { + Preconditions.checkArgument(player != null, "player cannot be null"); + Preconditions.checkArgument(player.isOnline(), "player %s must be online", player.getName()); + Preconditions.checkArgument(message != null, "message cannot be null"); + // rest of code +} +``` + +#### Checking Object State +To validate the state of an object inside a method, use `Preconditions#checkState`. This will throw an `IllegalStateException` if the condition is not met. +ex: +```java +private Player player; + +@Override +public void sendMessage(Component message) { + Preconditions.checkArgument(message != null, "message cannot be null"); + Preconditions.checkState(this.player != null, "player cannot be null"); + Preconditions.checkState(this.player.isOnline(), "player %s must be online", this.player.getName()); + // rest of code +} +``` + +## Access Transformers +Sometimes, Vanilla code already contains a field, method, or type you want to access +but the visibility is too low (e.g. a private field in an entity class). Purpur can use access transformers +to change the visibility or remove the final modifier from fields, methods, and classes. Inside the `build-data/purpur.at` +file, you can add ATs that are applied when you `./gradlew applyPatches`. You can read about the format of ATs +[here](https://mcforge.readthedocs.io/en/latest/advanced/accesstransformers/#access-modifiers). + + ## Obfuscation Helpers @@ -387,38 +494,22 @@ If you use Maven to build your plugin: If you use Windows and don't usually build using WSL, you might not need to do this. +## Tips and Tricks + +### IntelliJ IDEA + +- Under `Settings > Appearance & Behavior > System Settings`, disable + `Sync external changes: Periodically when the IDE is inactive (experimental)`. + When disabled, the IDE will not attempt to reindex files while patches are applying + unless you interact with the IDE during the process. This avoids severe slowdowns and freezes. +- Under `Settings > Appearance & Behavior > System Settings`, you may also want to + disable `Reopen projects on startup` to avoid freeze loops. + ## Frequently Asked Questions -### I can't find the NMS file I need! - -By default, Purpur (and upstream) only import files we make changes to. If you -would like to make changes to a file that isn't present in `Purpur-Server`'s -source directory, you just need to add it to our import script ran during the -patching process. - -1. Save (rebuild) any patches you are in the middle of working on! Their - progress will be lost if you do not; -1. Identify the name(s) of the file(s) you want to import. - - A complete list of all possible file names can be found at - `./Purpur-Server/.gradle/caches/paperweight/mc-dev-sources/net/minecraft/`. You might find - [MiniMappingViewer] useful if you need to translate between Mojang and Spigot mapped names. -1. Open the file at `./build-data/dev-imports.txt` and add the name of your file to - the script. Follow the instructions there; -1. Re-patch the server `./gradlew applyPatches`; -1. Edit away! - -> ❗ This change is temporary! **DO NOT COMMIT CHANGES TO THIS FILE!** -> Once you have made your changes to the new file, and rebuilt patches, you may -> undo your changes to `dev-imports.txt`. - -Any file modified in a patch file gets automatically imported, so you only need -this temporarily to import it to create the first patch. - -To undo your changes to the file, type `git checkout build-data/dev-imports.txt`. - ### My commit doesn't need a build, what do I do? -Well, quite simple: You add `[ci skip]` to the start of your commit subject. +Quite simple: You add `[ci skip]` to the start of your commit subject. This case most often applies to changes to files like `README.md`, this very file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth. @@ -426,16 +517,16 @@ file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth. ### Patching and building is *really* slow, what can I do? This only applies if you're running Windows. If you're running a prior Windows -release, either update to Windows 10 or move to macOS/Linux/BSD. +release, either update to Windows 10/11 or move to macOS/Linux/BSD. -In order to speed up patching process on Windows, it's recommended you get WSL. -2. This is available in Windows 10 v2004, build 19041 or higher. (You can check - your version by running `winver` in the run window (Windows key + R)). If you're - out of date, update your system with the - [Windows Update Assistant](https://www.microsoft.com/en-us/software-download/windows10). +In order to speed up patching process on Windows, it's recommended you get WSL 2. +This is available in Windows 10 v2004, build 19041 or higher. (You can check +your version by running `winver` in the run window (Windows key + R)). If you're +using an out of date version of Windows 10, update your system with the +[Windows 10 Update Assistant](https://www.microsoft.com/en-us/software-download/windows10) or [Windows 11 Update Assistant](https://www.microsoft.com/en-us/software-download/windows11). To set up WSL 2, follow the information here: - + You will most likely want to use the Ubuntu apps. Once it's set up, install the required tools with `sudo apt-get update && sudo apt-get install $TOOL_NAMES @@ -445,6 +536,4 @@ everything like usual. > ❗ Do not use the `/mnt/` directory in WSL! Instead, mount the WSL directories > in Windows like described here: -> - -[MiniMappingViewer]: https://minidigger.github.io/MiniMappingViewer/ +> diff --git a/LICENSE b/LICENSE index 424cc2f1c..4cffc22ed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 PurpurMC +Copyright (c) 2019-2024 PurpurMC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ddc75549f..7b36f46ab 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Purpur [![MIT License](https://img.shields.io/github/license/PurpurMC/Purpur?&logo=github)](LICENSE) -[![Build Status](https://img.shields.io/github/actions/workflow/status/PurpurMC/Purpur/build.yml?branch=ver%2F1.20.1&event=push&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bxSIVh1YQcYhQnSyIiuimVShChVArtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE0clJ0UVK/F9SaBHjwXE/3t173L0D/PUyU82OMUDVLCOViAuZ7KrQ9YogwujDEGYkZupzopiE5/i6h4+vdzGe5X3uz9Gj5EwG+ATiWaYbFvEG8dSmpXPeJ46woqQQnxOPGnRB4keuyy6/cS447OeZESOdmieOEAuFNpbbmBUNlXiSOKqoGuX7My4rnLc4q+Uqa96TvzCU01aWuU5zEAksYgkiBMioooQyLMRo1UgxkaL9uId/wPGL5JLJVQIjxwIqUCE5fvA/+N2tmZ8Yd5NCcaDzxbY/hoGuXaBRs+3vY9tunACBZ+BKa/krdWD6k/RaS4seAb3bwMV1S5P3gMsdoP9JlwzJkQI0/fk88H5G35QFwrdA95rbW3Mfpw9AmrpK3gAHh8BIgbLXPd4dbO/t3zPN/n4Ax9dyyerighsAAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfmCBMWBjFhOpnxAAACyklEQVQ4y32TXWgdZRCGn8k5Z3e/bzcJRFuoDRqIIPGniaSl1qgXKYhiLUgleFESW1qQ4g94oVKQJmga0ILRBuvfheJFIbY3SmzRFsWgQrTQFlRoKSVXgcMxJiS7++1ukvHiNKGU6FzNDO8MMw8zwn9Yz5c6anKeChxiMk599aq8sZ5Obk3sGNd7G5aZCHJ+Oj8gAwD9I3rSJmyvzLHz+HG5frO+vOaNa3NXiUkKQj9hQ1jQgWrlyCCV6gIdtmBjoJwbPqgL+R08PDQkCUADwD0TerqzxFTJY++vz0l7c0q/F7P5+aNUqyvM2CU2RAV7hsek3XPs3/QHF7/YrSfrDVQrxrErdESNNTYByAp3NmWUTYaGGSuho+QltAGYRdqilChMeKbO4Ijang5+CVJmbcr20PG3n9NiY6ZOvCW9ACMHdDLIeDDIqTWmtAQJV6OMyhOTsqWBQQo/hvP7pFcWeDxwtAQxGqbEq3isI7EOjRy3NyuPPHtOur0UXWMQZXgAX78uP3sJpTBmyHfsGDmotdF+rQWObq/gnSCm9PQ3chnA3qgRgL5jmjXlTM/n7Ny8yJXRY2IA3tur0yaHF8blLoAzj2kaNNBpZvk2iGntuiZBHWLOtXJC3hbzV5SxtDq6yZmzCXOrsRezHNa4YBzlKGN6bQXryD96W+4PU14zGbw/oLMf79EPbAI2g7M9+smP3fqPKVAT81LnFWnzM1y9gYC9gWtwTD6MEsq+Y8Lm7DMZ7cZxt5/T5xd8Fy5SfuC6fA4QZjdd8eEX9eLwAZ1StHKiT1MARStnHtWZH7bpjKIVgKutmipq50O94Mr6+9oKR8ekyy7x5qd9/NmYslynK4WfUjWOqiAFgElZmY+41BTzcrAkW9d9ptO9etgU7I8S3vVyDtkY/JzPwoxXzCJjt6Uy+r/fuGq/3affezkPRSniJ0y2zsqT6+n+BfRHKWgwbKNIAAAAAElFTkSuQmCC)](https://purpurmc.org/downloads/) +[![Build Status](https://img.shields.io/github/actions/workflow/status/PurpurMC/Purpur/build.yml?branch=ver%2F1.21.11&event=push&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bxSIVh1YQcYhQnSyIiuimVShChVArtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE0clJ0UVK/F9SaBHjwXE/3t173L0D/PUyU82OMUDVLCOViAuZ7KrQ9YogwujDEGYkZupzopiE5/i6h4+vdzGe5X3uz9Gj5EwG+ATiWaYbFvEG8dSmpXPeJ46woqQQnxOPGnRB4keuyy6/cS447OeZESOdmieOEAuFNpbbmBUNlXiSOKqoGuX7My4rnLc4q+Uqa96TvzCU01aWuU5zEAksYgkiBMioooQyLMRo1UgxkaL9uId/wPGL5JLJVQIjxwIqUCE5fvA/+N2tmZ8Yd5NCcaDzxbY/hoGuXaBRs+3vY9tunACBZ+BKa/krdWD6k/RaS4seAb3bwMV1S5P3gMsdoP9JlwzJkQI0/fk88H5G35QFwrdA95rbW3Mfpw9AmrpK3gAHh8BIgbLXPd4dbO/t3zPN/n4Ax9dyyerighsAAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfmCBMWBjFhOpnxAAACyklEQVQ4y32TXWgdZRCGn8k5Z3e/bzcJRFuoDRqIIPGniaSl1qgXKYhiLUgleFESW1qQ4g94oVKQJmga0ILRBuvfheJFIbY3SmzRFsWgQrTQFlRoKSVXgcMxJiS7++1ukvHiNKGU6FzNDO8MMw8zwn9Yz5c6anKeChxiMk599aq8sZ5Obk3sGNd7G5aZCHJ+Oj8gAwD9I3rSJmyvzLHz+HG5frO+vOaNa3NXiUkKQj9hQ1jQgWrlyCCV6gIdtmBjoJwbPqgL+R08PDQkCUADwD0TerqzxFTJY++vz0l7c0q/F7P5+aNUqyvM2CU2RAV7hsek3XPs3/QHF7/YrSfrDVQrxrErdESNNTYByAp3NmWUTYaGGSuho+QltAGYRdqilChMeKbO4Ijang5+CVJmbcr20PG3n9NiY6ZOvCW9ACMHdDLIeDDIqTWmtAQJV6OMyhOTsqWBQQo/hvP7pFcWeDxwtAQxGqbEq3isI7EOjRy3NyuPPHtOur0UXWMQZXgAX78uP3sJpTBmyHfsGDmotdF+rQWObq/gnSCm9PQ3chnA3qgRgL5jmjXlTM/n7Ny8yJXRY2IA3tur0yaHF8blLoAzj2kaNNBpZvk2iGntuiZBHWLOtXJC3hbzV5SxtDq6yZmzCXOrsRezHNa4YBzlKGN6bQXryD96W+4PU14zGbw/oLMf79EPbAI2g7M9+smP3fqPKVAT81LnFWnzM1y9gYC9gWtwTD6MEsq+Y8Lm7DMZ7cZxt5/T5xd8Fy5SfuC6fA4QZjdd8eEX9eLwAZ1StHKiT1MARStnHtWZH7bpjKIVgKutmipq50O94Mr6+9oKR8ekyy7x5qd9/NmYslynK4WfUjWOqiAFgElZmY+41BTzcrAkW9d9ptO9etgU7I8S3vVyDtkY/JzPwoxXzCJjt6Uy+r/fuGq/3affezkPRSniJ0y2zsqT6+n+BfRHKWgwbKNIAAAAAElFTkSuQmCC)](https://purpurmc.org/downloads/) [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/PurpurMC/Purpur?logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bxSIVh1YQcYhQnSyIiuimVShChVArtOpgcukXNGlIUlwcBdeCgx%2BLVQcXZ10dXAVB8APE0clJ0UVK%2FF9SaBHjwXE%2F3t173L0D%2FPUyU82OMUDVLCOViAuZ7KrQ9YogwujDEGYkZupzopiE5%2Fi6h4%2BvdzGe5X3uz9Gj5EwG%2BATiWaYbFvEG8dSmpXPeJ46woqQQnxOPGnRB4keuyy6%2FcS447OeZESOdmieOEAuFNpbbmBUNlXiSOKqoGuX7My4rnLc4q%2BUqa96TvzCU01aWuU5zEAksYgkiBMioooQyLMRo1UgxkaL9uId%2FwPGL5JLJVQIjxwIqUCE5fvA%2F%2BN2tmZ8Yd5NCcaDzxbY%2FhoGuXaBRs%2B3vY9tunACBZ%2BBKa%2FkrdWD6k%2FRaS4seAb3bwMV1S5P3gMsdoP9JlwzJkQI0%2Ffk88H5G35QFwrdA95rbW3Mfpw9AmrpK3gAHh8BIgbLXPd4dbO%2Ft3zPN%2Fn4Ax9dyyerighsAAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfmCBMVKAA5pS6%2BAAABlElEQVQ4y82PP2gVQRDGf7N3t%2Bvdixpi0N5OELFKJ1iohBciKlgYJLX6YkBbC0sVooVFBAvBPw%2BFZzrJs7DR2iYHRhBsxNI8VLwUx92MRXJGxKCp9AfL7DfDfPutFO3z5wy5DuRlWU2OvLj7hduLYXh0ZSEkOh4SjUKiBK%2BEZP34Gu%2FtbebLE86Qa8BO4FDwyWmAbPjzMWACiNgEMdun6macwfJ6z2qxZYBI6ndAxR%2BRN%2FL1ZGeXlDqFkm%2Fv33nZjHZ0u2OZrw%2F7pBYf16Re8UEJ8VpNE33fP3BxgX%2BOFOOdtjmuGpoPtT51pNcrMZORx4%2FmslQnslAlWahItymZrz%2Bmqc4%2B2z%2B71BjE5uwesEeQsaLY%2FQp42LrfPUqwy2DNO03ZK9hN4Ehj4IDBjzjKCoC5aMDG9q%2BhBz%2BrWCN3KqptBtG89Xx%2BEWB1%2Bszr8OTBFMgkSLKWQAA%2BVCU3%2BK%2BQb%2B0LB4FLGHmrP39LNv3773Ei9IBphLnVduf4VhM4M9JGqGzc%2F5bYnDsrqlcQloaK0adbNfgOUn6NRlZZ46YAAAAASUVORK5CYII%3D)](https://www.codefactor.io/repository/github/PurpurMC/Purpur) [![Join us on Discord](https://discord.com/api/guilds/685683385313919172/widget.png?style=shield)](https://purpurmc.org/discord) @@ -27,7 +27,7 @@ Join us on Discord: ## Downloads Downloads can be obtained from the [downloads page](https://purpurmc.org/downloads/) or the [downloads API](https://api.purpurmc.org). -[![Build Status](https://img.shields.io/github/actions/workflow/status/PurpurMC/Purpur/build.yml?branch=ver%2F1.20.1&event=push&label=Downloads&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bxSIVh1YQcYhQnSyIiuimVShChVArtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE0clJ0UVK/F9SaBHjwXE/3t173L0D/PUyU82OMUDVLCOViAuZ7KrQ9YogwujDEGYkZupzopiE5/i6h4+vdzGe5X3uz9Gj5EwG+ATiWaYbFvEG8dSmpXPeJ46woqQQnxOPGnRB4keuyy6/cS447OeZESOdmieOEAuFNpbbmBUNlXiSOKqoGuX7My4rnLc4q+Uqa96TvzCU01aWuU5zEAksYgkiBMioooQyLMRo1UgxkaL9uId/wPGL5JLJVQIjxwIqUCE5fvA/+N2tmZ8Yd5NCcaDzxbY/hoGuXaBRs+3vY9tunACBZ+BKa/krdWD6k/RaS4seAb3bwMV1S5P3gMsdoP9JlwzJkQI0/fk88H5G35QFwrdA95rbW3Mfpw9AmrpK3gAHh8BIgbLXPd4dbO/t3zPN/n4Ax9dyyerighsAAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfmCBMWBjFhOpnxAAACyklEQVQ4y32TXWgdZRCGn8k5Z3e/bzcJRFuoDRqIIPGniaSl1qgXKYhiLUgleFESW1qQ4g94oVKQJmga0ILRBuvfheJFIbY3SmzRFsWgQrTQFlRoKSVXgcMxJiS7++1ukvHiNKGU6FzNDO8MMw8zwn9Yz5c6anKeChxiMk599aq8sZ5Obk3sGNd7G5aZCHJ+Oj8gAwD9I3rSJmyvzLHz+HG5frO+vOaNa3NXiUkKQj9hQ1jQgWrlyCCV6gIdtmBjoJwbPqgL+R08PDQkCUADwD0TerqzxFTJY++vz0l7c0q/F7P5+aNUqyvM2CU2RAV7hsek3XPs3/QHF7/YrSfrDVQrxrErdESNNTYByAp3NmWUTYaGGSuho+QltAGYRdqilChMeKbO4Ijang5+CVJmbcr20PG3n9NiY6ZOvCW9ACMHdDLIeDDIqTWmtAQJV6OMyhOTsqWBQQo/hvP7pFcWeDxwtAQxGqbEq3isI7EOjRy3NyuPPHtOur0UXWMQZXgAX78uP3sJpTBmyHfsGDmotdF+rQWObq/gnSCm9PQ3chnA3qgRgL5jmjXlTM/n7Ny8yJXRY2IA3tur0yaHF8blLoAzj2kaNNBpZvk2iGntuiZBHWLOtXJC3hbzV5SxtDq6yZmzCXOrsRezHNa4YBzlKGN6bQXryD96W+4PU14zGbw/oLMf79EPbAI2g7M9+smP3fqPKVAT81LnFWnzM1y9gYC9gWtwTD6MEsq+Y8Lm7DMZ7cZxt5/T5xd8Fy5SfuC6fA4QZjdd8eEX9eLwAZ1StHKiT1MARStnHtWZH7bpjKIVgKutmipq50O94Mr6+9oKR8ekyy7x5qd9/NmYslynK4WfUjWOqiAFgElZmY+41BTzcrAkW9d9ptO9etgU7I8S3vVyDtkY/JzPwoxXzCJjt6Uy+r/fuGq/3affezkPRSniJ0y2zsqT6+n+BfRHKWgwbKNIAAAAAElFTkSuQmCC)](https://purpurmc.org/downloads/) +[![Build Status](https://img.shields.io/github/actions/workflow/status/PurpurMC/Purpur/build.yml?branch=ver%2F1.21.11event=push&label=Downloads&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9bxSIVh1YQcYhQnSyIiuimVShChVArtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE0clJ0UVK/F9SaBHjwXE/3t173L0D/PUyU82OMUDVLCOViAuZ7KrQ9YogwujDEGYkZupzopiE5/i6h4+vdzGe5X3uz9Gj5EwG+ATiWaYbFvEG8dSmpXPeJ46woqQQnxOPGnRB4keuyy6/cS447OeZESOdmieOEAuFNpbbmBUNlXiSOKqoGuX7My4rnLc4q+Uqa96TvzCU01aWuU5zEAksYgkiBMioooQyLMRo1UgxkaL9uId/wPGL5JLJVQIjxwIqUCE5fvA/+N2tmZ8Yd5NCcaDzxbY/hoGuXaBRs+3vY9tunACBZ+BKa/krdWD6k/RaS4seAb3bwMV1S5P3gMsdoP9JlwzJkQI0/fk88H5G35QFwrdA95rbW3Mfpw9AmrpK3gAHh8BIgbLXPd4dbO/t3zPN/n4Ax9dyyerighsAAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfmCBMWBjFhOpnxAAACyklEQVQ4y32TXWgdZRCGn8k5Z3e/bzcJRFuoDRqIIPGniaSl1qgXKYhiLUgleFESW1qQ4g94oVKQJmga0ILRBuvfheJFIbY3SmzRFsWgQrTQFlRoKSVXgcMxJiS7++1ukvHiNKGU6FzNDO8MMw8zwn9Yz5c6anKeChxiMk599aq8sZ5Obk3sGNd7G5aZCHJ+Oj8gAwD9I3rSJmyvzLHz+HG5frO+vOaNa3NXiUkKQj9hQ1jQgWrlyCCV6gIdtmBjoJwbPqgL+R08PDQkCUADwD0TerqzxFTJY++vz0l7c0q/F7P5+aNUqyvM2CU2RAV7hsek3XPs3/QHF7/YrSfrDVQrxrErdESNNTYByAp3NmWUTYaGGSuho+QltAGYRdqilChMeKbO4Ijang5+CVJmbcr20PG3n9NiY6ZOvCW9ACMHdDLIeDDIqTWmtAQJV6OMyhOTsqWBQQo/hvP7pFcWeDxwtAQxGqbEq3isI7EOjRy3NyuPPHtOur0UXWMQZXgAX78uP3sJpTBmyHfsGDmotdF+rQWObq/gnSCm9PQ3chnA3qgRgL5jmjXlTM/n7Ny8yJXRY2IA3tur0yaHF8blLoAzj2kaNNBpZvk2iGntuiZBHWLOtXJC3hbzV5SxtDq6yZmzCXOrsRezHNa4YBzlKGN6bQXryD96W+4PU14zGbw/oLMf79EPbAI2g7M9+smP3fqPKVAT81LnFWnzM1y9gYC9gWtwTD6MEsq+Y8Lm7DMZ7cZxt5/T5xd8Fy5SfuC6fA4QZjdd8eEX9eLwAZ1StHKiT1MARStnHtWZH7bpjKIVgKutmipq50O94Mr6+9oKR8ekyy7x5qd9/NmYslynK4WfUjWOqiAFgElZmY+41BTzcrAkW9d9ptO9etgU7I8S3vVyDtkY/JzPwoxXzCJjt6Uy+r/fuGq/3affezkPRSniJ0y2zsqT6+n+BfRHKWgwbKNIAAAAAElFTkSuQmCC)](https://purpurmc.org/downloads/) Downloads API endpoints: * List versions of Minecraft with builds available: @@ -67,7 +67,7 @@ Maven org.purpurmc.purpur purpur-api - 1.20.1-R0.1-SNAPSHOT + 1.21.11-R0.1-SNAPSHOT provided ``` @@ -80,7 +80,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("org.purpurmc.purpur:purpur-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("org.purpurmc.purpur:purpur-api:1.21.11-R0.1-SNAPSHOT") } ``` @@ -94,28 +94,23 @@ First, clone this repository. Do not download it. Then run the following command in the root directory: ``` -./gradlew applyPatches +./gradlew applyAllPatches ``` The project is now ready for use in your IDE. #### Creating a patch -Patches are effectively just commits in either `Purpur-API` or `Purpur-Server`. -To create one, just add a commit to either repo and run `./gradlew rebuildPatches`, and a -patch will be placed in the patches folder. Modifying commits will also modify its -corresponding patch file. - -See [CONTRIBUTING.md](CONTRIBUTING.md) for more detailed information. +See [CONTRIBUTING.md](CONTRIBUTING.md). #### Compiling Use the command `./gradlew build` to build the API and server. Compiled JARs -will be placed under `Purpur-API/build/libs` and `Purpur-Server/build/libs`. +will be placed under `purpur-api/build/libs` and `purpur-server/build/libs`. **These JARs are not used to start a server.** -To compile a server-ready purpurclip jar, run `./gradlew createReobfPaperclipJar`. -To install the `purpur-api` and `purpur` dependencies to your local Maven repo, run `./gradlew publishToMavenLocal`. The compiled purpurclip jar will be in `build/libs/` not `Purpur-Server/build/libs`. +To compile a server-ready purpurclip jar, run `./gradlew createMojmapBundlerJar`. +To install the `purpur-api` and `purpur` dependencies to your local Maven repo, run `./gradlew publishToMavenLocal`. The compiled purpurclip jar will be in `purpur-server/build/libs`. Special Thanks To: ------------- diff --git a/build-data/dev-imports.txt b/build-data/dev-imports.txt index b818b96e2..ff6c20ae5 100644 --- a/build-data/dev-imports.txt +++ b/build-data/dev-imports.txt @@ -5,6 +5,4 @@ # authlib com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java # datafixerupper com.mojang.datafixers.DataFixerBuilder # datafixerupper com/mojang/datafixers/util/Either.java -# To import classes from the vanilla Minecraft jar use `minecraft` as the artifactId: -# minecraft net.minecraft.world.level.entity.LevelEntityGetterAdapter -# minecraft net/minecraft/world/level/entity/LevelEntityGetter.java +# diff --git a/build-data/purpur.at b/build-data/purpur.at new file mode 100644 index 000000000..bd47c89a1 --- /dev/null +++ b/build-data/purpur.at @@ -0,0 +1,11 @@ +# This file is auto generated, any changes may be overridden! +# See CONTRIBUTING.md on how to add access transformers. +protected net.minecraft.world.entity.Entity dimensions +public net.minecraft.world.entity.Entity updateInWaterStateAndDoWaterCurrentPushing()V +public net.minecraft.world.entity.LivingEntity canGlide()Z +public net.minecraft.world.entity.monster.Shulker MAX_SCALE +public net.minecraft.world.entity.player.Player canGlide()Z +public net.minecraft.world.level.block.ShulkerBoxBlock canOpen(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/ShulkerBoxBlockEntity;)Z +public net.minecraft.world.level.block.entity.FuelValues values +public-f net.minecraft.world.entity.EntityType dimensions +public-f net.minecraft.world.level.block.state.BlockBehaviour explosionResistance diff --git a/build.gradle.kts b/build.gradle.kts index 724874df2..7d9eeaeb7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,29 +2,50 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - java - `maven-publish` - id("com.github.johnrengelman.shadow") version "8.1.1" apply false - id("io.papermc.paperweight.patcher") version "1.5.5" -} - -allprojects { - apply(plugin = "java") - apply(plugin = "maven-publish") - - java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } + java // TODO java launcher tasks + id("io.papermc.paperweight.patcher") version "2.0.0-beta.19" } val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" +paperweight { + upstreams.paper { + ref = providers.gradleProperty("paperCommit") + + patchFile { + path = "paper-server/build.gradle.kts" + outputFile = file("purpur-server/build.gradle.kts") + patchFile = file("purpur-server/build.gradle.kts.patch") + } + patchFile { + path = "paper-api/build.gradle.kts" + outputFile = file("purpur-api/build.gradle.kts") + patchFile = file("purpur-api/build.gradle.kts.patch") + } + patchDir("paperApi") { + upstreamPath = "paper-api" + excludes = setOf("build.gradle.kts") + patchesDir = file("purpur-api/paper-patches") + outputDir = file("paper-api") + } + } +} + subprojects { - tasks.withType().configureEach { + apply(plugin = "java-library") + apply(plugin = "maven-publish") + + extensions.configure { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + tasks.withType { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release = 21 + options.isFork = true + options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal")) } tasks.withType { options.encoding = Charsets.UTF_8.name() @@ -39,60 +60,18 @@ subprojects { events(TestLogEvent.STANDARD_OUT) } } + tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + } repositories { mavenCentral() maven(paperMavenPublicUrl) maven("https://jitpack.io") } -} -repositories { - mavenCentral() - maven(paperMavenPublicUrl) { - content { - onlyForConfigurations(configurations.paperclip.name) - } - } -} - -dependencies { - remapper("net.fabricmc:tiny-remapper:0.8.6:fat") - decompiler("net.minecraftforge:forgeflower:2.0.627.2") - paperclip("io.papermc:paperclip:3.0.3") -} - -paperweight { - serverProject.set(project(":purpur-server")) - - remapRepo.set(paperMavenPublicUrl) - decompileRepo.set(paperMavenPublicUrl) - - usePaperUpstream(providers.gradleProperty("paperCommit")) { - withPaperPatcher { - apiPatchDir.set(layout.projectDirectory.dir("patches/api")) - apiOutputDir.set(layout.projectDirectory.dir("Purpur-API")) - - serverPatchDir.set(layout.projectDirectory.dir("patches/server")) - serverOutputDir.set(layout.projectDirectory.dir("Purpur-Server")) - } - } -} - -tasks.generateDevelopmentBundle { - apiCoordinates.set("org.purpurmc.purpur:purpur-api") - mojangApiCoordinates.set("io.papermc.paper:paper-mojangapi") - libraryRepositories.set( - listOf( - "https://repo.maven.apache.org/maven2/", - paperMavenPublicUrl, - "https://repo.purpurmc.org/snapshots", - ) - ) -} - -allprojects { - publishing { + extensions.configure { repositories { maven("https://repo.purpurmc.org/snapshots") { name = "purpur" @@ -102,14 +81,6 @@ allprojects { } } -publishing { - publications.create("devBundle") { - artifact(tasks.generateDevelopmentBundle) { - artifactId = "dev-bundle" - } - } -} - tasks.register("printMinecraftVersion") { doLast { println(providers.gradleProperty("mcVersion").get().trim()) diff --git a/gradle.properties b/gradle.properties index d54587e35..0ff9eb533 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,11 @@ group = org.purpurmc.purpur -version = 1.20.1-R0.1-SNAPSHOT +version = 1.21.11-R0.1-SNAPSHOT -mcVersion = 1.20.1 -paperCommit = 0c0a480d82132dca0e4bb7a2275c9363430e544b +mcVersion = 1.21.11 +apiVersion=1.21.11 +paperCommit = e5e2c50ed3f7a6e4dbe7451c089cb1ad3f77f8f9 +org.gradle.configuration-cache = true org.gradle.caching = true org.gradle.parallel = true org.gradle.vfs.watch = false -org.gradle.jvmargs = -Xmx3G diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba771..f8e1ee312 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c30b486a8..bad7c2462 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d421..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,10 +85,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -133,10 +132,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +146,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +154,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -169,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -197,16 +198,19 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f1..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,22 +59,21 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/patches/api/0013-LivingEntity-safeFallDistance.patch b/patches/1-20-6/dropped-api/0011-LivingEntity-safeFallDistance.patch similarity index 69% rename from patches/api/0013-LivingEntity-safeFallDistance.patch rename to patches/1-20-6/dropped-api/0011-LivingEntity-safeFallDistance.patch index dc208aa25..a858bf887 100644 --- a/patches/api/0013-LivingEntity-safeFallDistance.patch +++ b/patches/1-20-6/dropped-api/0011-LivingEntity-safeFallDistance.patch @@ -5,27 +5,31 @@ Subject: [PATCH] LivingEntity safeFallDistance diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java -index 19e58e62ae442ef9be02ca7fa2f55e370a54afa4..c5dcfd380206603c2979aa02e7094fef27348eab 100644 +index b777e530122549455dcce6fac8d4a151c1c0af42..57a3e330043077f042a284c99e2631e1582cb32c 100644 --- a/src/main/java/org/bukkit/entity/LivingEntity.java +++ b/src/main/java/org/bukkit/entity/LivingEntity.java -@@ -1192,4 +1192,20 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource +@@ -1447,4 +1447,24 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource */ void setBodyYaw(float bodyYaw); - // Paper end + // Paper end - body yaw API + + // Purpur start + /** + * Gets the distance (in blocks) this entity can safely fall without taking damage + * + * @return Safe fall distance ++ * @deprecated use {@link org.bukkit.attribute.Attribute#GENERIC_SAFE_FALL_DISTANCE} instead + */ ++ @Deprecated + float getSafeFallDistance(); + + /** + * Set the distance (in blocks) this entity can safely fall without taking damage + * + * @param safeFallDistance Safe fall distance ++ * @deprecated use {@link org.bukkit.attribute.Attribute#GENERIC_SAFE_FALL_DISTANCE} instead + */ ++ @Deprecated + void setSafeFallDistance(float safeFallDistance); + // Purpur end } diff --git a/patches/api/0038-Potion-NamespacedKey.patch b/patches/1-20-6/dropped-api/0032-Potion-NamespacedKey.patch similarity index 66% rename from patches/api/0038-Potion-NamespacedKey.patch rename to patches/1-20-6/dropped-api/0032-Potion-NamespacedKey.patch index babf7156b..b26f1772c 100644 --- a/patches/api/0038-Potion-NamespacedKey.patch +++ b/patches/1-20-6/dropped-api/0032-Potion-NamespacedKey.patch @@ -5,18 +5,10 @@ Subject: [PATCH] Potion NamespacedKey diff --git a/src/main/java/org/bukkit/potion/PotionEffect.java b/src/main/java/org/bukkit/potion/PotionEffect.java -index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f026703cb80 100644 +index c8ab330ef171795d08fa201cf8320703c7f1c66b..93e2ea220dc03c122f82af65d5e9fda5b582290c 100644 --- a/src/main/java/org/bukkit/potion/PotionEffect.java +++ b/src/main/java/org/bukkit/potion/PotionEffect.java -@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap; - import java.util.Map; - import java.util.NoSuchElementException; - import org.bukkit.Color; -+import org.bukkit.NamespacedKey; - import org.bukkit.configuration.serialization.ConfigurationSerializable; - import org.bukkit.configuration.serialization.SerializableAs; - import org.bukkit.entity.LivingEntity; -@@ -31,12 +32,14 @@ public class PotionEffect implements ConfigurationSerializable { +@@ -33,6 +33,7 @@ public class PotionEffect implements ConfigurationSerializable { private static final String AMBIENT = "ambient"; private static final String PARTICLES = "has-particles"; private static final String ICON = "has-icon"; @@ -24,34 +16,46 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 private final int amplifier; private final int duration; private final PotionEffectType type; - private final boolean ambient; +@@ -40,6 +41,7 @@ public class PotionEffect implements ConfigurationSerializable { private final boolean particles; private final boolean icon; + private final PotionEffect hiddenEffect; // Paper + @Nullable private final NamespacedKey key; // Purpur /** * Creates a potion effect. -@@ -49,6 +52,36 @@ public class PotionEffect implements ConfigurationSerializable { +@@ -50,11 +52,12 @@ public class PotionEffect implements ConfigurationSerializable { + * @param ambient the ambient status, see {@link PotionEffect#isAmbient()} + * @param particles the particle status, see {@link PotionEffect#hasParticles()} + * @param icon the icon status, see {@link PotionEffect#hasIcon()} ++ * @param key the namespacedKey, see {@link PotionEffect#getKey()} + * @param hiddenEffect the hidden PotionEffect + * @hidden Internal-- hidden effects are only shown internally + */ + @org.jetbrains.annotations.ApiStatus.Internal // Paper +- public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon, @Nullable PotionEffect hiddenEffect) { // Paper ++ public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon, @Nullable PotionEffect hiddenEffect, @Nullable NamespacedKey key) { // Paper // Purpur + Preconditions.checkArgument(type != null, "effect type cannot be null"); + this.type = type; + this.duration = duration; +@@ -62,6 +65,7 @@ public class PotionEffect implements ConfigurationSerializable { + this.ambient = ambient; + this.particles = particles; + this.icon = icon; ++ this.key = key; // Purpur + // Paper start + this.hiddenEffect = hiddenEffect; + } +@@ -77,10 +81,27 @@ public class PotionEffect implements ConfigurationSerializable { * @param icon the icon status, see {@link PotionEffect#hasIcon()} */ public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon) { -+ // Purpur start -+ this(type, duration, amplifier, ambient, particles, icon, null); -+ } -+ -+ /** -+ * Create a potion effect. -+ * @param duration measured in ticks, see {@link -+ * PotionEffect#getDuration()} -+ * @param amplifier the amplifier, see {@link PotionEffect#getAmplifier()} -+ * @param ambient the ambient status, see {@link PotionEffect#isAmbient()} -+ * @param particles the particle status, see {@link PotionEffect#hasParticles()} -+ * @param key the namespacedKey, see {@link PotionEffect#getKey()} -+ */ -+ public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, @Nullable NamespacedKey key) { -+ this(type, duration, amplifier, ambient, particles, particles, key); -+ } -+ +- this(type, duration, amplifier, ambient, particles, icon, null); ++ this(type, duration, amplifier, ambient, particles, icon, null, null); // Purpur + // Paper end + } + ++ // Purpur start + /** + * Creates a potion effect. + * @param type effect type @@ -64,24 +68,19 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 + * @param key the namespacedKey, see {@link PotionEffect#getKey()} + */ + public PotionEffect(@NotNull PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon, @Nullable NamespacedKey key) { -+ // Purpur end - Preconditions.checkArgument(type != null, "effect type cannot be null"); - this.type = type; - this.duration = duration; -@@ -56,6 +89,7 @@ public class PotionEffect implements ConfigurationSerializable { - this.ambient = ambient; - this.particles = particles; - this.icon = icon; -+ this.key = key; // Purpur - add key - } - ++ this(type, duration, amplifier, ambient, particles, icon, null, key); ++ } ++ // Purpur end ++ /** -@@ -103,36 +137,43 @@ public class PotionEffect implements ConfigurationSerializable { + * Creates a potion effect with no defined color. + * +@@ -126,33 +147,33 @@ public class PotionEffect implements ConfigurationSerializable { * @param map the map to deserialize from */ public PotionEffect(@NotNull Map map) { -- this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT, false), getBool(map, PARTICLES, true), getBool(map, ICON, getBool(map, PARTICLES, true))); -+ this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT, false), getBool(map, PARTICLES, true), getBool(map, ICON, getBool(map, PARTICLES, true)), getKey(map)); // Purpur - getKey +- this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT, false), getBool(map, PARTICLES, true), getBool(map, ICON, getBool(map, PARTICLES, true)), (PotionEffect) map.get(HIDDEN_EFFECT)); // Paper ++ this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT, false), getBool(map, PARTICLES, true), getBool(map, ICON, getBool(map, PARTICLES, true)), (PotionEffect) map.get(HIDDEN_EFFECT), getKey(map)); // Paper // Purpur - getKey } // Paper start @@ -115,6 +114,10 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 - return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon); + return new PotionEffect(this.type, duration, amplifier, ambient, particles, icon, key); // Purpur - add key } + + /** +@@ -169,6 +190,13 @@ public class PotionEffect implements ConfigurationSerializable { + } // Paper end + // Purpur start @@ -126,8 +129,8 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 + @NotNull private static PotionEffectType getEffectType(@NotNull Map map) { - int type = getInt(map, TYPE); -@@ -159,17 +200,33 @@ public class PotionEffect implements ConfigurationSerializable { + PotionEffectType effect; +@@ -201,6 +229,17 @@ public class PotionEffect implements ConfigurationSerializable { return def; } @@ -145,40 +148,28 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 @Override @NotNull public Map serialize() { -- return ImmutableMap.builder() -- .put(TYPE, type.getId()) -- .put(DURATION, duration) -- .put(AMPLIFIER, amplifier) -- .put(AMBIENT, ambient) -- .put(PARTICLES, particles) -- .put(ICON, icon) -- .build(); -+ // Purpur start - add key, don't serialize if null. -+ ImmutableMap.Builder builder = ImmutableMap.builder() -+ .put(TYPE, type.getId()) -+ .put(DURATION, duration) -+ .put(AMPLIFIER, amplifier) -+ .put(AMBIENT, ambient) -+ .put(PARTICLES, particles) -+ .put(ICON, icon); -+ if(key != null) { +@@ -215,6 +254,11 @@ public class PotionEffect implements ConfigurationSerializable { + if (this.hiddenEffect != null) { + builder.put(HIDDEN_EFFECT, this.hiddenEffect); + } ++ // Purpur start ++ if (key != null) { + builder.put(KEY, key.toString()); + } -+ return builder.build(); + // Purpur end + return builder.build(); + // Paper end } - - /** -@@ -193,7 +250,7 @@ public class PotionEffect implements ConfigurationSerializable { +@@ -243,7 +287,7 @@ public class PotionEffect implements ConfigurationSerializable { return false; } PotionEffect that = (PotionEffect) obj; -- return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration && this.particles == that.particles && this.icon == that.icon; -+ return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration && this.particles == that.particles && this.icon == that.icon && this.key == that.key; // Purpur - add key +- return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration && this.particles == that.particles && this.icon == that.icon && java.util.Objects.equals(this.hiddenEffect, that.hiddenEffect); // Paper ++ return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration && this.particles == that.particles && this.icon == that.icon && java.util.Objects.equals(this.hiddenEffect, that.hiddenEffect) && this.key == that.key; // Paper // Purpur - add key } /** -@@ -289,6 +346,24 @@ public class PotionEffect implements ConfigurationSerializable { +@@ -339,6 +383,24 @@ public class PotionEffect implements ConfigurationSerializable { return icon; } @@ -203,11 +194,11 @@ index ccdca0d75868135dc7b96daeff2236b225c4add1..cad9f4ddc6be23c595e79419872f8f02 @Override public int hashCode() { int hash = 1; -@@ -303,6 +378,6 @@ public class PotionEffect implements ConfigurationSerializable { +@@ -354,6 +416,6 @@ public class PotionEffect implements ConfigurationSerializable { @Override public String toString() { -- return type.getName() + (ambient ? ":(" : ":") + duration + "t-x" + amplifier + (ambient ? ")" : ""); -+ return type.getName() + (ambient ? ":(" : ":") + duration + "t-x" + amplifier + (ambient ? ")" : "") + (hasKey() ? "(" + key + ")" : ""); // Purpur - add key if not null +- return "PotionEffect{" + "amplifier=" + amplifier + ", duration=" + duration + ", type=" + type + ", ambient=" + ambient + ", particles=" + particles + ", icon=" + icon + ", hiddenEffect=" + hiddenEffect + '}'; // Paper ++ return "PotionEffect{" + "amplifier=" + amplifier + ", duration=" + duration + ", type=" + type + ", ambient=" + ambient + ", particles=" + particles + ", icon=" + icon + ", hiddenEffect=" + hiddenEffect + ", key=" + key + '}'; // Paper // Purpur - add key } } diff --git a/patches/api/0047-Add-item-packet-serialize-event.patch b/patches/1-20-6/dropped-api/0041-Add-item-packet-serialize-event.patch similarity index 100% rename from patches/api/0047-Add-item-packet-serialize-event.patch rename to patches/1-20-6/dropped-api/0041-Add-item-packet-serialize-event.patch diff --git a/patches/1-20-6/dropped-api/0049-Add-hover-lines-API.patch b/patches/1-20-6/dropped-api/0049-Add-hover-lines-API.patch new file mode 100644 index 000000000..052712ac8 --- /dev/null +++ b/patches/1-20-6/dropped-api/0049-Add-hover-lines-API.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Meln Cat +Date: Mon, 2 Oct 2023 17:42:19 -0700 +Subject: [PATCH] Add hover lines API + + +diff --git a/src/main/java/org/bukkit/inventory/ItemFactory.java b/src/main/java/org/bukkit/inventory/ItemFactory.java +index 98a970a6582dca22e719a31559c7becea4725cb2..1cd2962ceb4fa0a0a3e28a09fa4ccef5802cd5c4 100644 +--- a/src/main/java/org/bukkit/inventory/ItemFactory.java ++++ b/src/main/java/org/bukkit/inventory/ItemFactory.java +@@ -353,4 +353,14 @@ public interface ItemFactory { + */ + @NotNull ItemStack enchantWithLevels(@NotNull ItemStack itemStack, @org.jetbrains.annotations.Range(from = 1, to = 30) int levels, boolean allowTreasure, @NotNull java.util.Random random); + // Paper end - enchantWithLevels API ++ ++ // Purpur start ++ /** ++ * Returns the lines of text shown when hovering over an item ++ * @param itemStack The ItemStack ++ * @param advanced Whether advanced tooltips are shown ++ * @return the list of Components ++ */ ++ @NotNull java.util.List getHoverLines(@NotNull ItemStack itemStack, boolean advanced); ++ // Purpur end + } +diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java +index 6e9b4cbc81878616b1c48add5db534286d267b05..e497575b2f44bb8b3bb6fdda3ae2f5247cbb4679 100644 +--- a/src/main/java/org/bukkit/inventory/ItemStack.java ++++ b/src/main/java/org/bukkit/inventory/ItemStack.java +@@ -1644,5 +1644,14 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat + } + return random.nextInt(unbreaking + 1) > 0; + } ++ ++ /** ++ * Returns the lines of text shown when hovering over the item ++ * @param advanced Whether advanced tooltips are shown ++ * @return the list of Components ++ */ ++ public @NotNull List getHoverLines(boolean advanced) { ++ return Bukkit.getItemFactory().getHoverLines(this, advanced); ++ } + // Purpur end + } diff --git a/patches/1-20-6/dropped-server/0013-LivingEntity-safeFallDistance.patch b/patches/1-20-6/dropped-server/0013-LivingEntity-safeFallDistance.patch new file mode 100644 index 000000000..4991196f6 --- /dev/null +++ b/patches/1-20-6/dropped-server/0013-LivingEntity-safeFallDistance.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 5 May 2019 12:58:45 -0500 +Subject: [PATCH] LivingEntity safeFallDistance + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index aa351df679f300018367244c7ccb3e5a59e9276f..39c2a9f732b8e2452fd2dca07193a173d0c2ba1c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -1173,4 +1173,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + this.getHandle().setYBodyRot(bodyYaw); + } + // Paper end - body yaw API ++ ++ // Purpur start ++ @Override ++ public float getSafeFallDistance() { ++ return (float) getHandle().getAttributeValue(Attributes.SAFE_FALL_DISTANCE); ++ } ++ ++ @Override ++ public void setSafeFallDistance(float safeFallDistance) { ++ getHandle().getAttribute(Attributes.SAFE_FALL_DISTANCE).setBaseValue(safeFallDistance); ++ } ++ // Purpur end + } diff --git a/patches/1-20-6/dropped-server/0220-Potion-NamespacedKey.patch b/patches/1-20-6/dropped-server/0220-Potion-NamespacedKey.patch new file mode 100644 index 000000000..7b8d0b15e --- /dev/null +++ b/patches/1-20-6/dropped-server/0220-Potion-NamespacedKey.patch @@ -0,0 +1,223 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Racci +Date: Sat, 4 Dec 2021 00:07:05 +1100 +Subject: [PATCH] Potion NamespacedKey + + +diff --git a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java +index 32cf6ea96aaa2e6bd0cc28fa88492ceea3d34052..9787dd4fc6ca2ed8aba3db7674ad2dc26a529a7a 100644 +--- a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java ++++ b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java +@@ -53,6 +53,7 @@ public class MobEffectInstance implements Comparable { + private boolean showIcon; + @Nullable + public MobEffectInstance hiddenEffect; ++ private org.bukkit.NamespacedKey key; // Purpur - add key + private final MobEffectInstance.BlendState blendState = new MobEffectInstance.BlendState(); + + public MobEffectInstance(Holder effect) { +@@ -71,8 +72,14 @@ public class MobEffectInstance implements Comparable { + this(effect, duration, amplifier, ambient, visible, visible); + } + ++ // Purpur start ++ public MobEffectInstance(Holder effect, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable org.bukkit.NamespacedKey key) { ++ this(effect, duration, amplifier, ambient, showParticles, showIcon, null, key); ++ // Purpur end ++ } ++ + public MobEffectInstance(Holder effect, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon) { +- this(effect, duration, amplifier, ambient, showParticles, showIcon, null); ++ this(effect, duration, amplifier, ambient, showParticles, showIcon, null, null); // Purpur + } + + public MobEffectInstance( +@@ -82,7 +89,8 @@ public class MobEffectInstance implements Comparable { + boolean ambient, + boolean showParticles, + boolean showIcon, +- @Nullable MobEffectInstance hiddenEffect ++ @Nullable MobEffectInstance hiddenEffect, ++ @Nullable org.bukkit.NamespacedKey key // Purpur + ) { + this.effect = effect; + this.duration = duration; +@@ -90,6 +98,7 @@ public class MobEffectInstance implements Comparable { + this.ambient = ambient; + this.visible = showParticles; + this.showIcon = showIcon; ++ this.key = key; // Purpur - add key + this.hiddenEffect = hiddenEffect; + } + +@@ -135,6 +144,7 @@ public class MobEffectInstance implements Comparable { + this.ambient = that.ambient; + this.visible = that.visible; + this.showIcon = that.showIcon; ++ this.key = that.key; // Purpur - add key + } + + public boolean update(MobEffectInstance that) { +@@ -179,6 +189,13 @@ public class MobEffectInstance implements Comparable { + bl = true; + } + ++ // Purpur start ++ if (that.key != this.key) { ++ this.key = that.key; ++ bl = true; ++ } ++ // Purpur end ++ + return bl; + } + +@@ -222,6 +239,17 @@ public class MobEffectInstance implements Comparable { + return this.showIcon; + } + ++ // Purpur start ++ public boolean hasKey() { ++ return this.key != null; ++ } ++ ++ @Nullable ++ public org.bukkit.NamespacedKey getKey() { ++ return this.key; ++ } ++ // Purpur end ++ + public boolean tick(LivingEntity entity, Runnable overwriteCallback) { + if (this.hasRemainingDuration()) { + int i = this.isInfiniteDuration() ? entity.tickCount : this.duration; +@@ -286,6 +314,12 @@ public class MobEffectInstance implements Comparable { + string = string + ", Show Icon: false"; + } + ++ // Purpur start ++ if (this.hasKey()) { ++ string = string + ", Key: " + this.key; ++ } ++ // Purpur end ++ + return string; + } + +@@ -300,6 +334,7 @@ public class MobEffectInstance implements Comparable { + && this.duration == mobEffectInstance.duration + && this.amplifier == mobEffectInstance.amplifier + && this.ambient == mobEffectInstance.ambient ++ && this.key == mobEffectInstance.key // Purpur - add key + && this.effect.equals(mobEffectInstance.effect); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java +index 4a9e6a679530025caa710a152c5249299ceffdf9..ea4f3f606aad69965384c73eb1273ed0644297b8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java +@@ -42,6 +42,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + static final ItemMetaKey POTION_EFFECTS = new ItemMetaKey("custom-effects"); + static final ItemMetaKey POTION_COLOR = new ItemMetaKey("custom-color"); + static final ItemMetaKey DEFAULT_POTION = new ItemMetaKey("potion-type"); ++ static final ItemMetaKey KEY = new ItemMetaKey("key", "namespacedkey"); // Purpur - add key + + private PotionType type; + private List customEffects; +@@ -91,7 +92,13 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + boolean ambient = effect.isAmbient(); + boolean particles = effect.isVisible(); + boolean icon = effect.showIcon(); +- this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon)); ++ // Purpur start ++ NamespacedKey key = null; ++ if (tag.contains(CraftMetaPotion.KEY.NBT)) { ++ key = NamespacedKey.fromString(effect.getString(CraftMetaPotion.KEY.NBT)); ++ } ++ this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon, key)); ++ // Purpur end + } + }); + } +@@ -130,6 +137,11 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + if (this.customEffects != null) { + for (PotionEffect effect : this.customEffects) { + effectList.add(new MobEffectInstance(CraftPotionEffectType.bukkitToMinecraftHolder(effect.getType()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon())); ++ // Purpur start ++ if (effect.hasKey()) { ++ effectData.putString(CraftMetaPotion.KEY.NBT, effect.getKey().toString()); ++ } ++ // Purpur end + } + } + +@@ -196,7 +208,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + if (index != -1) { + if (overwrite) { + PotionEffect old = this.customEffects.get(index); +- if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient()) { ++ if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient() && old.getKey() == effect.getKey()) { // Purpur - add key + return false; + } + this.customEffects.set(index, effect); +diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java +index 14c58cf8d255c51473fd3d0092faeaf5a3c1ae0c..3ee9c14440046872b83de628b7f460d0782e9315 100644 +--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java ++++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java +@@ -11,7 +11,7 @@ public class CraftPotionUtil { + public static MobEffectInstance fromBukkit(PotionEffect effect) { + Holder type = CraftPotionEffectType.bukkitToMinecraftHolder(effect.getType()); + // Paper - Note: do not copy over the hidden effect, as this method is only used for applying to entities which we do not want to convert over. +- return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()); // Paper ++ return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon(), effect.getKey()); // Paper // Purpur - add key + } + + public static PotionEffect toBukkit(MobEffectInstance effect) { +@@ -20,7 +20,7 @@ public class CraftPotionUtil { + int duration = effect.getDuration(); + boolean ambient = effect.isAmbient(); + boolean particles = effect.isVisible(); +- return new PotionEffect(type, duration, amp, ambient, particles, effect.showIcon(), effect.hiddenEffect == null ? null : toBukkit(effect.hiddenEffect)); // Paper ++ return new PotionEffect(type, duration, amp, ambient, particles, effect.showIcon(), effect.hiddenEffect == null ? null : toBukkit(effect.hiddenEffect), effect.getKey()); // Paper // Purpur - add key + } + + public static boolean equals(Holder mobEffect, PotionEffectType type) { +diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java +index cbcd1c21646308b2a9706095e2e12177ca06734d..b3ccaea713e858e334cc91d1ae498e589e3daafa 100644 +--- a/src/test/java/org/bukkit/potion/PotionTest.java ++++ b/src/test/java/org/bukkit/potion/PotionTest.java +@@ -10,6 +10,7 @@ import net.minecraft.world.effect.MobEffect; + import net.minecraft.world.effect.MobEffectInstance; + import net.minecraft.world.item.alchemy.Potion; + import org.bukkit.craftbukkit.legacy.FieldRename; ++import org.bukkit.NamespacedKey; // Purpur + import org.bukkit.craftbukkit.potion.CraftPotionEffectType; + import org.bukkit.support.AbstractTestingBase; + import org.junit.jupiter.api.Test; +@@ -47,4 +48,27 @@ public class PotionTest extends AbstractTestingBase { + assertEquals(bukkit, byName, "Same type not returned by name " + key); + } + } ++ ++ // Purpur start ++ @Test ++ public void testNamespacedKey() { ++ NamespacedKey key = new NamespacedKey("testnamespace", "testkey"); ++ PotionEffect namedSpacedEffect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true, key); ++ assertNotNull(namedSpacedEffect.getKey()); ++ assertTrue(namedSpacedEffect.hasKey()); ++ assertFalse(namedSpacedEffect.withKey(null).hasKey()); ++ ++ PotionEffect effect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true); ++ assertNull(effect.getKey()); ++ assertFalse(effect.hasKey()); ++ assertTrue(namedSpacedEffect.withKey(key).hasKey()); ++ ++ Map s1 = namedSpacedEffect.serialize(); ++ Map s2 = effect.serialize(); ++ assertTrue(s1.containsKey("namespacedKey")); ++ assertFalse(s2.containsKey("namespacedKey")); ++ assertNotNull(new PotionEffect(s1).getKey()); ++ assertNull(new PotionEffect(s2).getKey()); ++ } ++ // Purpur end + } diff --git a/patches/server/0242-Configurable-food-attributes.patch b/patches/1-20-6/dropped-server/0236-Configurable-food-attributes.patch similarity index 90% rename from patches/server/0242-Configurable-food-attributes.patch rename to patches/1-20-6/dropped-server/0236-Configurable-food-attributes.patch index f13cde73e..cc0c5a94c 100644 --- a/patches/server/0242-Configurable-food-attributes.patch +++ b/patches/1-20-6/dropped-server/0236-Configurable-food-attributes.patch @@ -37,24 +37,23 @@ index 9967ba762567631f2bdb1e4f8fe16a13ea927b46..6c945ae8fe8b1517e312c688f829fab4 FoodProperties(int hunger, float saturationModifier, boolean meat, boolean alwaysEdible, boolean snack, List> statusEffects) { diff --git a/src/main/java/net/minecraft/world/food/Foods.java b/src/main/java/net/minecraft/world/food/Foods.java -index b16d9e2eaa589f19c563ee70b1a56d67dbcdecb0..71beab673f04cd051c46ea37f8c847316885d38d 100644 +index 4569cf30b33167a415256a8542820557ad38f89e..9c65eefa855f3622b6c9ae2a698cf332ba225c7f 100644 --- a/src/main/java/net/minecraft/world/food/Foods.java +++ b/src/main/java/net/minecraft/world/food/Foods.java -@@ -4,6 +4,9 @@ import net.minecraft.world.effect.MobEffectInstance; +@@ -4,6 +4,8 @@ import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; public class Foods { + public static final java.util.Map ALL_PROPERTIES = new java.util.HashMap<>(); // Purpur + public static final java.util.Map DEFAULT_PROPERTIES = new java.util.HashMap<>(); // Purpur -+ - public static final FoodProperties APPLE = (new FoodProperties.Builder()).nutrition(4).saturationMod(0.3F).build(); - public static final FoodProperties BAKED_POTATO = (new FoodProperties.Builder()).nutrition(5).saturationMod(0.6F).build(); - public static final FoodProperties BEEF = (new FoodProperties.Builder()).nutrition(3).saturationMod(0.3F).meat().build(); + public static final FoodProperties APPLE = new FoodProperties.Builder().nutrition(4).saturationMod(0.3F).build(); + public static final FoodProperties BAKED_POTATO = new FoodProperties.Builder().nutrition(5).saturationMod(0.6F).build(); + public static final FoodProperties BEEF = new FoodProperties.Builder().nutrition(3).saturationMod(0.3F).meat().build(); diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java -index 31f5ed9dd1727eee24804a384817d2b76a45676b..5fbb13ebef0ca66419f3e5006d19e4a5918a038a 100644 +index bb2103a488964f25335393fa91e8ae5749eca333..249c887af68f56739c3609bad2405ba2cbe11762 100644 --- a/src/main/java/net/minecraft/world/item/Items.java +++ b/src/main/java/net/minecraft/world/item/Items.java -@@ -1309,6 +1309,13 @@ public class Items { +@@ -1715,6 +1715,13 @@ public class Items { ((BlockItem)item).registerBlocks(Item.BY_BLOCK, item); } @@ -69,10 +68,10 @@ index 31f5ed9dd1727eee24804a384817d2b76a45676b..5fbb13ebef0ca66419f3e5006d19e4a5 } } diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 730a4a2e46aeb233d8036e8d7e1749dcb397e6c0..fa81039056af14c37426a508b7b2da77e8b50737 100644 +index 0e28cf20a870f3f3662bd1d8f7a4f2cbf13c48bf..ce3ab604e6ed6669f38abf83d40b500148277b9d 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -461,4 +461,75 @@ public class PurpurConfig { +@@ -466,4 +466,75 @@ public class PurpurConfig { String setPattern = getString("settings.username-valid-characters", defaultPattern); usernameValidCharactersPattern = java.util.regex.Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern); } diff --git a/patches/server/0265-Send-client-custom-name-of-BE.patch b/patches/1-20-6/dropped-server/0255-Send-client-custom-name-of-BE.patch similarity index 82% rename from patches/server/0265-Send-client-custom-name-of-BE.patch rename to patches/1-20-6/dropped-server/0255-Send-client-custom-name-of-BE.patch index 792957da9..30f7486ef 100644 --- a/patches/server/0265-Send-client-custom-name-of-BE.patch +++ b/patches/1-20-6/dropped-server/0255-Send-client-custom-name-of-BE.patch @@ -3,12 +3,13 @@ From: Ben Kerllenevich Date: Wed, 13 Jul 2022 16:27:43 -0400 Subject: [PATCH] Send client custom name of BE +https://modrinth.com/mod/know-my-name diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -index 2f19f6ac5de454845f5d13a3ebb93af625b2afc8..3431f1a00ae2918b91a6b7a449e613e6e12ff6d4 100644 +index 4ea15e17a1393864422edb6d5c57962651abf69a..a78ed43288cfefaeb2592ed0a33fd11565dea2b2 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -@@ -205,10 +205,24 @@ public abstract class BlockEntity { +@@ -256,10 +256,24 @@ public abstract class BlockEntity { @Nullable public Packet getUpdatePacket() { @@ -22,7 +23,7 @@ index 2f19f6ac5de454845f5d13a3ebb93af625b2afc8..3431f1a00ae2918b91a6b7a449e613e6 return null; } - public CompoundTag getUpdateTag() { + public CompoundTag getUpdateTag(HolderLookup.Provider registryLookup) { + // Purpur start + if (this instanceof net.minecraft.world.Nameable nameable && nameable.hasCustomName()) { + CompoundTag nbt = this.saveWithoutMetadata(); diff --git a/patches/server/0275-Allay-respect-item-NBT.patch b/patches/1-20-6/dropped-server/0263-Allay-respect-item-NBT.patch similarity index 88% rename from patches/server/0275-Allay-respect-item-NBT.patch rename to patches/1-20-6/dropped-server/0263-Allay-respect-item-NBT.patch index d49181e1b..4e5e97b5e 100644 --- a/patches/server/0275-Allay-respect-item-NBT.patch +++ b/patches/1-20-6/dropped-server/0263-Allay-respect-item-NBT.patch @@ -3,12 +3,13 @@ From: BillyGalbreath Date: Tue, 20 Sep 2022 17:56:21 -0500 Subject: [PATCH] Allay respect item NBT +https://github.com/PurpurMC/Purpur/discussions/1127 diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index d50bf1b980231a1045c1c9df622a9a50fc2ed893..7166f4a39fd615e10d7b1f53c57363832a41f365 100644 +index bca7b7192debb3a34a08047010a2438e7b7e2a78..53765198483e137d411e227119e4f912964aefe3 100644 --- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -407,9 +407,31 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS +@@ -399,9 +399,31 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS @Override public boolean wantsToPickUp(ItemStack stack) { @@ -44,10 +45,10 @@ index d50bf1b980231a1045c1c9df622a9a50fc2ed893..7166f4a39fd615e10d7b1f53c5736383 private boolean allayConsidersItemEqual(ItemStack stack, ItemStack stack2) { diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5f4b6d211e1a310ca2efcc94686e2757cff973ec..a4c5c469fed51b46727783ddefa293e53b9c66ca 100644 +index 5b12c08a1d8e6f62c5653c95071a1d36d735d039..94e29919ddc7f507d54e14c3360f7a3e8bb831a7 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1087,10 +1087,13 @@ public class PurpurWorldConfig { +@@ -1138,10 +1138,13 @@ public class PurpurWorldConfig { public boolean allayRidable = false; public boolean allayRidableInWater = true; public boolean allayControllable = true; diff --git a/patches/server/0279-Add-item-packet-serialize-event.patch b/patches/1-20-6/dropped-server/0271-Add-item-packet-serialize-event.patch similarity index 80% rename from patches/server/0279-Add-item-packet-serialize-event.patch rename to patches/1-20-6/dropped-server/0271-Add-item-packet-serialize-event.patch index 0174a055b..81f4367ab 100644 --- a/patches/server/0279-Add-item-packet-serialize-event.patch +++ b/patches/1-20-6/dropped-server/0271-Add-item-packet-serialize-event.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add item packet serialize event diff --git a/src/main/java/net/minecraft/network/FriendlyByteBuf.java b/src/main/java/net/minecraft/network/FriendlyByteBuf.java -index 9938bb90bef84cf784f9a1ceb02a1a45aa8b48a1..1f4b64a5f812376c499c98cb4be62469bd0b7dbe 100644 +index b863249ff7e13cf4939c8961601f0564c62fd661..bdcfd80f937c34956911373905d66424bbff8e1d 100644 --- a/src/main/java/net/minecraft/network/FriendlyByteBuf.java +++ b/src/main/java/net/minecraft/network/FriendlyByteBuf.java -@@ -98,6 +98,8 @@ public class FriendlyByteBuf extends ByteBuf { +@@ -95,6 +95,8 @@ public class FriendlyByteBuf extends ByteBuf { private static final int MAX_PUBLIC_KEY_LENGTH = 512; private static final Gson GSON = new Gson(); @@ -17,7 +17,7 @@ index 9938bb90bef84cf784f9a1ceb02a1a45aa8b48a1..1f4b64a5f812376c499c98cb4be62469 public FriendlyByteBuf(ByteBuf parent) { this.source = parent; } -@@ -679,6 +681,17 @@ public class FriendlyByteBuf extends ByteBuf { +@@ -640,6 +642,17 @@ public class FriendlyByteBuf extends ByteBuf { this.writeBoolean(false); } else { this.writeBoolean(true); @@ -36,22 +36,22 @@ index 9938bb90bef84cf784f9a1ceb02a1a45aa8b48a1..1f4b64a5f812376c499c98cb4be62469 this.writeId(BuiltInRegistries.ITEM, item); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 798586a3b01814548d702d16faddd406cdd5acec..99fd775e7a564aaef327a62bb7742da07b2d2829 100644 +index 4611116f3328c0f8d5b37c8765feca36b2448ffe..60b5e0643d933393b5473681ac9261db29fe2416 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1559,6 +1559,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Purpur - Iterator iterator = this.getAllLevels().iterator(); // Paper - move down + Iterator iterator = this.getAllLevels().iterator(); // Paper - Throw exception on world create while being ticked; move down while (iterator.hasNext()) { ServerLevel worldserver = (ServerLevel) iterator.next(); diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 9b488a98fdf06515ff267040dd6d654004665207..9cc3ab8cd3f7ab7f8fbf4d9d14f25ea0bd757eec 100644 +index 872be72e24017fdcb3060f6e4e9a92c342d59fc1..f7ac60e1aa188ec25a4c5d326cdd4a109a54101c 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -3545,6 +3545,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic +@@ -3382,6 +3382,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl } } } @@ -65,10 +65,10 @@ index 9b488a98fdf06515ff267040dd6d654004665207..9cc3ab8cd3f7ab7f8fbf4d9d14f25ea0 boolean flag1 = packet.getSlotNum() >= 1 && packet.getSlotNum() <= 45; boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty(); diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 568bb53e91dda4804cd328a81ba12ce735c52603..62aabfc021fa3349b0ab46744a9ec78d57f058b0 100644 +index 5313ba91ffc625b27d5bb99395f0e719829f6bda..5329ad6493950a561bd46045e35a9bd70ac4405f 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -563,4 +563,9 @@ public class PurpurConfig { +@@ -568,4 +568,9 @@ public class PurpurConfig { } }); } diff --git a/patches/1-20-6/dropped-server/0292-Add-hover-lines-API.patch b/patches/1-20-6/dropped-server/0292-Add-hover-lines-API.patch new file mode 100644 index 000000000..47c1dd61e --- /dev/null +++ b/patches/1-20-6/dropped-server/0292-Add-hover-lines-API.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Meln Cat +Date: Mon, 2 Oct 2023 17:42:26 -0700 +Subject: [PATCH] Add hover lines API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +index 6e2a6ce5cf456bd9f6c8c18a58f08e2285dc77ed..b27d16e74c3f99aa693b38590c1fb62db8204509 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +@@ -619,4 +619,17 @@ public final class CraftItemFactory implements ItemFactory { + return CraftItemStack.asCraftMirror(enchanted); + } + // Paper end - enchantWithLevels API ++ ++ // Purpur start ++ @Override ++ public @org.jetbrains.annotations.NotNull java.util.List getHoverLines(@org.jetbrains.annotations.NotNull ItemStack itemStack, boolean advanced) { ++ return io.papermc.paper.adventure.PaperAdventure.asAdventure( ++ CraftItemStack.asNMSCopy(itemStack).getTooltipLines( ++ null, ++ advanced ? net.minecraft.world.item.TooltipFlag.ADVANCED ++ : net.minecraft.world.item.TooltipFlag.NORMAL ++ ) ++ ); ++ } ++ // Purpur end + } diff --git a/patches/api/0021-LivingEntity-broadcastItemBreak.patch b/patches/1-21-1/dropped-api/0016-LivingEntity-broadcastItemBreak.patch similarity index 72% rename from patches/api/0021-LivingEntity-broadcastItemBreak.patch rename to patches/1-21-1/dropped-api/0016-LivingEntity-broadcastItemBreak.patch index ebc874b8b..7157d3d5f 100644 --- a/patches/api/0021-LivingEntity-broadcastItemBreak.patch +++ b/patches/1-21-1/dropped-api/0016-LivingEntity-broadcastItemBreak.patch @@ -5,19 +5,20 @@ Subject: [PATCH] LivingEntity#broadcastItemBreak diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java -index c5dcfd380206603c2979aa02e7094fef27348eab..accefccca9993edd00f72b4e5779fce0b3ee628f 100644 +index 5c29956c6db53440322330ff723c7087193641f1..e1079c5c4be99e75a646c090189678dd131f210e 100644 --- a/src/main/java/org/bukkit/entity/LivingEntity.java +++ b/src/main/java/org/bukkit/entity/LivingEntity.java -@@ -1207,5 +1207,12 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource - * @param safeFallDistance Safe fall distance +@@ -1447,4 +1447,13 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource */ - void setSafeFallDistance(float safeFallDistance); + void setBodyYaw(float bodyYaw); + // Paper end - body yaw API + ++ // Purpur start + /** + * Play item break animation for the item in specified equipment slot + * + * @param slot Equipment slot to play break animation for + */ + void broadcastItemBreak(@NotNull org.bukkit.inventory.EquipmentSlot slot); - // Purpur end ++ // Purpur end } diff --git a/patches/server/0044-End-gateway-should-check-if-entity-can-use-portal.patch b/patches/1-21-1/dropped-server/0040-End-gateway-should-check-if-entity-can-use-portal.patch similarity index 74% rename from patches/server/0044-End-gateway-should-check-if-entity-can-use-portal.patch rename to patches/1-21-1/dropped-server/0040-End-gateway-should-check-if-entity-can-use-portal.patch index bfc7326f3..d5bd1de5c 100644 --- a/patches/server/0044-End-gateway-should-check-if-entity-can-use-portal.patch +++ b/patches/1-21-1/dropped-server/0040-End-gateway-should-check-if-entity-can-use-portal.patch @@ -5,14 +5,14 @@ Subject: [PATCH] End gateway should check if entity can use portal diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -index 1ec80f9c901dff1c9f29befa5a8e3c3f6f37aaf7..9717b37aef9f487502e696c209ae209ab3b8f000 100644 +index 93bd70c1dc2ba8b893a6087730071c81fb1132f4..60b343edc4383c8bc450f106f483349850432fa3 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -@@ -177,6 +177,7 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { +@@ -167,6 +167,7 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { public static void teleportEntity(Level world, BlockPos pos, BlockState state, Entity entity, TheEndGatewayBlockEntity blockEntity) { - if (world instanceof ServerLevel && !blockEntity.isCoolingDown()) { + if (world instanceof ServerLevel worldserver && !blockEntity.isCoolingDown()) { + if (!entity.canChangeDimensions()) return; // Purpur - ServerLevel worldserver = (ServerLevel) world; - blockEntity.teleportCooldown = 100; + BlockPos blockposition1; + diff --git a/patches/server/0088-LivingEntity-broadcastItemBreak.patch b/patches/1-21-1/dropped-server/0082-LivingEntity-broadcastItemBreak.patch similarity index 72% rename from patches/server/0088-LivingEntity-broadcastItemBreak.patch rename to patches/1-21-1/dropped-server/0082-LivingEntity-broadcastItemBreak.patch index 56b7db78c..1e339efeb 100644 --- a/patches/server/0088-LivingEntity-broadcastItemBreak.patch +++ b/patches/1-21-1/dropped-server/0082-LivingEntity-broadcastItemBreak.patch @@ -5,18 +5,19 @@ Subject: [PATCH] LivingEntity#broadcastItemBreak diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index f7ae6d11c6043a2037dbfd160b409579bd9a35fd..f39747767d9a8da86bb25c34415b06172f39bcfc 100644 +index a1e715629313346f670bce92483996122b0f1d7b..386647f6000c71c59ab8d7875219eefdc5a3d7ef 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -1105,5 +1105,11 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - public void setSafeFallDistance(float safeFallDistance) { - getHandle().safeFallDistance = safeFallDistance; +@@ -1181,4 +1181,12 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + this.getHandle().setYBodyRot(bodyYaw); } + // Paper end - body yaw API + ++ // Purpur start + @Override + public void broadcastItemBreak(org.bukkit.inventory.EquipmentSlot slot) { + if (slot == null) return; + getHandle().broadcastBreakEvent(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)); + } - // Purpur end ++ // Purpur end } diff --git a/patches/server/0098-Allow-infinite-and-mending-enchantments-together.patch b/patches/1-21-1/dropped-server/0094-Allow-infinite-and-mending-enchantments-together.patch similarity index 77% rename from patches/server/0098-Allow-infinite-and-mending-enchantments-together.patch rename to patches/1-21-1/dropped-server/0094-Allow-infinite-and-mending-enchantments-together.patch index 270808d3a..b5f4be4d7 100644 --- a/patches/server/0098-Allow-infinite-and-mending-enchantments-together.patch +++ b/patches/1-21-1/dropped-server/0094-Allow-infinite-and-mending-enchantments-together.patch @@ -5,19 +5,19 @@ Subject: [PATCH] Allow infinite and mending enchantments together diff --git a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java -index 518d85a13c37a2f7d32ca0718323181048559986..2c4ce164ab3011f372ff1719c8d4a3331d8db55f 100644 +index 81cc05c929d612898609965d82454b89cd18f9f5..fa73c3d4b58ad8379963a9866d8f09e1d6abd663 100644 --- a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java +++ b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java -@@ -19,6 +19,6 @@ public class ArrowInfiniteEnchantment extends Enchantment { +@@ -7,6 +7,6 @@ public class ArrowInfiniteEnchantment extends Enchantment { @Override public boolean checkCompatibility(Enchantment other) { -- return other instanceof MendingEnchantment ? false : super.checkCompatibility(other); -+ return other instanceof MendingEnchantment ? org.purpurmc.purpur.PurpurConfig.allowInfinityMending : super.checkCompatibility(other); +- return !(other instanceof MendingEnchantment) && super.checkCompatibility(other); ++ return !(other instanceof MendingEnchantment) && super.checkCompatibility(other) || other instanceof MendingEnchantment && org.purpurmc.purpur.PurpurConfig.allowInfinityMending; // Purpur } } diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6470f383b6f044877f0a4d8c91119d0eae451b8f..de1a9b08946005d1235264c7658b51bbdc2c47c2 100644 +index 2f68cf2fc064a38ca059504a39d563d95d01643e..6d621a93aaf94927fa6c73e649dcdb8bbbaadd2a 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -246,6 +246,16 @@ public class PurpurConfig { diff --git a/patches/1-21-1/dropped-server/0142-Allow-infinity-on-crossbows.patch b/patches/1-21-1/dropped-server/0142-Allow-infinity-on-crossbows.patch new file mode 100644 index 000000000..5f50b3a98 --- /dev/null +++ b/patches/1-21-1/dropped-server/0142-Allow-infinity-on-crossbows.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ben Kerllenevich +Date: Thu, 18 Mar 2021 12:25:29 -0400 +Subject: [PATCH] Allow infinity on crossbows + + +diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java +index 1f52feb5684ee1bab710e1557cf69b43b4d4dfd4..d2bb5c84e1895f28afed03d1868a79338e4f3e58 100644 +--- a/src/main/java/net/minecraft/world/item/CrossbowItem.java ++++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java +@@ -107,7 +107,7 @@ public class CrossbowItem extends ProjectileWeaponItem { + return CrossbowItem.tryLoadProjectiles(shooter, crossbow, true); + } + private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbow, boolean consume) { +- List list = draw(crossbow, shooter.getProjectile(crossbow), shooter, consume); ++ List list = draw(crossbow, shooter.getProjectile(crossbow), shooter, (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY, crossbow) > 0) || consume); + // Paper end - Add EntityLoadCrossbowEvent + if (!list.isEmpty()) { + crossbow.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(list)); +diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +index 0587b0c7f34ae90f0d06f29d58fafbcf5b80ff13..c60d0d1861fd24b74bfa93228357f27d524f4ce7 100644 +--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java ++++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +@@ -284,6 +284,7 @@ public class PurpurConfig { + } + + public static boolean allowInfinityMending = false; ++ public static boolean allowCrossbowInfinity = true; + private static void enchantmentSettings() { + if (version < 5) { + boolean oldValue = getBoolean("settings.enchantment.allow-infinite-and-mending-together", false); +@@ -291,6 +292,7 @@ public class PurpurConfig { + set("settings.enchantment.allow-infinite-and-mending-together", null); + } + allowInfinityMending = getBoolean("settings.enchantment.allow-infinity-and-mending-together", allowInfinityMending); ++ allowCrossbowInfinity = getBoolean("settings.enchantment.allow-infinity-on-crossbow", allowCrossbowInfinity); + } + + public static boolean endermanShortHeight = false; diff --git a/patches/server/0299-Add-mending-multiplier.patch b/patches/1-21-1/dropped-server/0282-Add-mending-multiplier.patch similarity index 87% rename from patches/server/0299-Add-mending-multiplier.patch rename to patches/1-21-1/dropped-server/0282-Add-mending-multiplier.patch index 24d6b6676..53fb2e556 100644 --- a/patches/server/0299-Add-mending-multiplier.patch +++ b/patches/1-21-1/dropped-server/0282-Add-mending-multiplier.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add mending multiplier diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -index b2233635b6acc35ea3668c36c56e57f15420ac62..724bf857bf1b89cb0947b8a82e0ce09a0bec0335 100644 +index 7130d483ccdce26526e715bd7e68d2e896e6215f..40d168d225932717b8ac8bdd27dfe2a202bc2808 100644 --- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java +++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -@@ -359,13 +359,15 @@ public class ExperienceOrb extends Entity { +@@ -372,13 +372,15 @@ public class ExperienceOrb extends Entity { } } @@ -27,7 +27,7 @@ index b2233635b6acc35ea3668c36c56e57f15420ac62..724bf857bf1b89cb0947b8a82e0ce09a public int getValue() { return this.value; diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9365469584fd647dbc52176efb8c282c21dd28d6..76216ce3a481b7430bd5a3d60bb41798216fa692 100644 +index 02b8333222668c3419296ec5513c1c4e8e8d1a79..3c614b8f62c0d3839ebc4e948c952d52c4f66819 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -119,6 +119,7 @@ public class PurpurWorldConfig { @@ -38,7 +38,7 @@ index 9365469584fd647dbc52176efb8c282c21dd28d6..76216ce3a481b7430bd5a3d60bb41798 public boolean alwaysTameInCreative = false; public boolean boatEjectPlayersOnLand = false; public boolean boatsDoFallDamage = false; -@@ -146,6 +147,7 @@ public class PurpurWorldConfig { +@@ -147,6 +148,7 @@ public class PurpurWorldConfig { public int mobLastHurtByPlayerTime = 100; private void miscGameplayMechanicsSettings() { useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); diff --git a/patches/1-21-3/dropped-server/0224-Shears-can-have-looting-enchantment.patch b/patches/1-21-3/dropped-server/0224-Shears-can-have-looting-enchantment.patch new file mode 100644 index 000000000..c68a2bdb9 --- /dev/null +++ b/patches/1-21-3/dropped-server/0224-Shears-can-have-looting-enchantment.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Mon, 3 Jan 2022 00:06:51 -0600 +Subject: [PATCH] Shears can have looting enchantment + + +diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +index 44b79a7c2f8b95a484d1999fa2167ce588f7985b..68632372c8704058f35f12e0ae6cdd98ebd55937 100644 +--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +@@ -104,7 +104,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior { + if (ishearable.readyForShearing()) { + // CraftBukkit start + // Paper start - Add drops to shear events +- org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops()); ++ org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops(net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.LOOTING, CraftItemStack.asNMSCopy(craftItem)))); // Purpur + if (event.isCancelled()) { + // Paper end - Add drops to shear events + continue; +diff --git a/src/main/java/net/minecraft/world/entity/Shearable.java b/src/main/java/net/minecraft/world/entity/Shearable.java +index 2ee48ac3b665db2b02bcb1a30ec972d43a3725b0..59e8f5431ce5026209e1428b5fa5b5485dcfebc7 100644 +--- a/src/main/java/net/minecraft/world/entity/Shearable.java ++++ b/src/main/java/net/minecraft/world/entity/Shearable.java +@@ -8,7 +8,7 @@ public interface Shearable { + + boolean readyForShearing(); + // Paper start - custom shear drops; ensure all implementing entities override this +- default java.util.List generateDefaultDrops() { ++ default java.util.List generateDefaultDrops(int looting) { // Purpur + return java.util.Collections.emptyList(); + } + // Paper end - custom shear drops +diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java +index 06fac8dae42451f912c2db14d792461cee3dba83..e5a84dd5f1a375dd44b9d14dc7f44339bd8ddf3a 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java +@@ -161,7 +161,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder drops = this.generateDefaultDrops(); ++ java.util.List drops = this.generateDefaultDrops(net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.LOOTING, itemstack)); // Purpur + org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); + if (event != null) { + if (event.isCancelled()) { +@@ -209,13 +209,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder generateDefaultDrops() { ++ public java.util.List generateDefaultDrops(int looting) { // Purpur + java.util.List dropEntities = new java.util.ArrayList<>(5); +- for (int i = 0; i < 5; ++i) { ++ for (int i = 0; i < 5 + (org.purpurmc.purpur.PurpurConfig.allowShearsLooting ? looting : 0); ++i) { // Purpur + dropEntities.add(new ItemStack(this.getVariant().getBlockState().getBlock())); + } + return dropEntities; +diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java +index ca63c98bc45584812c0fb2af84a63aa08daa9a9e..17b49186293578c06144a476473324a9a1f6fcbb 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java +@@ -296,7 +296,7 @@ public class Sheep extends Animal implements Shearable { + if (!this.level().isClientSide && this.readyForShearing()) { + // CraftBukkit start + // Paper start - custom shear drops +- java.util.List drops = this.generateDefaultDrops(); ++ java.util.List drops = this.generateDefaultDrops(net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.LOOTING, itemstack)); // Purpur + org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); + if (event != null) { + if (event.isCancelled()) { +@@ -321,12 +321,13 @@ public class Sheep extends Animal implements Shearable { + @Override + public void shear(SoundSource shearedSoundCategory) { + // Paper start - custom shear drops +- this.shear(shearedSoundCategory, this.generateDefaultDrops()); ++ this.shear(shearedSoundCategory, this.generateDefaultDrops(0)); // Purpur + } + + @Override +- public java.util.List generateDefaultDrops() { ++ public java.util.List generateDefaultDrops(int looting) { // Purpur + int count = 1 + this.random.nextInt(3); ++ if (org.purpurmc.purpur.PurpurConfig.allowShearsLooting) count += looting; // Purpur + java.util.List dropEntities = new java.util.ArrayList<>(count); + for (int j = 0; j < count; ++j) { + dropEntities.add(new ItemStack(Sheep.ITEM_BY_DYE.get(this.getColor()))); +diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java +index b3bb0d55da0278d4981830f7073bb326bc836215..7bd2d2b9bb1275fa8e4bdc6d498a59e47838930f 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java ++++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java +@@ -190,7 +190,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { + // CraftBukkit start + // Paper start - custom shear drops +- java.util.List drops = this.generateDefaultDrops(); ++ java.util.List drops = this.generateDefaultDrops(net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.LOOTING, itemstack)); // Purpur + org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); + if (event != null) { + if (event.isCancelled()) { +@@ -223,11 +223,20 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + @Override + public void shear(SoundSource shearedSoundCategory) { + // Paper start - custom shear drops +- this.shear(shearedSoundCategory, this.generateDefaultDrops()); ++ this.shear(shearedSoundCategory, this.generateDefaultDrops(0)); // Purpur + } + + @Override +- public java.util.List generateDefaultDrops() { ++ // Purpur start ++ public java.util.List generateDefaultDrops(int looting) { ++ if (org.purpurmc.purpur.PurpurConfig.allowShearsLooting) { ++ java.util.ArrayList list = new java.util.ArrayList<>(); ++ for (int i = 0; i < 1 + looting; i++) { ++ list.add(new ItemStack(Items.CARVED_PUMPKIN)); ++ } ++ return java.util.Collections.unmodifiableList(list); ++ } ++ // Purpur end + return java.util.Collections.singletonList(new ItemStack(Items.CARVED_PUMPKIN)); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Bogged.java b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +index 71efea7a6bfb5662890dae5faae27a3c80afad2b..f3aae2c50d05a05ad4aef36d432b87b431ba07e8 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +@@ -104,7 +104,7 @@ public class Bogged extends AbstractSkeleton implements Shearable { + if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { + // CraftBukkit start + // Paper start - expose drops in event +- java.util.List drops = generateDefaultDrops(); ++ java.util.List drops = generateDefaultDrops(net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.LOOTING, itemstack)); // Purpur + final org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops); + if (event != null) { + if (event.isCancelled()) { +@@ -171,7 +171,7 @@ public class Bogged extends AbstractSkeleton implements Shearable { + @Override + public void shear(SoundSource shearedSoundCategory) { + // Paper start - shear drop API +- this.shear(shearedSoundCategory, generateDefaultDrops()); ++ this.shear(shearedSoundCategory, generateDefaultDrops(0)); // Purpur + } + + @Override +@@ -184,7 +184,7 @@ public class Bogged extends AbstractSkeleton implements Shearable { + + private void spawnShearedMushrooms() { + // Paper start - shear drops API +- this.spawnDrops(generateDefaultDrops()); // Only here for people calling spawnSheardMushrooms. Not used otherwise. ++ this.spawnDrops(generateDefaultDrops(0)); // Only here for people calling spawnSheardMushrooms. Not used otherwise. // Purpur + } + private void spawnDrops(java.util.List drops) { + drops.forEach(stack -> { +@@ -193,14 +193,22 @@ public class Bogged extends AbstractSkeleton implements Shearable { + this.forceDrops = false; + }); + } +- private void generateShearedMushrooms(java.util.function.Consumer stackConsumer) { ++ private void generateShearedMushrooms(java.util.function.Consumer stackConsumer, int looting) { // Purpur + // Paper end - shear drops API + Level world = this.level(); + + if (world instanceof ServerLevel worldserver) { + LootTable loottable = worldserver.getServer().reloadableRegistries().getLootTable(BuiltInLootTables.BOGGED_SHEAR); + LootParams lootparams = (new LootParams.Builder(worldserver)).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.THIS_ENTITY, this).create(LootContextParamSets.SHEARING); +- ObjectListIterator objectlistiterator = loottable.getRandomItems(lootparams).iterator(); ++ // Purpur start ++ it.unimi.dsi.fastutil.objects.ObjectArrayList randomItemsList = loottable.getRandomItems(lootparams); ++ if (org.purpurmc.purpur.PurpurConfig.allowShearsLooting && looting > 0) { ++ for (int i = 0; i < looting; i++) { ++ randomItemsList.addAll(loottable.getRandomItems(lootparams)); ++ } ++ } ++ ObjectListIterator objectlistiterator = randomItemsList.iterator(); ++ // Purpur end + + while (objectlistiterator.hasNext()) { + ItemStack itemstack = (ItemStack) objectlistiterator.next(); +@@ -213,9 +221,9 @@ public class Bogged extends AbstractSkeleton implements Shearable { + + // Paper start - shear drops API + @Override +- public java.util.List generateDefaultDrops() { ++ public java.util.List generateDefaultDrops(int looting) { // Purpur + final java.util.List drops = new java.util.ArrayList<>(); +- this.generateShearedMushrooms(drops::add); ++ this.generateShearedMushrooms(drops::add, looting); // Purpur + return drops; + } + // Paper end - shear drops API +diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +index 7ad309e81ec61a6f2553e9ffeb9a986f4d569b37..3d5c89238ee3fbd3c9b33107995ae47733539960 100644 +--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java ++++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +@@ -393,6 +393,7 @@ public class PurpurConfig { + } + } + ++ public static boolean allowShearsLooting = false; + public static boolean allowInapplicableEnchants = false; + public static boolean allowIncompatibleEnchants = false; + public static boolean allowHigherEnchantsLevels = false; +@@ -416,6 +417,7 @@ public class PurpurConfig { + } + set("settings.enchantment.anvil.allow-unsafe-enchants", null); + } ++ allowShearsLooting = getBoolean("settings.enchantment.allow-looting-on-shears", allowShearsLooting); + allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants); + allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants); + allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels); diff --git a/patches/1-21-3/dropped-server/0247-Remove-Mojang-Profiler.patch b/patches/1-21-3/dropped-server/0247-Remove-Mojang-Profiler.patch new file mode 100644 index 000000000..6bd55ce39 --- /dev/null +++ b/patches/1-21-3/dropped-server/0247-Remove-Mojang-Profiler.patch @@ -0,0 +1,2108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Sat, 16 Jul 2022 21:37:10 -0500 +Subject: [PATCH] Remove Mojang Profiler + + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index d8ee4422a13c7f09b84e9bbe8b57f0c139caac32..0ba926b6ff0bd54159765cc7f37d1753ded89dee 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -169,7 +169,7 @@ public class Commands { + DamageCommand.register(this.dispatcher, commandRegistryAccess); + DataCommands.register(this.dispatcher); + DataPackCommand.register(this.dispatcher); +- DebugCommand.register(this.dispatcher); ++ //DebugCommand.register(this.dispatcher); // Purpur + DefaultGameModeCommands.register(this.dispatcher); + DifficultyCommand.register(this.dispatcher); + EffectCommands.register(this.dispatcher, commandRegistryAccess); +@@ -351,9 +351,9 @@ public class Commands { + // Paper end + CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); + +- Profiler.get().push(() -> { ++ /*Profiler.get().push(() -> { // Purpur + return "/" + s; +- }); ++ });*/ // Purpur + ContextChain contextchain = this.finishParsing(parseresults, s, commandlistenerwrapper, label); // CraftBukkit // Paper - Add UnknownCommandEvent + + try { +@@ -383,7 +383,7 @@ public class Commands { + Commands.LOGGER.error("'/{}' threw an exception", s, exception); + } + } finally { +- Profiler.get().pop(); ++ //Profiler.get().pop(); // Purpur + } + + } +diff --git a/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java +index 9f5c3ec2eae9b30bdb8dbcb328d7f701cb7aeb9d..e7cc8105fff9cb952eabfd006e0a4e4638091019 100644 +--- a/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java ++++ b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java +@@ -42,7 +42,7 @@ public class BuildContexts> { + ChainModifiers chainModifiers = flags; + List list = sources; + if (contextChain.getStage() != Stage.EXECUTE) { +- context.profiler().push(() -> "prepare " + this.commandInput); ++ //context.profiler().push(() -> "prepare " + this.commandInput); // Purpur + + try { + for (int i = context.forkLimit(); contextChain.getStage() != Stage.EXECUTE; contextChain = contextChain.nextStage()) { +@@ -86,7 +86,7 @@ public class BuildContexts> { + } + } + } finally { +- context.profiler().pop(); ++ //context.profiler().pop(); // Purpur + } + } + +diff --git a/src/main/java/net/minecraft/commands/execution/tasks/ExecuteCommand.java b/src/main/java/net/minecraft/commands/execution/tasks/ExecuteCommand.java +index e9775b4506909bee65a74964f0d5391a0513de1d..684f7f202305c09b1037c5d38a52a5ea7f00751b 100644 +--- a/src/main/java/net/minecraft/commands/execution/tasks/ExecuteCommand.java ++++ b/src/main/java/net/minecraft/commands/execution/tasks/ExecuteCommand.java +@@ -23,7 +23,7 @@ public class ExecuteCommand> implements Unbo + + @Override + public void execute(T executionCommandSource, ExecutionContext executionContext, Frame frame) { +- executionContext.profiler().push(() -> "execute " + this.commandInput); ++ //executionContext.profiler().push(() -> "execute " + this.commandInput); // Purpur + + try { + executionContext.incrementCost(); +@@ -37,7 +37,7 @@ public class ExecuteCommand> implements Unbo + } catch (CommandSyntaxException var9) { + executionCommandSource.handleError(var9, this.modifiers.isForked(), executionContext.tracer()); + } finally { +- executionContext.profiler().pop(); ++ //executionContext.profiler().pop(); // Purpur + } + } + } +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 2065f03b70bc77fffd8bac4fab6efc89d598f8b4..cb18ba70ea12785a95de90e4f9d1533ab755a576 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -431,12 +431,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { ++ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur ++ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur + this.stopRecordingMetrics(); +- }; +- this.onMetricsRecordingFinished = (path) -> { +- }; ++ };*/ // Purpur ++ //this.onMetricsRecordingFinished = (path) -> { // Purpur ++ //}; // Purpur + this.random = RandomSource.create(); + this.port = -1; + this.levels = Maps.newLinkedHashMap(); +@@ -1050,9 +1050,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + return false; +@@ -1365,7 +1365,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Purpur + +- gameprofilerfiller.push(() -> { ++ /*gameprofilerfiller.push(() -> { // Purpur + String s = String.valueOf(worldserver); + + return s + " " + String.valueOf(worldserver.dimension().location()); +- }); ++ });*/ // Purpur + /* Drop global time updates + if (this.tickCount % 20 == 0) { +- gameprofilerfiller.push("timeSync"); ++ //gameprofilerfiller.push("timeSync"); // Purpur + this.synchronizeTime(worldserver); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + // CraftBukkit end */ + +- gameprofilerfiller.push("tick"); ++ //gameprofilerfiller.push("tick"); // Purpur + + try { + worldserver.tick(shouldKeepTicking); +@@ -1912,26 +1912,26 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + this.executeBlocking(() -> { + this.saveDebugReport(path.resolve("server")); +@@ -2927,37 +2927,38 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resultConsumer, Consumer dumpConsumer) { +- this.onMetricsRecordingStopped = (methodprofilerresults) -> { ++ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur + this.stopRecordingMetrics(); + resultConsumer.accept(methodprofilerresults); + }; + this.onMetricsRecordingFinished = dumpConsumer; +- this.willStartRecordingMetrics = true; ++ this.willStartRecordingMetrics = true;*/ // Purpur + } + + public void stopRecordingMetrics() { +- this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; ++ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur + } + + public void finishRecordingMetrics() { +- this.metricsRecorder.end(); ++ //this.metricsRecorder.end(); // Purpur + } + + public void cancelRecordingMetrics() { +- this.metricsRecorder.cancel(); ++ //this.metricsRecorder.cancel(); // Purpur + } + + public Path getWorldPath(LevelResource worldSavePath) { +@@ -3010,15 +3011,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> functions, ResourceLocation label) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + + Objects.requireNonNull(label); +- gameprofilerfiller.push(label::toString); ++ //gameprofilerfiller.push(label::toString); // Purpur + Iterator iterator = functions.iterator(); + + while (iterator.hasNext()) { +@@ -66,15 +66,15 @@ public class ServerFunctionManager { + this.execute(commandfunction, this.getGameLoopSender()); + } + +- Profiler.get().pop(); ++ //Profiler.get().pop(); // Purpur + } + + public void execute(CommandFunction function, CommandSourceStack source) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push(() -> { ++ /*gameprofilerfiller.push(() -> { // Purpur + return "function " + String.valueOf(function.id()); +- }); ++ });*/ // Purpur + + try { + InstantiatedFunction instantiatedfunction = function.instantiate((CompoundTag) null, this.getDispatcher()); +@@ -87,7 +87,7 @@ public class ServerFunctionManager { + } catch (Exception exception) { + ServerFunctionManager.LOGGER.warn("Failed to execute function {}", function.id(), exception); + } finally { +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + } +diff --git a/src/main/java/net/minecraft/server/commands/PerfCommand.java b/src/main/java/net/minecraft/server/commands/PerfCommand.java +index 8c587f829c5e8c6b6df3150024c4ae704988c47b..8ac4d5dbe7f8febf4226f26a6b035282dcdf1b0f 100644 +--- a/src/main/java/net/minecraft/server/commands/PerfCommand.java ++++ b/src/main/java/net/minecraft/server/commands/PerfCommand.java +@@ -42,6 +42,7 @@ public class PerfCommand { + } + + private static int startProfilingDedicatedServer(CommandSourceStack source) throws CommandSyntaxException { ++ if (true) return removedMessage(source); // Purpur + MinecraftServer minecraftServer = source.getServer(); + if (minecraftServer.isRecordingMetrics()) { + throw ERROR_ALREADY_RUNNING.create(); +@@ -55,6 +56,7 @@ public class PerfCommand { + } + + private static int stopProfilingDedicatedServer(CommandSourceStack source) throws CommandSyntaxException { ++ if (true) return removedMessage(source); // Purpur + MinecraftServer minecraftServer = source.getServer(); + if (!minecraftServer.isRecordingMetrics()) { + throw ERROR_NOT_RUNNING.create(); +@@ -64,6 +66,18 @@ public class PerfCommand { + } + } + ++ // Purpur start ++ private static int removedMessage(CommandSourceStack source) { ++ if (true) { ++ net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(); ++ source.getSender().sendMessage(mm.deserialize("Purpur has removed Mojang's Profiler to save your performance. Please use /spark instead")); ++ source.getSender().sendMessage(mm.deserialize("For more information, view its documentation at")); ++ source.getSender().sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage")); ++ } ++ return 0; ++ } ++ // Purpur end ++ + private static void saveResults(CommandSourceStack source, Path tempProfilingDirectory, MinecraftServer server) { + String string = String.format( + Locale.ROOT, "%s-%s-%s", Util.getFilenameFormattedDateTime(), server.getWorldData().getLevelName(), SharedConstants.getCurrentVersion().getId() +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 5b3a886c624b36557cbfaccdc3fb05a46a4ba36a..e16f22dd82b4315da34af3c9a189d9d5fec0fd2f 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -406,16 +406,16 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + protected void tick(BooleanSupplier shouldKeepTicking) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("poi"); ++ //gameprofilerfiller.push("poi"); // Purpur + this.poiManager.tick(shouldKeepTicking); +- gameprofilerfiller.popPush("chunk_unload"); ++ //gameprofilerfiller.popPush("chunk_unload"); // Purpur + if (!this.level.noSave()) { + this.processUnloads(shouldKeepTicking); + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + public boolean hasWork() { +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 3c711e1df57ac5b0f8795ebb12299d275792b1d4..bb168636cbf23b5b0c7232529e390f434546dc37 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -442,38 +442,38 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + // CraftBukkit start - modelled on below + public void purgeUnload() { + if (true) return; // Paper - rewrite chunk system +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("purge"); ++ //gameprofilerfiller.push("purge"); // Purpur + this.distanceManager.purgeStaleTickets(); + this.runDistanceManagerUpdates(); +- gameprofilerfiller.popPush("unload"); ++ //gameprofilerfiller.popPush("unload"); // Purpur + this.chunkMap.tick(() -> true); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.clearCache(); + } + // CraftBukkit end + + @Override + public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("purge"); ++ //gameprofilerfiller.push("purge"); // Purpur + if (this.level.tickRateManager().runsNormally() || !tickChunks || this.level.spigotConfig.unloadFrozenChunks) { // Spigot + this.distanceManager.purgeStaleTickets(); + } + + this.runDistanceManagerUpdates(); +- gameprofilerfiller.popPush("chunks"); ++ //gameprofilerfiller.popPush("chunks"); // Purpur + if (tickChunks) { + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().tick(); // Paper - rewrite chunk system + this.tickChunks(); + this.chunkMap.tick(); + } + +- gameprofilerfiller.popPush("unload"); ++ //gameprofilerfiller.popPush("unload"); // Purpur + this.chunkMap.tick(shouldKeepTicking); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.clearCache(); + } + +@@ -483,34 +483,34 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + + this.lastInhabitedUpdate = i; + if (!this.level.isDebug()) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("pollingChunks"); ++ //gameprofilerfiller.push("pollingChunks"); // Purpur + if (this.level.tickRateManager().runsNormally()) { + List list = this.tickingChunks; + + try { +- gameprofilerfiller.push("filteringTickingChunks"); ++ //gameprofilerfiller.push("filteringTickingChunks"); // Purpur + this.collectTickingChunks(list); +- gameprofilerfiller.popPush("shuffleChunks"); ++ //gameprofilerfiller.popPush("shuffleChunks"); // Purpur + // Paper start - chunk tick iteration optimisation + this.shuffleRandom.setSeed(this.level.random.nextLong()); + if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled + // Paper end - chunk tick iteration optimisation +- this.tickChunks(gameprofilerfiller, j, list); +- gameprofilerfiller.pop(); ++ this.tickChunks(null, j, list); // Purpur ++ //gameprofilerfiller.pop(); // Purpur + } finally { + list.clear(); + } + } + +- this.broadcastChangedChunks(gameprofilerfiller); +- gameprofilerfiller.pop(); ++ this.broadcastChangedChunks(null); // Purpur ++ //gameprofilerfiller.pop(); // Purpur + } + } + +- private void broadcastChangedChunks(ProfilerFiller profiler) { +- profiler.push("broadcast"); ++ private void broadcastChangedChunks(ProfilerFiller profiler) { // Purpur ++ //profiler.push("broadcast"); // Purpur + Iterator iterator = this.chunkHoldersToBroadcast.iterator(); + + while (iterator.hasNext()) { +@@ -523,7 +523,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + + this.chunkHoldersToBroadcast.clear(); +- profiler.pop(); ++ //profiler.pop(); // Purpur + } + + private void collectTickingChunks(List chunks) { +@@ -550,7 +550,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + + private void tickChunks(ProfilerFiller profiler, long timeDelta, List chunks) { +- profiler.popPush("naturalSpawnCount"); ++ //profiler.popPush("naturalSpawnCount"); // Purpur + int j = this.distanceManager.getNaturalSpawnChunkCount(); + // Paper start - Optional per player mob spawns + final int naturalSpawnChunkCount = j; +@@ -577,7 +577,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + // Paper end - Optional per player mob spawns + + this.lastSpawnState = spawnercreature_d; +- profiler.popPush("spawnAndTick"); ++ //profiler.popPush("spawnAndTick"); // Purpur + boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit + int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); + List list1; +@@ -615,7 +615,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + } + +- profiler.popPush("customSpawners"); ++ //profiler.popPush("customSpawners"); // Purpur + if (flag) { + this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); + } +@@ -813,7 +813,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + + @Override + protected void doRunTask(Runnable task) { +- Profiler.get().incrementCounter("runTask"); ++ //Profiler.get().incrementCounter("runTask"); // Purpur + super.doRunTask(task); + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index a8650040b69fe92f18606e5029ecd881961b39e7..4c11f0a23ee897691d8a8022baa134ef6f5e3d89 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -724,18 +724,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + public void tick(BooleanSupplier shouldKeepTicking) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + + this.handlingTick = true; + TickRateManager tickratemanager = this.tickRateManager(); + boolean flag = tickratemanager.runsNormally(); + + if (flag) { +- gameprofilerfiller.push("world border"); ++ //gameprofilerfiller.push("world border"); // Purpur + this.getWorldBorder().tick(); +- gameprofilerfiller.popPush("weather"); ++ //gameprofilerfiller.popPush("weather"); // Purpur + this.advanceWeatherCycle(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); +@@ -766,30 +766,30 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.tickTime(); + } + +- gameprofilerfiller.push("tickPending"); ++ //gameprofilerfiller.push("tickPending"); // Purpur + if (!this.isDebug() && flag) { + j = this.getGameTime(); +- gameprofilerfiller.push("blockTicks"); ++ //gameprofilerfiller.push("blockTicks"); // Purpur + this.blockTicks.tick(j, paperConfig().environment.maxBlockTicks, this::tickBlock); // Paper - configurable max block ticks +- gameprofilerfiller.popPush("fluidTicks"); ++ //gameprofilerfiller.popPush("fluidTicks"); // Purpur + this.fluidTicks.tick(j, paperConfig().environment.maxFluidTicks, this::tickFluid); // Paper - configurable max fluid ticks +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + +- gameprofilerfiller.popPush("raid"); ++ //gameprofilerfiller.popPush("raid"); // Purpur + if (flag) { + this.raids.tick(); + } + +- gameprofilerfiller.popPush("chunkSource"); ++ //gameprofilerfiller.popPush("chunkSource"); // Purpur + this.getChunkSource().tick(shouldKeepTicking, true); +- gameprofilerfiller.popPush("blockEvents"); ++ //gameprofilerfiller.popPush("blockEvents"); // Purpur + if (flag) { + this.runBlockEvents(); + } + + this.handlingTick = false; +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + boolean flag1 = !paperConfig().unsupportedSettings.disableWorldTickingWhenEmpty || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players // Paper - restore this + + if (flag1) { +@@ -797,20 +797,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + if (flag1 || this.emptyTime++ < 300) { +- gameprofilerfiller.push("entities"); ++ //gameprofilerfiller.push("entities"); // Purpur + if (this.dragonFight != null && flag) { +- gameprofilerfiller.push("dragonFight"); ++ //gameprofilerfiller.push("dragonFight"); // Purpur + this.dragonFight.tick(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + this.entityTickList.forEach((entity) -> { + if (!entity.isRemoved()) { + if (!tickratemanager.isEntityFrozen(entity)) { +- gameprofilerfiller.push("checkDespawn"); ++ //gameprofilerfiller.push("checkDespawn"); // Purpur + entity.checkDespawn(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + if (true) { // Paper - rewrite chunk system + Entity entity1 = entity.getVehicle(); + +@@ -822,20 +822,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + entity.stopRiding(); + } + +- gameprofilerfiller.push("tick"); ++ //gameprofilerfiller.push("tick"); // Purpur + this.guardEntityTick(this::tickNonPassenger, entity); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + } + } + }); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.tickBlockEntities(); + } + +- gameprofilerfiller.push("entityManagement"); ++ //gameprofilerfiller.push("entityManagement"); // Purpur + // Paper - rewrite chunk system +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + @Override +@@ -962,9 +962,9 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + boolean flag = this.isRaining(); + int j = chunkcoordintpair.getMinBlockX(); + int k = chunkcoordintpair.getMinBlockZ(); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("thunder"); ++ //gameprofilerfiller.push("thunder"); // Purpur + if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && simpleRandom.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder // Paper - optimise random ticking + BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); + +@@ -1001,7 +1001,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + } + +- gameprofilerfiller.popPush("iceandsnow"); ++ //gameprofilerfiller.popPush("iceandsnow"); // Purpur + + if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow + for (int l = 0; l < randomTickSpeed; ++l) { +@@ -1011,12 +1011,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + } // Paper - Option to disable ice and snow + +- gameprofilerfiller.popPush("tickBlocks"); ++ //gameprofilerfiller.popPush("tickBlocks"); // Purpur + if (randomTickSpeed > 0) { + this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + @VisibleForTesting +@@ -1355,18 +1355,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Spigot end + final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); // Paper - EAR 2 + entity.setOldPosAndRot(); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + + ++entity.tickCount; +- gameprofilerfiller.push(() -> { ++ /*gameprofilerfiller.push(() -> { // Purpur + return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString(); +- }); +- gameprofilerfiller.incrementCounter("tickNonPassenger"); ++ });*/ // Purpur ++ //gameprofilerfiller.incrementCounter("tickNonPassenger"); // Purpur + if (isActive) { // Paper - EAR 2 + entity.tick(); + entity.postTick(); // CraftBukkit + } else { entity.inactiveTick(); } // Paper - EAR 2 +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + Iterator iterator = entity.getPassengers().iterator(); + + while (iterator.hasNext()) { +@@ -1389,12 +1389,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + if (passenger instanceof Player || this.entityTickList.contains(passenger)) { + passenger.setOldPosAndRot(); + ++passenger.tickCount; +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push(() -> { ++ /*gameprofilerfiller.push(() -> { // Purpur + return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString(); +- }); +- gameprofilerfiller.incrementCounter("tickPassenger"); ++ });*/ // Purpur ++ //gameprofilerfiller.incrementCounter("tickPassenger"); // Purpur + // Paper start - EAR 2 + if (isActive) { + passenger.rideTick(); +@@ -1406,7 +1406,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + vehicle.positionRider(passenger); + } + // Paper end - EAR 2 +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + Iterator iterator = passenger.getPassengers().iterator(); + + while (iterator.hasNext()) { +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 51229d600ea8a58e5c75bd97a1926f5cdf3704d4..0286b1bd461a5050cb78b9485bac84909ae91150 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1669,15 +1669,15 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple + this.unsetRemoved(); + */ + // CraftBukkit end +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("moving"); ++ //gameprofilerfiller.push("moving"); // Purpur + if (worldserver != null && resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit - empty to fall through to null to event + this.enteredNetherPosition = this.position(); + } + +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("placing"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("placing"); // Purpur + // CraftBukkit start + this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds + LevelData worlddata = worldserver.getLevelData(); +@@ -1695,7 +1695,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple + this.connection.internalTeleport(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives()); // CraftBukkit - use internal teleport without event + this.connection.resetPosition(); + worldserver.addDuringTeleport(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.triggerDimensionChangeTriggers(worldserver1); + this.stopUsingItem(); + this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities())); +diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index 7d276c191b391bca24948ddb36b8b7d0f1f03b03..49cb116fd55e6d5cd36b9773b39191e4ab06b7e0 100644 +--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -274,7 +274,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + + protected void keepConnectionAlive() { +- Profiler.get().push("keepAlive"); ++ //Profiler.get().push("keepAlive"); // Purpur + // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings + // This should effectively place the keepalive handling back to "as it was" before 1.12.2 + long currentTime = Util.getMillis(); +@@ -307,7 +307,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings + +- Profiler.get().pop(); ++ //Profiler.get().pop(); // Purpur + } + + private boolean checkIfClosed(long time) { +diff --git a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java +index d2d82e4f22bfeac8881b6815e4bef56c254fded9..abc92e09b7bb636612f04ace8232947c8d454e68 100644 +--- a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java ++++ b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java +@@ -12,10 +12,10 @@ public interface ResourceManagerReloadListener extends PreparableReloadListener + PreparableReloadListener.PreparationBarrier synchronizer, ResourceManager manager, Executor prepareExecutor, Executor applyExecutor + ) { + return synchronizer.wait(Unit.INSTANCE).thenRunAsync(() -> { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("listener"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("listener"); // Purpur + this.onResourceManagerReload(manager); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + }, applyExecutor); + } + +diff --git a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java +index bce2dac613d29083dd5fbb68739304cc5a6d4d27..600a7036b503f60cc9c95f189f73c2dbf020e2e1 100644 +--- a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java ++++ b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java +@@ -55,7 +55,7 @@ public class ActiveProfiler implements ProfileCollector { + this.started = true; + this.path = ""; + this.paths.clear(); +- this.push("root"); ++ //this.push("root"); // Purpur + } + } + +@@ -64,7 +64,7 @@ public class ActiveProfiler implements ProfileCollector { + if (!this.started) { + LOGGER.error("Profiler tick already ended - missing startTick()?"); + } else { +- this.pop(); ++ //this.pop(); // Purpur + this.started = false; + if (!this.path.isEmpty()) { + LOGGER.error( +@@ -93,7 +93,7 @@ public class ActiveProfiler implements ProfileCollector { + + @Override + public void push(Supplier locationGetter) { +- this.push(locationGetter.get()); ++ //this.push(locationGetter.get()); // Purpur + } + + @Override +@@ -132,14 +132,14 @@ public class ActiveProfiler implements ProfileCollector { + + @Override + public void popPush(String location) { +- this.pop(); +- this.push(location); ++ //this.pop(); // Purpur ++ //this.push(location); // Purpur + } + + @Override + public void popPush(Supplier locationGetter) { +- this.pop(); +- this.push(locationGetter); ++ //this.pop(); // Purpur ++ //this.push(locationGetter); // Purpur + } + + private ActiveProfiler.PathEntry getCurrentEntry() { +diff --git a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java +index bc5c8879befe849ce81becf5e3fba6757b01cb70..ce81d6bd87f688a24003f2fbf6d5010ad6273917 100644 +--- a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java ++++ b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java +@@ -6,51 +6,68 @@ import net.minecraft.util.profiling.metrics.MetricCategory; + public interface ProfilerFiller { + String ROOT = "root"; + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void startTick(); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void endTick(); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void push(String location); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void push(Supplier locationGetter); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void pop(); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void popPush(String location); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void popPush(Supplier locationGetter); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default void addZoneText(String label) { + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default void addZoneValue(long value) { + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default void setZoneColor(int color) { + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default Zone zone(String name) { + this.push(name); + return new Zone(this); + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default Zone zone(Supplier nameSupplier) { + this.push(nameSupplier); + return new Zone(this); + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void markForCharting(MetricCategory type); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default void incrementCounter(String marker) { +- this.incrementCounter(marker, 1); ++ //this.incrementCounter(marker, 1); // Purpur + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void incrementCounter(String marker, int num); + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + default void incrementCounter(Supplier markerGetter) { +- this.incrementCounter(markerGetter, 1); ++ //this.incrementCounter(markerGetter, 1); // Purpur + } + ++ @io.papermc.paper.annotation.DoNotUse // Purpur + void incrementCounter(Supplier markerGetter, int num); + + static ProfilerFiller combine(ProfilerFiller first, ProfilerFiller second) { +@@ -72,80 +89,80 @@ public interface ProfilerFiller { + + @Override + public void startTick() { +- this.first.startTick(); +- this.second.startTick(); ++ //this.first.startTick(); // Purpur ++ //this.second.startTick(); // Purpur + } + + @Override + public void endTick() { +- this.first.endTick(); +- this.second.endTick(); ++ //this.first.endTick(); // Purpur ++ //this.second.endTick(); // Purpur + } + + @Override + public void push(String location) { +- this.first.push(location); +- this.second.push(location); ++ //this.first.push(location); // Purpur ++ //this.second.push(location); // Purpur + } + + @Override + public void push(Supplier locationGetter) { +- this.first.push(locationGetter); +- this.second.push(locationGetter); ++ //this.first.push(locationGetter); // Purpur ++ //this.second.push(locationGetter); // Purpur + } + + @Override + public void markForCharting(MetricCategory type) { +- this.first.markForCharting(type); +- this.second.markForCharting(type); ++ //this.first.markForCharting(type); // Purpur ++ //this.second.markForCharting(type); // Purpur + } + + @Override + public void pop() { +- this.first.pop(); +- this.second.pop(); ++ //this.first.pop(); // Purpur ++ //this.second.pop(); // Purpur + } + + @Override + public void popPush(String location) { +- this.first.popPush(location); +- this.second.popPush(location); ++ //this.first.popPush(location); // Purpur ++ //this.second.popPush(location); // Purpur + } + + @Override + public void popPush(Supplier locationGetter) { +- this.first.popPush(locationGetter); +- this.second.popPush(locationGetter); ++ //this.first.popPush(locationGetter); // Purpur ++ //this.second.popPush(locationGetter); // Purpur + } + + @Override + public void incrementCounter(String marker, int num) { +- this.first.incrementCounter(marker, num); +- this.second.incrementCounter(marker, num); ++ //this.first.incrementCounter(marker, num); // Purpur ++ //this.second.incrementCounter(marker, num); // Purpur + } + + @Override + public void incrementCounter(Supplier markerGetter, int num) { +- this.first.incrementCounter(markerGetter, num); +- this.second.incrementCounter(markerGetter, num); ++ //this.first.incrementCounter(markerGetter, num); // Purpur ++ //this.second.incrementCounter(markerGetter, num); // Purpur + } + + @Override + public void addZoneText(String label) { +- this.first.addZoneText(label); +- this.second.addZoneText(label); ++ //this.first.addZoneText(label); // Purpur ++ //this.second.addZoneText(label); // Purpur + } + + @Override + public void addZoneValue(long value) { +- this.first.addZoneValue(value); +- this.second.addZoneValue(value); ++ //this.first.addZoneValue(value); // Purpur ++ //this.second.addZoneValue(value); // Purpur + } + + @Override + public void setZoneColor(int color) { +- this.first.setZoneColor(color); +- this.second.setZoneColor(color); ++ //this.first.setZoneColor(color); // Purpur ++ //this.second.setZoneColor(color); // Purpur + } + } + } +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 520808a1238b28ec42f261b58b2b768cdb1d8277..1e740178898720cffff0a18255ebb9b46265f45f 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -946,9 +946,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // CraftBukkit end + + public void baseTick() { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("entityBaseTick"); ++ //gameprofilerfiller.push("entityBaseTick"); // Purpur + if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Prevent entity loading causing async lookups + this.inBlockState = null; + if (this.isPassenger() && this.getVehicle().isRemoved()) { +@@ -1017,7 +1017,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + public void setSharedFlagOnFire(boolean onFire) { +@@ -1243,9 +1243,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("move"); ++ //gameprofilerfiller.push("move"); // Purpur + if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) { + movement = movement.multiply(this.stuckSpeedMultiplier); + this.stuckSpeedMultiplier = Vec3.ZERO; +@@ -1254,7 +1254,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // Paper start - ignore movement changes while inactive. + if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && type == MoverType.SELF) { + setDeltaMovement(Vec3.ZERO); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + return; + } + // Paper end +@@ -1275,8 +1275,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z); + } + +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("rest"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("rest"); // Purpur + boolean flag = !Mth.equal(movement.x, vec3d1.x); + boolean flag1 = !Mth.equal(movement.z, vec3d1.z); + +@@ -1298,7 +1298,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + if (this.isRemoved()) { +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } else { + if (this.horizontalCollision) { + Vec3 vec3d2 = this.getDeltaMovement(); +@@ -1347,7 +1347,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + float f = this.getBlockSpeedFactor(); + + this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f)); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + } + // Paper start - detailed watchdog information +@@ -3512,9 +3512,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.processPortalCooldown(); + if (this.portalProcess != null) { + if (this.portalProcess.processPortalTeleportation(worldserver, this, this.canUsePortal(false))) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("portal"); ++ //gameprofilerfiller.push("portal"); // Purpur + this.setPortalCooldown(); + TeleportTransition teleporttransition = this.portalProcess.getPortalDestination(worldserver, this); + +@@ -3526,7 +3526,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } else if (this.portalProcess.hasExpired()) { + this.portalProcess = null; + } +@@ -4079,12 +4079,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("teleportCrossDimension"); ++ //gameprofilerfiller.push("teleportCrossDimension"); // Purpur + entity = this.getType().create(world, EntitySpawnReason.DIMENSION_TRAVEL); + if (entity == null) { +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + return null; + } else { + // Paper start - Fix item duplication and teleport issues +@@ -4110,7 +4110,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + world.resetEmptyTime(); + teleportTarget.postTeleportTransition().onTransition(entity); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + return entity; + } + } +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index e2a5d66e4b945f69d9f2b538b95aa0b187749d8b..cd2fd85178acdfffc07b72bc0419a73e1d1bef64 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -457,9 +457,9 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + super.baseTick(); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("livingEntityBaseTick"); ++ //gameprofilerfiller.push("livingEntityBaseTick"); // Purpur + if (this.fireImmune() || this.level().isClientSide) { + this.clearFire(); + } +@@ -567,7 +567,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.yHeadRotO = this.yHeadRot; + this.yRotO = this.getYRot(); + this.xRotO = this.getXRot(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + @Override +@@ -3382,12 +3382,12 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + this.run += (f3 - this.run) * 0.3F; +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("headTurn"); ++ //gameprofilerfiller.push("headTurn"); // Purpur + f2 = this.tickHeadTurn(f1, f2); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("rangeChecks"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("rangeChecks"); // Purpur + + // Paper start - stop large pitch and yaw changes from crashing the server + this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F; +@@ -3399,7 +3399,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; + // Paper end + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.animStep += f2; + if (this.isFallFlying()) { + ++this.fallFlyTicks; +@@ -3629,21 +3629,21 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + this.setDeltaMovement(d0, d1, d2); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("ai"); ++ //gameprofilerfiller.push("ai"); // Purpur + if (this.isImmobile()) { + this.jumping = false; + this.xxa = 0.0F; + this.zza = 0.0F; + } else if (this.isEffectiveAi()) { +- gameprofilerfiller.push("newAi"); ++ //gameprofilerfiller.push("newAi"); // Purpur + this.serverAiStep(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("jump"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("jump"); // Purpur + if (this.jumping && this.isAffectedByFluids()) { + double d3; + +@@ -3670,8 +3670,8 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.noJumpDelay = 0; + } + +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("travel"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("travel"); // Purpur + this.xxa *= 0.98F; + this.zza *= 0.98F; + if (this.isFallFlying()) { +@@ -3704,8 +3704,8 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + this.calculateEntityAnimation(this instanceof FlyingAnimal); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("freezing"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("freezing"); // Purpur + if (!this.level().isClientSide && !this.isDeadOrDying() && !this.freezeLocked) { // Paper - Freeze Tick Lock API + int i = this.getTicksFrozen(); + +@@ -3726,15 +3726,15 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + } + +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("push"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("push"); // Purpur + if (this.autoSpinAttackTicks > 0) { + --this.autoSpinAttackTicks; + this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox()); + } + + this.pushEntities(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + // Paper start - Add EntityMoveEvent + // Purpur start + if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 7978e307e7bb7f80993c49dcc5ada319a907c648..ff9d23aef4658922692b43a859bd83632fe23612 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -371,15 +371,15 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + @Override + public void baseTick() { + super.baseTick(); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("mobBaseTick"); ++ //gameprofilerfiller.push("mobBaseTick"); // Purpur + if (this.isAlive() && this.random.nextInt(1000) < this.ambientSoundTime++) { + this.resetAmbientSoundTime(); + this.playAmbientSound(); + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + incrementTicksSinceLastInteraction(); // Purpur + } + +@@ -698,9 +698,9 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + @Override + public void aiStep() { + super.aiStep(); +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("looting"); ++ //gameprofilerfiller.push("looting"); // Purpur + Level world = this.level(); + + if (world instanceof ServerLevel worldserver) { +@@ -724,7 +724,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + protected Vec3i getPickupReach() { +@@ -946,44 +946,44 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + return; + } + // Paper end - Allow nerfed mobs to jump and float +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("sensing"); ++ //gameprofilerfiller.push("sensing"); // Purpur + this.sensing.tick(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + int i = this.tickCount + this.getId(); + + if (i % 2 != 0 && this.tickCount > 1) { +- gameprofilerfiller.push("targetSelector"); ++ //gameprofilerfiller.push("targetSelector"); // Purpur + this.targetSelector.tickRunningGoals(false); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("goalSelector"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("goalSelector"); // Purpur + this.goalSelector.tickRunningGoals(false); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } else { +- gameprofilerfiller.push("targetSelector"); ++ //gameprofilerfiller.push("targetSelector"); // Purpur + this.targetSelector.tick(); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("goalSelector"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("goalSelector"); // Purpur + this.goalSelector.tick(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + +- gameprofilerfiller.push("navigation"); ++ //gameprofilerfiller.push("navigation"); // Purpur + this.navigation.tick(); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("mob tick"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("mob tick"); // Purpur + this.customServerAiStep((ServerLevel) this.level()); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("controls"); +- gameprofilerfiller.push("move"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("controls"); // Purpur ++ //gameprofilerfiller.push("move"); // Purpur + this.moveControl.tick(); +- gameprofilerfiller.popPush("look"); ++ //gameprofilerfiller.popPush("look"); // Purpur + this.lookControl.tick(); +- gameprofilerfiller.popPush("jump"); ++ //gameprofilerfiller.popPush("jump"); // Purpur + this.jumpControl.tick(); +- gameprofilerfiller.pop(); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.pop(); // Purpur + this.sendDebugPackets(); + } + +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 29ae74339a4831ccef3d01e8054931715ba192ad..74c5914bc51cff128d4b86853316e5e51e02b416 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -82,8 +82,8 @@ public class GoalSelector { + } + + public void tick() { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("goalCleanup"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("goalCleanup"); // Purpur + + for (WrappedGoal wrappedGoal : this.availableGoals) { + if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams +@@ -92,8 +92,8 @@ public class GoalSelector { + } + + this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); +- profilerFiller.pop(); +- profilerFiller.push("goalUpdate"); ++ //profilerFiller.pop(); // Purpur ++ //profilerFiller.push("goalUpdate"); // Purpur + + for (WrappedGoal wrappedGoal2 : this.availableGoals) { + // Paper start +@@ -113,13 +113,13 @@ public class GoalSelector { + } + } + +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + this.tickRunningGoals(true); + } + + public void tickRunningGoals(boolean tickAll) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("goalTick"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("goalTick"); // Purpur + + for (WrappedGoal wrappedGoal : this.availableGoals) { + if (wrappedGoal.isRunning() && (tickAll || wrappedGoal.requiresUpdateEveryTick())) { +@@ -127,7 +127,7 @@ public class GoalSelector { + } + } + +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + } + + public Set getAvailableGoals() { +diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java +index 48c0de870a5bbf647309e69361dfb10ab56c65ab..a5289b6c453c24cb7b8b3a301b72c3adf92c1d13 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java ++++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java +@@ -188,13 +188,13 @@ public abstract class PathNavigation { + } + } + // Paper end - EntityPathfindEvent +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("pathfind"); ++ //ProfilerFiller profilerFiller = Profiler.get(); ++ //profilerFiller.push("pathfind"); // Purpur + BlockPos blockPos = useHeadPos ? this.mob.blockPosition().above() : this.mob.blockPosition(); + int i = (int)(followRange + (float)range); + PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i)); + Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, positions, followRange, distance, this.maxVisitedNodesMultiplier); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + if (path != null && path.getTarget() != null) { + this.targetPos = path.getTarget(); + this.reachRange = distance; +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +index 116b1e251ffe68bae5c404d0823c2bc7c1afddf6..8f5a5db1a07fa6a95ecfacaab2f9de609202faf4 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +@@ -28,10 +28,10 @@ public class Sensing { + } else if (this.unseen.contains(i)) { + return false; + } else { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("hasLineOfSight"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("hasLineOfSight"); // Purpur + boolean bl = this.mob.hasLineOfSight(entity); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + if (bl) { + this.seen.add(i); + } else { +diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +index 5904b82b87bcdbe41367843885f7c1bd1272d1f1..f60961bd5fdf6d1417e458b92e311c1a0a62463d 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +@@ -262,15 +262,15 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("allayBrain"); ++ //gameprofilerfiller.push("allayBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("allayActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("allayActivityUpdate"); // Purpur + AllayAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java +index 9ef53608f1608e88de7a7b3e96b43d8d2b4230ae..84516dcfc6cd0fe16c26538ccb86fb5c85b44ffa 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/src/main/java/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -163,14 +163,14 @@ public class Armadillo extends Animal { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("armadilloBrain"); ++ //gameprofilerfiller.push("armadilloBrain"); // Purpur + ((Brain) this.brain).tick(world, this); // CraftBukkit - decompile error +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("armadilloActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("armadilloActivityUpdate"); // Purpur + ArmadilloAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + if (this.isAlive() && !this.isBaby() && --this.scuteTime <= 0) { + this.forceDrops = true; // CraftBukkit + if (this.dropFromGiftLootTable(world, BuiltInLootTables.ARMADILLO_SHED, this::spawnAtLocation)) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 91768d49c3cbef0a135cfd0e60bdb4e84bf109b3..c80d74caa393a31e09f2776cdb3cb950ef99e2ef 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -332,15 +332,15 @@ public class Axolotl extends Animal implements VariantHolder, B + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("axolotlBrain"); ++ //gameprofilerfiller.push("axolotlBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("axolotlActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("axolotlActivityUpdate"); // Purpur + AxolotlAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + if (!this.isNoAi()) { + Optional optional = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS); + +diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java +index dbce71f45c008da883b6d244a06343d83ae25e7e..56d97225a909fd55f0d8aec992d5b6d42687c948 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java +@@ -154,16 +154,16 @@ public class Camel extends AbstractHorse { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("camelBrain"); ++ //gameprofilerfiller.push("camelBrain"); // Purpur + Brain behaviorcontroller = (Brain) this.getBrain(); // CraftBukkit - decompile error + + behaviorcontroller.tick(world, this); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("camelActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("camelActivityUpdate"); // Purpur + CamelAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +index 49915ec0ee5fb6e7d25b079e0410942d44aa1e33..501a12398c56fe0df4e76a3bbce0f98c6c5aa6cb 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +@@ -240,14 +240,14 @@ public class Frog extends Animal implements VariantHolder> { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("frogBrain"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("frogBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- profilerFiller.pop(); +- profilerFiller.push("frogActivityUpdate"); ++ //profilerFiller.pop(); // Purpur ++ //profilerFiller.push("frogActivityUpdate"); // Purpur + FrogAi.updateActivity(this); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +index fb91677fb03b7bc1decdf181b7b15d971ffacdc2..071d14cc6697587ec14f02c69c78df364e7d8a8f 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -122,15 +122,15 @@ public class Tadpole extends AbstractFish { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); + +- gameprofilerfiller.push("tadpoleBrain"); ++ //gameprofilerfiller.push("tadpoleBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("tadpoleActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("tadpoleActivityUpdate"); // Purpur + TadpoleAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +index 7f196e8a8b5094375c9b13fe6e7311a3450dbb5f..51dcc3893b964e6d20a2b963d3105a2687b3c426 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +@@ -226,15 +226,15 @@ public class Goat extends Animal { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("goatBrain"); ++ //gameprofilerfiller.push("goatBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); +- gameprofilerfiller.push("goatActivityUpdate"); ++ //gameprofilerfiller.pop(); // Purpur ++ //gameprofilerfiller.push("goatActivityUpdate"); // Purpur + GoatAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java +index 464a16df15d8759d66f94ad080d1ea28b3f6474c..3fb4f12095883ea4ec6e0d60e0600b9de6ed7be2 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java ++++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java +@@ -495,13 +495,13 @@ public class Sniffer extends Animal { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("snifferBrain"); ++ //gameprofilerfiller.push("snifferBrain"); // Purpur + this.getBrain().tick(world, this); +- gameprofilerfiller.popPush("snifferActivityUpdate"); ++ //gameprofilerfiller.popPush("snifferActivityUpdate"); // Purpur + SnifferAi.updateActivity(this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java +index cb20c018c11a0e707c2083cf964bd5303d216edd..f30ad422f19757664228f2064465fbcb22bb54f6 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java +@@ -281,11 +281,11 @@ public class Zoglin extends Monster implements HoglinBase { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("zoglinBrain"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("zoglinBrain"); // Purpur + if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(world, this); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + this.updateActivity(); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/breeze/Breeze.java b/src/main/java/net/minecraft/world/entity/monster/breeze/Breeze.java +index a16fd9c4679e874ad2d499f3c00c2ddfd780a7a5..0bc771e20a9bab139cd3fc03ff40baabf787b2f7 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/breeze/Breeze.java ++++ b/src/main/java/net/minecraft/world/entity/monster/breeze/Breeze.java +@@ -235,12 +235,12 @@ public class Breeze extends Monster { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("breezeBrain"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("breezeBrain"); // Purpur + this.getBrain().tick(world, this); +- profilerFiller.popPush("breezeActivityUpdate"); ++ //profilerFiller.popPush("breezeActivityUpdate"); // Purpur + BreezeAi.updateActivity(this); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + super.customServerAiStep(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 7520ab7ec5902f083aefd68895a7d9c47bf4e418..7afde99f81ec222ecbffbdcdad3aa6404e8221e0 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -196,11 +196,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("hoglinBrain"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("hoglinBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + HoglinAi.updateActivity(this); + if (this.isConverting()) { + this.timeInOverworld++; +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index 7d5ce66c6691b0911ed5afcb1d4186525f09a15e..b53e4a312ea0dbbd16e948e61c3f6b836d46f3b8 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -339,12 +339,12 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("piglinBrain"); ++ //gameprofilerfiller.push("piglinBrain"); // Purpur + //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // // Purpur - TODO: Pufferfish + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + PiglinAi.updateActivity(this); + super.customServerAiStep(world); + } +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index bc3b0eb21200eae7e419b2571871b36fc1e07c0f..719179fb232a4f39a2c1642cc0e9593f4dea4bb8 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +@@ -148,11 +148,11 @@ public class PiglinBrute extends AbstractPiglin { + + @Override + protected void customServerAiStep(ServerLevel world) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("piglinBruteBrain"); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("piglinBruteBrain"); // Purpur + if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(world, this); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + PiglinBruteAi.updateActivity(this); + PiglinBruteAi.maybePlayActivitySound(this); + super.customServerAiStep(world); +diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +index fcfea2cdd5df9fe505df3f7c866cd8d27ad9b249..692261880d05daa75fc53dde31d0f2b95dc52746 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +@@ -303,9 +303,9 @@ public class Warden extends Monster implements VibrationSystem { + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + +- gameprofilerfiller.push("wardenBrain"); ++ //gameprofilerfiller.push("wardenBrain"); // Purpur + this.getBrain().tick(world, this); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + super.customServerAiStep(world); + if ((this.tickCount + this.getId()) % 120 == 0) { + Warden.applyDarknessAround(world, this.position(), this, 20); +diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java +index c885b214f4d7d91627e98d8779aab8515f205636..b4ffad429ace965f16ebf47119c880b709b27f2e 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -340,9 +340,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + protected void customServerAiStep(ServerLevel world, boolean inactive) { // Purpur - not final + // Paper end - EAR 2 +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("villagerBrain"); ++ //gameprofilerfiller.push("villagerBrain"); // Purpur + // Purpur start + if (this.level().purpurConfig.villagerLobotomizeEnabled) { + // treat as inactive if lobotomized +@@ -352,7 +352,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end + if (!inactive && (getRider() == null || !this.isControllable())) this.getBrain().tick(world, this); // Purpur +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; + } +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index c2aff2f03451b97f1ec6bd4ee987bb729177320a..7493262c2879af196e5585b15faad69ae42764e3 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -270,7 +270,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + + @Override + public final List getEntitiesOfClass(final Class entityClass, final AABB boundingBox, final Predicate predicate) { +- Profiler.get().incrementCounter("getEntities"); ++ //Profiler.get().incrementCounter("getEntities"); // Purpur + final List ret = new java.util.ArrayList<>(); + + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate); +@@ -280,7 +280,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + + @Override + public final List moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate predicate) { +- Profiler.get().incrementCounter("getEntities"); ++ //Profiler.get().incrementCounter("getEntities"); // Purpur + final List ret = new java.util.ArrayList<>(); + + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate); +@@ -1486,9 +1486,9 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + } + + protected void tickBlockEntities() { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); // Purpur + +- gameprofilerfiller.push("blockEntities"); ++ //gameprofilerfiller.push("blockEntities"); // Purpur + this.tickingBlockEntities = true; + if (!this.pendingBlockEntityTickers.isEmpty()) { + this.blockEntityTickers.addAll(this.pendingBlockEntityTickers); +@@ -1526,7 +1526,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 + + this.tickingBlockEntities = false; +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + this.spigotConfig.currentPrimedTnt = 0; // Spigot + } + +@@ -1696,7 +1696,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + + @Override + public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { +- Profiler.get().incrementCounter("getEntities"); ++ //Profiler.get().incrementCounter("getEntities"); // Purpur + List list = Lists.newArrayList(); + + // Paper start - rewrite chunk system +@@ -1726,7 +1726,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + public void getEntities(final EntityTypeTest entityTypeTest, + final AABB boundingBox, final Predicate predicate, + final List into, final int maxCount) { +- Profiler.get().incrementCounter("getEntities"); ++ //Profiler.get().incrementCounter("getEntities"); // Purpur + + if (entityTypeTest instanceof net.minecraft.world.entity.EntityType byType) { + if (maxCount != Integer.MAX_VALUE) { +diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +index 5297798c2be1ba85569c2b92ed221956bf75477a..fa47d14e17d9e7d1b62de7990c875245c3a445a3 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -211,7 +211,7 @@ public final class NaturalSpawner { + } + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + // Paper start - Add mobcaps commands +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 4640baec5bed6c2d53cc0f8ca1d273cc115abe9b..15c83c6f5f56d6a27911d3bbd326cef1c21b1e58 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -407,11 +407,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + if (LightEngine.hasDifferentLightProperties(iblockdata1, iblockdata)) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + +- gameprofilerfiller.push("updateSkyLightSources"); ++ //gameprofilerfiller.push("updateSkyLightSources"); // Purpur + // Paper - rewrite chunk system +- gameprofilerfiller.popPush("queueCheckLight"); ++ //gameprofilerfiller.popPush("queueCheckLight"); // Purpur + this.level.getChunkSource().getLightEngine().checkBlock(blockposition); +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); // Purpur + } + + boolean flag3 = iblockdata1.hasBlockEntity(); +@@ -1058,9 +1058,9 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + + if (LevelChunk.this.isTicking(blockposition)) { + try { +- ProfilerFiller gameprofilerfiller = Profiler.get(); ++ //ProfilerFiller gameprofilerfiller = Profiler.get(); + +- gameprofilerfiller.push(this::getType); ++ //gameprofilerfiller.push(this::getType); + BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); + + if (this.blockEntity.getType().isValid(iblockdata)) { +@@ -1076,7 +1076,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + // Paper end - Remove the Block Entity if it's invalid + } + +- gameprofilerfiller.pop(); ++ //gameprofilerfiller.pop(); + } catch (Throwable throwable) { + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent block entity and entity crashes +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java +index cc7d94144e39f7dace7b569b4567def98396e8f9..91abbda59446d462979dddc4b380c6f24d0b4c92 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java +@@ -58,9 +58,9 @@ public class PathFinder { + @Nullable + // Paper start - Perf: remove streams and optimize collection + private Path findPath(Node startNode, List> positions, float followRange, int distance, float rangeMultiplier) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("find_path"); +- profilerFiller.markForCharting(MetricCategory.PATH_FINDING); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("find_path"); // Purpur ++ //profilerFiller.markForCharting(MetricCategory.PATH_FINDING); // Purpur + // Set set = positions.keySet(); + startNode.g = 0.0F; + startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection +@@ -128,7 +128,7 @@ public class PathFinder { + if (best == null || comparator.compare(path, best) < 0) + best = path; + } +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + return best; + // Paper end - Perf: remove streams and optimize collection + } +diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java +index 778e6476c86d823dc8efe603a95e589e8b2ea9d9..452fb3442221fd66debfe1e1d6e85ee17951e556 100644 +--- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java ++++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java +@@ -79,20 +79,20 @@ public class LevelTicks implements LevelTickAccess { + } + + public void tick(long time, int maxTicks, BiConsumer ticker) { +- ProfilerFiller profilerFiller = Profiler.get(); +- profilerFiller.push("collect"); +- this.collectTicks(time, maxTicks, profilerFiller); +- profilerFiller.popPush("run"); +- profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size()); ++ //ProfilerFiller profilerFiller = Profiler.get(); // Purpur ++ //profilerFiller.push("collect"); // Purpur ++ this.collectTicks(time, maxTicks, null); // Purpur ++ //profilerFiller.popPush("run"); // Purpur ++ //profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size()); // Purpur + this.runCollectedTicks(ticker); +- profilerFiller.popPush("cleanup"); ++ //profilerFiller.popPush("cleanup"); // Purpur + this.cleanupAfterTick(); +- profilerFiller.pop(); ++ //profilerFiller.pop(); // Purpur + } + + private void collectTicks(long time, int maxTicks, ProfilerFiller profiler) { + this.sortContainersToTick(time); +- profiler.incrementCounter("containersToTick", this.containersToTick.size()); ++ //profiler.incrementCounter("containersToTick", this.containersToTick.size()); // Purpur + this.drainContainers(time, maxTicks); + this.rescheduleLeftoverContainers(); + } diff --git a/patches/api/0004-Build-System-Changes.patch b/patches/api/0004-Build-System-Changes.patch deleted file mode 100644 index 2b261b1d6..000000000 --- a/patches/api/0004-Build-System-Changes.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sat, 12 Jun 2021 12:35:38 -0400 -Subject: [PATCH] Build System Changes - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 0330a20576c372c29ca4d98a2bc5b01e28303ac3..67f31886a0e5025c1a66b6fb04b44430a9a103fc 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -109,6 +109,8 @@ tasks.jar { - } - - tasks.withType { -+ (options as StandardJavadocDocletOptions).addStringOption("-add-modules", "jdk.incubator.vector") // Purpur - our javadocs need this for pufferfish's SIMD patch -+ (options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet") // Purpur - silence Paper's bajillion javadoc warnings - val options = options as StandardJavadocDocletOptions - options.overview = "src/main/javadoc/overview.html" - options.use() diff --git a/patches/api/0005-Purpur-client-support.patch b/patches/api/0005-Purpur-client-support.patch deleted file mode 100644 index 4ddbb6f31..000000000 --- a/patches/api/0005-Purpur-client-support.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Fredthedoggy <45927799+fredthedoggy@users.noreply.github.com> -Date: Thu, 19 Aug 2021 20:04:18 -0400 -Subject: [PATCH] Purpur client support - - -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index f9dd00210c1762a40259f823aeb8d8a5ddc78e3e..9ee0d60775a00a99c8ac779e8602c619565e6a42 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3271,4 +3271,13 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - @Override - Spigot spigot(); - // Spigot end -+ -+ // Purpur start -+ /** -+ * Allows you to get if player uses Purpur Client -+ * -+ * @return True if Player uses Purpur Client -+ */ -+ public boolean usesPurpurClient(); -+ // Purpur end - } diff --git a/patches/api/0006-Default-permissions.patch b/patches/api/0006-Default-permissions.patch deleted file mode 100644 index 66bf74338..000000000 --- a/patches/api/0006-Default-permissions.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 5 Jun 2020 23:32:38 -0500 -Subject: [PATCH] Default permissions - - -diff --git a/src/main/java/org/bukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/util/permissions/CommandPermissions.java -index 7763d6101ac61900db1e2310966b99584539fd0e..d5a42707d365ffd72532bbb1a59a1ca7145f9918 100644 ---- a/src/main/java/org/bukkit/util/permissions/CommandPermissions.java -+++ b/src/main/java/org/bukkit/util/permissions/CommandPermissions.java -@@ -18,6 +18,7 @@ public final class CommandPermissions { - DefaultPermissions.registerPermission(PREFIX + "plugins", "Allows the user to view the list of plugins running on this server", PermissionDefault.TRUE, commands); - DefaultPermissions.registerPermission(PREFIX + "reload", "Allows the user to reload the server settings", PermissionDefault.OP, commands); - DefaultPermissions.registerPermission(PREFIX + "version", "Allows the user to view the version of the server", PermissionDefault.TRUE, commands); -+ DefaultPermissions.registerPermission(PREFIX + "purpur", "Allows the user to use the purpur command", PermissionDefault.OP, commands); // Purpur - - commands.recalculatePermissibles(); - return commands; -diff --git a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -index e1a4ddf2c07cdd242fa8054a0152522fe4039e85..8e481e3815f5645ee92f0d229e5ff25c8fc9a6c2 100644 ---- a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -+++ b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -@@ -89,6 +89,8 @@ public final class DefaultPermissions { - CommandPermissions.registerPermissions(parent); - BroadcastPermissions.registerPermissions(parent); - -+ PurpurPermissions.registerPermissions(); // Purpur -+ - parent.recalculatePermissibles(); - } - } -diff --git a/src/main/java/org/bukkit/util/permissions/PurpurPermissions.java b/src/main/java/org/bukkit/util/permissions/PurpurPermissions.java -new file mode 100644 -index 0000000000000000000000000000000000000000..baec4c87d7ea4d54934ca22fd1eb7b46dd69061b ---- /dev/null -+++ b/src/main/java/org/bukkit/util/permissions/PurpurPermissions.java -@@ -0,0 +1,87 @@ -+package org.bukkit.util.permissions; -+ -+import org.bukkit.entity.Entity; -+import org.bukkit.entity.EntityType; -+import org.bukkit.entity.Mob; -+import org.bukkit.permissions.Permission; -+import org.bukkit.permissions.PermissionDefault; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.HashSet; -+import java.util.Set; -+ -+public final class PurpurPermissions { -+ private static final String ROOT = "purpur"; -+ private static final String PREFIX = ROOT + "."; -+ private static final Set mobs = new HashSet<>(); -+ -+ static { -+ for (EntityType mob : EntityType.values()) { -+ Class clazz = mob.getEntityClass(); -+ if (clazz != null && Mob.class.isAssignableFrom(clazz)) { -+ mobs.add(mob.getName()); -+ } -+ } -+ } -+ -+ @NotNull -+ public static Permission registerPermissions() { -+ Permission purpur = DefaultPermissions.registerPermission(ROOT, "Gives the user the ability to use all Purpur utilities and commands", PermissionDefault.FALSE); -+ -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.six", "Gives the user six rows of enderchest space", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.five", "Gives the user five rows of enderchest space", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.four", "Gives the user four rows of enderchest space", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.three", "Gives the user three rows of enderchest space", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.two", "Gives the user two rows of enderchest space", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.one", "Gives the user one row of enderchest space", PermissionDefault.FALSE, purpur); -+ -+ DefaultPermissions.registerPermission(PREFIX + "debug.f3n", "Allows the user to use F3+N keybind to swap gamemodes", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "joinfullserver", "Allows the user to join a full server", PermissionDefault.OP, purpur); -+ -+ DefaultPermissions.registerPermission(PREFIX + "drop.spawners", "Allows the user to drop spawner cage when broken with diamond pickaxe with silk touch", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "place.spawners", "Allows the user to place spawner cage in the world", PermissionDefault.FALSE, purpur); -+ -+ DefaultPermissions.registerPermission(PREFIX + "mending_shift_click", "Allows the user to use shift-right-click to mend items", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "inventory_totem", "Uses a totem from anywhere in the user's inventory on death", PermissionDefault.FALSE, purpur); -+ -+ Permission anvil = DefaultPermissions.registerPermission(PREFIX + "anvil", "Allows the user to use all anvil color and format abilities", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "anvil.color", "Allows the user to use color codes in an anvil", PermissionDefault.FALSE, anvil); -+ DefaultPermissions.registerPermission(PREFIX + "anvil.minimessage", "Allows the user to use minimessage tags in an anvil", PermissionDefault.FALSE, anvil); -+ DefaultPermissions.registerPermission(PREFIX + "anvil.remove_italics", "Allows the user to remove italics in an anvil", PermissionDefault.FALSE, anvil); -+ DefaultPermissions.registerPermission(PREFIX + "anvil.format", "Allows the user to use format codes in an anvil", PermissionDefault.FALSE, anvil); -+ anvil.recalculatePermissibles(); -+ -+ Permission book = DefaultPermissions.registerPermission(PREFIX + "book", "Allows the user to use color codes on books", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "book.color.edit", "Allows the user to use color codes on books when editing", PermissionDefault.FALSE, book); -+ DefaultPermissions.registerPermission(PREFIX + "book.color.sign", "Allows the user to use color codes on books when signing", PermissionDefault.FALSE, book); -+ book.recalculatePermissibles(); -+ -+ Permission sign = DefaultPermissions.registerPermission(PREFIX + "sign", "Allows the user to use all sign abilities", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission(PREFIX + "sign.edit", "Allows the user to click signs to open sign editor", PermissionDefault.FALSE, sign); -+ DefaultPermissions.registerPermission(PREFIX + "sign.color", "Allows the user to use color codes on signs", PermissionDefault.FALSE, sign); -+ DefaultPermissions.registerPermission(PREFIX + "sign.style", "Allows the user to use style codes on signs", PermissionDefault.FALSE, sign); -+ DefaultPermissions.registerPermission(PREFIX + "sign.magic", "Allows the user to use magic/obfuscate code on signs", PermissionDefault.FALSE, sign); -+ sign.recalculatePermissibles(); -+ -+ Permission ride = DefaultPermissions.registerPermission("allow.ride", "Allows the user to ride all mobs", PermissionDefault.FALSE, purpur); -+ for (String mob : mobs) { -+ DefaultPermissions.registerPermission("allow.ride." + mob, "Allows the user to ride " + mob, PermissionDefault.FALSE, ride); -+ } -+ ride.recalculatePermissibles(); -+ -+ Permission special = DefaultPermissions.registerPermission("allow.special", "Allows the user to use all mobs special abilities", PermissionDefault.FALSE, purpur); -+ for (String mob : mobs) { -+ DefaultPermissions.registerPermission("allow.special." + mob, "Allows the user to use " + mob + " special ability", PermissionDefault.FALSE, special); -+ } -+ special.recalculatePermissibles(); -+ -+ Permission powered = DefaultPermissions.registerPermission("allow.powered", "Allows the user to toggle all mobs powered state", PermissionDefault.FALSE, purpur); -+ DefaultPermissions.registerPermission("allow.powered.creeper", "Allows the user to toggle creeper powered state", PermissionDefault.FALSE, powered); -+ powered.recalculatePermissibles(); -+ -+ DefaultPermissions.registerPermission(PREFIX + "portal.instant", "Allows the user to bypass portal wait time", PermissionDefault.FALSE, purpur); -+ -+ purpur.recalculatePermissibles(); -+ return purpur; -+ } -+} diff --git a/patches/api/0007-Ridables.patch b/patches/api/0007-Ridables.patch deleted file mode 100644 index c52fb6969..000000000 --- a/patches/api/0007-Ridables.patch +++ /dev/null @@ -1,215 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 4 May 2019 00:57:16 -0500 -Subject: [PATCH] Ridables - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index b7a2cecb334ce39fa09d8ab949a29eedbdc44c36..6f55ccfff74b361854bf424fd93f0428e66a6bd7 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -201,6 +201,12 @@ public interface VanillaGoal extends Goal { - GoalKey CLIMB_ON_TOP_OF_POWDER_SNOW = GoalKey.of(Mob.class, NamespacedKey.minecraft("climb_on_top_of_powder_snow")); - GoalKey WOLF_PANIC = GoalKey.of(Wolf.class, NamespacedKey.minecraft("wolf_panic")); - -+ // Purpur start -+ GoalKey MOB_HAS_RIDER = GoalKey.of(Mob.class, NamespacedKey.minecraft("has_rider")); -+ GoalKey HORSE_HAS_RIDER = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("horse_has_rider")); -+ GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider")); -+ // Purpur end -+ - /** - * @deprecated removed in 1.16 - */ -diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index 762cb07861ca8ff058ce8d57ea6c15df1e588bf3..de60e8773e58ef62c15d8f7e293313a62dff674b 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1049,4 +1049,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent - */ - @NotNull String getScoreboardEntryName(); - // Paper end - entity scoreboard name -+ -+ // Purpur start -+ /** -+ * Get the riding player -+ * -+ * @return Riding player -+ */ -+ @Nullable -+ Player getRider(); -+ -+ /** -+ * Check if entity is being ridden -+ * -+ * @return True if being ridden -+ */ -+ boolean hasRider(); -+ -+ /** -+ * Check if entity is ridable -+ * -+ * @return True if ridable -+ */ -+ boolean isRidable(); -+ -+ /** -+ * Check if entity is ridable in water -+ * -+ * @return True if ridable in water -+ */ -+ boolean isRidableInWater(); -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a037df01b07af9ffb98b67aca412c1d34fade03b ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java -@@ -0,0 +1,103 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import com.google.common.base.Preconditions; -+import org.bukkit.Location; -+import org.bukkit.entity.Mob; -+import org.bukkit.entity.Player; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Triggered when a ridable mob moves with a rider -+ */ -+public class RidableMoveEvent extends EntityEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean canceled; -+ private final Player rider; -+ private Location from; -+ private Location to; -+ -+ public RidableMoveEvent(@NotNull Mob entity, @NotNull Player rider, @NotNull Location from, @NotNull Location to) { -+ super(entity); -+ this.rider = rider; -+ this.from = from; -+ this.to = to; -+ } -+ -+ @Override -+ @NotNull -+ public Mob getEntity() { -+ return (Mob) entity; -+ } -+ -+ @NotNull -+ public Player getRider() { -+ return rider; -+ } -+ -+ public boolean isCancelled() { -+ return canceled; -+ } -+ -+ public void setCancelled(boolean cancel) { -+ canceled = cancel; -+ } -+ -+ /** -+ * Gets the location this entity moved from -+ * -+ * @return Location the entity moved from -+ */ -+ @NotNull -+ public Location getFrom() { -+ return from; -+ } -+ -+ /** -+ * Sets the location to mark as where the entity moved from -+ * -+ * @param from New location to mark as the entity's previous location -+ */ -+ public void setFrom(@NotNull Location from) { -+ validateLocation(from); -+ this.from = from; -+ } -+ -+ /** -+ * Gets the location this entity moved to -+ * -+ * @return Location the entity moved to -+ */ -+ @NotNull -+ public Location getTo() { -+ return to; -+ } -+ -+ /** -+ * Sets the location that this entity will move to -+ * -+ * @param to New Location this entity will move to -+ */ -+ public void setTo(@NotNull Location to) { -+ validateLocation(to); -+ this.to = to; -+ } -+ -+ private void validateLocation(@NotNull Location loc) { -+ Preconditions.checkArgument(loc != null, "Cannot use null location!"); -+ Preconditions.checkArgument(loc.getWorld() != null, "Cannot use null location with null world!"); -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3d3a7d898e3278ce998d713dafbb4b354dad7fc7 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java -@@ -0,0 +1,37 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.entity.Entity; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+public class RidableSpacebarEvent extends EntityEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean cancelled; -+ -+ public RidableSpacebarEvent(@NotNull Entity entity) { -+ super(entity); -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return cancelled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ cancelled = cancel; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0008-Allow-inventory-resizing.patch b/patches/api/0008-Allow-inventory-resizing.patch deleted file mode 100644 index a93bb8a10..000000000 --- a/patches/api/0008-Allow-inventory-resizing.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 23 Jul 2019 06:50:55 -0500 -Subject: [PATCH] Allow inventory resizing - - -diff --git a/src/main/java/org/bukkit/event/inventory/InventoryType.java b/src/main/java/org/bukkit/event/inventory/InventoryType.java -index cbce826add9dc2b3187c7bea00c27b785d7517df..3a98de6407d9a6307f89c207be1f09e639385ebe 100644 ---- a/src/main/java/org/bukkit/event/inventory/InventoryType.java -+++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java -@@ -151,7 +151,7 @@ public enum InventoryType { - SMITHING_NEW(4, "Upgrade Gear"), - ; - -- private final int size; -+ private int size; public void setDefaultSize(int size) { this.size = size; } // Purpur - remove final and add setter - private final String title; - private final boolean isCreatable; - diff --git a/patches/api/0009-Llama-API.patch b/patches/api/0009-Llama-API.patch deleted file mode 100644 index cfebb1cee..000000000 --- a/patches/api/0009-Llama-API.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 18 Oct 2019 22:50:05 -0500 -Subject: [PATCH] Llama API - - -diff --git a/src/main/java/org/bukkit/entity/Llama.java b/src/main/java/org/bukkit/entity/Llama.java -index bc84b892cae5fe7019a3ad481e9da79956efa1fe..48eb5b00c460cccde29d327cef1d63fc04d6a829 100644 ---- a/src/main/java/org/bukkit/entity/Llama.java -+++ b/src/main/java/org/bukkit/entity/Llama.java -@@ -119,4 +119,20 @@ public interface Llama extends ChestedHorse, RangedEntity { // Paper - @org.jetbrains.annotations.Nullable - Llama getCaravanTail(); - // Paper end -+ -+ // Purpur start -+ /** -+ * Check if this Llama should attempt to join a caravan -+ * -+ * @return True if Llama is allowed to join a caravan -+ */ -+ boolean shouldJoinCaravan(); -+ -+ /** -+ * Set if this Llama should attempt to join a caravan -+ * -+ * @param shouldJoinCaravan True to allow joining a caravan -+ */ -+ void setShouldJoinCaravan(boolean shouldJoinCaravan); -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8849bb0becb16db907fa648cca2e98ab9d957c75 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java -@@ -0,0 +1,61 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.entity.Llama; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a Llama tries to join a caravan. -+ *

-+ * Cancelling the event will not let the Llama join. To prevent future attempts -+ * at joining a caravan use {@link Llama#setShouldJoinCaravan(boolean)}. -+ */ -+public class LlamaJoinCaravanEvent extends EntityEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean canceled; -+ private final Llama head; -+ -+ public LlamaJoinCaravanEvent(@NotNull Llama llama, @NotNull Llama head) { -+ super(llama); -+ this.head = head; -+ } -+ -+ @Override -+ @NotNull -+ public Llama getEntity() { -+ return (Llama) entity; -+ } -+ -+ /** -+ * Get the Llama that this Llama is about to follow -+ * -+ * @return Llama about to be followed -+ */ -+ @NotNull -+ public Llama getHead() { -+ return head; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return canceled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ canceled = cancel; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c268c35b541a222d50875c29770c846a8ffcc4f8 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java -@@ -0,0 +1,34 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.entity.Llama; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a Llama leaves a caravan -+ */ -+public class LlamaLeaveCaravanEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ -+ public LlamaLeaveCaravanEvent(@NotNull Llama llama) { -+ super(llama); -+ } -+ -+ @Override -+ @NotNull -+ public Llama getEntity() { -+ return (Llama) entity; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0010-AFK-API.patch b/patches/api/0010-AFK-API.patch deleted file mode 100644 index ea7bab12e..000000000 --- a/patches/api/0010-AFK-API.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 10 Aug 2019 22:19:56 -0500 -Subject: [PATCH] AFK API - - -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 9ee0d60775a00a99c8ac779e8602c619565e6a42..2ddacd7f74cba66f67ab6957e03522453de01117 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3279,5 +3279,24 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * @return True if Player uses Purpur Client - */ - public boolean usesPurpurClient(); -+ -+ /** -+ * Check if player is AFK -+ * -+ * @return True if AFK -+ */ -+ boolean isAfk(); -+ -+ /** -+ * Set player as AFK -+ * -+ * @param setAfk Whether to set AFK or not -+ */ -+ void setAfk(boolean setAfk); -+ -+ /** -+ * Reset the idle timer back to 0 -+ */ -+ void resetIdleTimer(); - // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..25e92af7710316ed2afedf846a59dbd672869b51 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java -@@ -0,0 +1,70 @@ -+package org.purpurmc.purpur.event; -+ -+import org.bukkit.entity.Player; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.player.PlayerEvent; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+public class PlayerAFKEvent extends PlayerEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private final boolean setAfk; -+ private boolean shouldKick; -+ private String broadcast; -+ private boolean cancel; -+ -+ public PlayerAFKEvent(@NotNull Player player, boolean setAfk, boolean shouldKick, @Nullable String broadcast, boolean async) { -+ super(player, async); -+ this.setAfk = setAfk; -+ this.shouldKick = shouldKick; -+ this.broadcast = broadcast; -+ } -+ -+ /** -+ * Whether player is going afk or coming back -+ * -+ * @return True if going afk. False is coming back -+ */ -+ public boolean isGoingAfk() { -+ return setAfk; -+ } -+ -+ public boolean shouldKick() { -+ return shouldKick; -+ } -+ -+ public void setShouldKick(boolean shouldKick) { -+ this.shouldKick = shouldKick; -+ } -+ -+ @Nullable -+ public String getBroadcastMsg() { -+ return broadcast; -+ } -+ -+ public void setBroadcastMsg(@Nullable String broadcast) { -+ this.broadcast = broadcast; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return cancel; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancel = cancel; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0011-Bring-back-server-name.patch b/patches/api/0011-Bring-back-server-name.patch deleted file mode 100644 index 19d0443a9..000000000 --- a/patches/api/0011-Bring-back-server-name.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 26 May 2019 15:18:40 -0500 -Subject: [PATCH] Bring back server name - - -diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index f78b5fd3c3347d28da58777bff88903d2eb140f6..7338dad470cd75814cce5be40f82058554484607 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2756,4 +2756,15 @@ public final class Bukkit { - public static Server.Spigot spigot() { - return server.spigot(); - } -+ -+ // Purpur start -+ /** -+ * Get the name of this server -+ * @return the name of the server -+ */ -+ @NotNull -+ public static String getServerName() { -+ return server.getServerName(); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index c71576fea7e0e7c3ad54912ed61d1ff20aaea4c2..fc2ff6fce4d7b4466cb7936b530c1c0d17f9f24f 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2416,4 +2416,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - */ - boolean isOwnedByCurrentRegion(@NotNull Entity entity); - // Paper end - Folia region threading API -+ -+ // Purpur start -+ /** -+ * Get the name of this server -+ * @return the name of the server -+ */ -+ @NotNull -+ String getServerName(); -+ // Purpur end - } diff --git a/patches/api/0012-ExecuteCommandEvent.patch b/patches/api/0012-ExecuteCommandEvent.patch deleted file mode 100644 index dd05e0eb9..000000000 --- a/patches/api/0012-ExecuteCommandEvent.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 31 May 2019 00:08:28 -0500 -Subject: [PATCH] ExecuteCommandEvent - - -diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java -index ac9a28922f8a556944a4c3649d74c32c622f0cb0..e842d13febca67ffa1c89fb2c1324d2609fb81fd 100644 ---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java -+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -143,6 +143,19 @@ public class SimpleCommandMap implements CommandMap { - return false; - } - -+ // Purpur start -+ String[] parsedArgs = Arrays.copyOfRange(args, 1, args.length); -+ org.purpurmc.purpur.event.ExecuteCommandEvent event = new org.purpurmc.purpur.event.ExecuteCommandEvent(sender, target, sentCommandLabel, parsedArgs); -+ if (!event.callEvent()) { -+ return true; // cancelled -+ } -+ -+ sender = event.getSender(); -+ target = event.getCommand(); -+ sentCommandLabel = event.getLabel(); -+ parsedArgs = event.getArgs(); -+ // Purpur end -+ - // Paper start - Plugins do weird things to workaround normal registration - if (target.timings == null) { - target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); -@@ -152,7 +165,7 @@ public class SimpleCommandMap implements CommandMap { - try { - try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources - // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) -- target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length)); -+ target.execute(sender, sentCommandLabel, parsedArgs); // Purpur - } // target.timings.stopTiming(); // Spigot // Paper - } catch (CommandException ex) { - server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper -diff --git a/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java b/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bc590c4d49d32f4365a50ceb5785e798702a8179 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java -@@ -0,0 +1,130 @@ -+package org.purpurmc.purpur.event; -+ -+import com.google.common.base.Preconditions; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.Event; -+import org.bukkit.event.HandlerList; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * This event is called whenever someone runs a command -+ */ -+public class ExecuteCommandEvent extends Event implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean cancel = false; -+ private CommandSender sender; -+ private Command command; -+ private String label; -+ private String[] args; -+ -+ public ExecuteCommandEvent(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @Nullable String[] args) { -+ this.sender = sender; -+ this.command = command; -+ this.label = label; -+ this.args = args; -+ } -+ -+ /** -+ * Gets the command that the player is attempting to execute. -+ * -+ * @return Command the player is attempting to execute -+ */ -+ @NotNull -+ public Command getCommand() { -+ return command; -+ } -+ -+ /** -+ * Sets the command that the player will execute. -+ * -+ * @param command New command that the player will execute -+ * @throws IllegalArgumentException if command is null or empty -+ */ -+ public void setCommand(@NotNull Command command) throws IllegalArgumentException { -+ Preconditions.checkArgument(command != null, "Command cannot be null"); -+ this.command = command; -+ } -+ -+ /** -+ * Gets the sender that this command will be executed as. -+ * -+ * @return Sender this command will be executed as -+ */ -+ @NotNull -+ public CommandSender getSender() { -+ return sender; -+ } -+ -+ /** -+ * Sets the sender that this command will be executed as. -+ * -+ * @param sender New sender which this event will execute as -+ * @throws IllegalArgumentException if the sender provided is null -+ */ -+ public void setSender(@NotNull final CommandSender sender) throws IllegalArgumentException { -+ Preconditions.checkArgument(sender != null, "Sender cannot be null"); -+ this.sender = sender; -+ } -+ -+ /** -+ * Get the label used to execute this command -+ * -+ * @return Label used to execute this command -+ */ -+ @NotNull -+ public String getLabel() { -+ return label; -+ } -+ -+ /** -+ * Set the label used to execute this command -+ * -+ * @param label Label used -+ */ -+ public void setLabel(@NotNull String label) { -+ this.label = label; -+ } -+ -+ /** -+ * Get the args passed to the command -+ * -+ * @return Args passed to the command -+ */ -+ @NotNull -+ public String[] getArgs() { -+ return args; -+ } -+ -+ /** -+ * Set the args passed to the command -+ * -+ * @param args Args passed to the command -+ */ -+ public void setArgs(@NotNull String[] args) { -+ this.args = args; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return cancel; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancel = cancel; -+ } -+ -+ @NotNull -+ @Override -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0014-Lagging-threshold.patch b/patches/api/0014-Lagging-threshold.patch deleted file mode 100644 index 23b7d9905..000000000 --- a/patches/api/0014-Lagging-threshold.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 23 Jul 2019 10:07:24 -0500 -Subject: [PATCH] Lagging threshold - - -diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 7338dad470cd75814cce5be40f82058554484607..a32155e0cc63d4e4714a953edbae0b19544ff3ef 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2766,5 +2766,14 @@ public final class Bukkit { - public static String getServerName() { - return server.getServerName(); - } -+ -+ /** -+ * Check if server is lagging according to laggy threshold setting -+ * -+ * @return True if lagging -+ */ -+ public static boolean isLagging() { -+ return server.isLagging(); -+ } - // Purpur end - } -diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index fc2ff6fce4d7b4466cb7936b530c1c0d17f9f24f..5b8ce8a5658021c6fdfeabca6c7bb31ebc58e71d 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2424,5 +2424,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - */ - @NotNull - String getServerName(); -+ -+ /** -+ * Check if server is lagging according to laggy threshold setting -+ * -+ * @return True if lagging -+ */ -+ boolean isLagging(); - // Purpur end - } diff --git a/patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch deleted file mode 100644 index fa03a99a9..000000000 --- a/patches/api/0015-PlayerSetSpawnerTypeWithEggEvent.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 5 Jul 2019 18:21:15 -0500 -Subject: [PATCH] PlayerSetSpawnerTypeWithEggEvent - - -diff --git a/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..519809eab5d926dc7b0a7bad5d446d0defc099dc ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java -@@ -0,0 +1,85 @@ -+package org.purpurmc.purpur.event; -+ -+import org.bukkit.block.Block; -+import org.bukkit.block.CreatureSpawner; -+import org.bukkit.entity.EntityType; -+import org.bukkit.entity.Player; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.player.PlayerEvent; -+import org.jetbrains.annotations.NotNull; -+ -+public class PlayerSetSpawnerTypeWithEggEvent extends PlayerEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Block block; -+ private final CreatureSpawner spawner; -+ private EntityType type; -+ private boolean cancel; -+ -+ public PlayerSetSpawnerTypeWithEggEvent(@NotNull Player player, @NotNull Block block, @NotNull CreatureSpawner spawner, @NotNull EntityType type) { -+ super(player); -+ this.block = block; -+ this.spawner = spawner; -+ this.type = type; -+ } -+ -+ /** -+ * Get the spawner Block in the world -+ * -+ * @return Spawner Block -+ */ -+ @NotNull -+ public Block getBlock() { -+ return block; -+ } -+ -+ /** -+ * Get the spawner state -+ * -+ * @return Spawner state -+ */ -+ @NotNull -+ public CreatureSpawner getSpawner() { -+ return spawner; -+ } -+ -+ /** -+ * Gets the EntityType being set on the spawner -+ * -+ * @return EntityType being set -+ */ -+ @NotNull -+ public EntityType getEntityType() { -+ return type; -+ } -+ -+ /** -+ * Sets the EntityType being set on the spawner -+ * -+ * @param type EntityType to set -+ */ -+ public void setEntityType(@NotNull EntityType type) { -+ this.type = type; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return cancel; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancel = cancel; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0016-Player-invulnerabilities.patch b/patches/api/0016-Player-invulnerabilities.patch deleted file mode 100644 index 32920bcf2..000000000 --- a/patches/api/0016-Player-invulnerabilities.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 2 May 2020 20:55:31 -0500 -Subject: [PATCH] Player invulnerabilities - - -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 2ddacd7f74cba66f67ab6957e03522453de01117..1994acf66919c92f59e2a9f2f873f50b73d76d1a 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3298,5 +3298,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * Reset the idle timer back to 0 - */ - void resetIdleTimer(); -+ -+ /** -+ * Check if player is invulnerable from recently spawning or accepting a resource pack -+ * -+ * @return True if invulnerable -+ */ -+ boolean isSpawnInvulnerable(); -+ -+ /** -+ * Get invulnerable ticks remaining -+ * -+ * @return Invulnerable ticks -+ */ -+ int getSpawnInvulnerableTicks(); -+ -+ /** -+ * Set invulnerable ticks remaining -+ * -+ * @param invulnerableTicks Invulnerable ticks remaining -+ */ -+ void setSpawnInvulnerableTicks(int invulnerableTicks); - // Purpur end - } diff --git a/patches/api/0017-Anvil-API.patch b/patches/api/0017-Anvil-API.patch deleted file mode 100644 index 5ed2626ed..000000000 --- a/patches/api/0017-Anvil-API.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 19 Apr 2020 00:25:09 -0500 -Subject: [PATCH] Anvil API - - -diff --git a/src/main/java/org/bukkit/inventory/AnvilInventory.java b/src/main/java/org/bukkit/inventory/AnvilInventory.java -index c60be4fd24c7fdf65251dd6169e5e1ac3b588d95..569deccd2f1cf21da9b5906433ac493c1f2081be 100644 ---- a/src/main/java/org/bukkit/inventory/AnvilInventory.java -+++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java -@@ -123,4 +123,14 @@ public interface AnvilInventory extends Inventory { - setItem(2, result); - } - // Paper end -+ -+ // Purpur start -+ boolean canBypassCost(); -+ -+ void setBypassCost(boolean bypassCost); -+ -+ boolean canDoUnsafeEnchants(); -+ -+ void setDoUnsafeEnchants(boolean canDoUnsafeEnchants); -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b363c91a29f826910db22f2643decf996a067ab5 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java -@@ -0,0 +1,52 @@ -+package org.purpurmc.purpur.event.inventory; -+ -+import org.bukkit.entity.HumanEntity; -+import org.bukkit.entity.Player; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.inventory.InventoryEvent; -+import org.bukkit.inventory.AnvilInventory; -+import org.bukkit.inventory.InventoryView; -+import org.bukkit.inventory.ItemStack; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a player takes the result item out of an anvil -+ */ -+public class AnvilTakeResultEvent extends InventoryEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Player player; -+ private final ItemStack result; -+ -+ public AnvilTakeResultEvent(@NotNull HumanEntity player, @NotNull InventoryView view, @NotNull ItemStack result) { -+ super(view); -+ this.player = (Player) player; -+ this.result = result; -+ } -+ -+ @NotNull -+ public Player getPlayer() { -+ return player; -+ } -+ -+ @NotNull -+ public ItemStack getResult() { -+ return result; -+ } -+ -+ @NotNull -+ @Override -+ public AnvilInventory getInventory() { -+ return (AnvilInventory) super.getInventory(); -+ } -+ -+ @NotNull -+ @Override -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fd6a5a3589d436c2aaf988fd305899695799d3bb ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java -@@ -0,0 +1,35 @@ -+package org.purpurmc.purpur.event.inventory; -+ -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.inventory.InventoryEvent; -+import org.bukkit.inventory.AnvilInventory; -+import org.bukkit.inventory.InventoryView; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when anvil slots change, triggering the result slot to be updated -+ */ -+public class AnvilUpdateResultEvent extends InventoryEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ -+ public AnvilUpdateResultEvent(@NotNull InventoryView view) { -+ super(view); -+ } -+ -+ @NotNull -+ @Override -+ public AnvilInventory getInventory() { -+ return (AnvilInventory) super.getInventory(); -+ } -+ -+ @NotNull -+ @Override -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0019-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/api/0019-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch deleted file mode 100644 index 44337b14f..000000000 --- a/patches/api/0019-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 28 Jun 2020 21:50:55 -0500 -Subject: [PATCH] Phantoms attracted to crystals and crystals shoot phantoms - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 6f55ccfff74b361854bf424fd93f0428e66a6bd7..038b990fcd72847e7ea43eda3c24c4ce1a3d5bc4 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -205,6 +205,8 @@ public interface VanillaGoal extends Goal { - GoalKey MOB_HAS_RIDER = GoalKey.of(Mob.class, NamespacedKey.minecraft("has_rider")); - GoalKey HORSE_HAS_RIDER = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("horse_has_rider")); - GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider")); -+ GoalKey FIND_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("find_crystal")); -+ GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal")); - // Purpur end - - /** diff --git a/patches/api/0023-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/api/0023-Add-option-to-disable-zombie-aggressiveness-towards-.patch deleted file mode 100644 index 60a235185..000000000 --- a/patches/api/0023-Add-option-to-disable-zombie-aggressiveness-towards-.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: nitricspace -Date: Wed, 23 Sep 2020 22:14:38 +0100 -Subject: [PATCH] Add option to disable zombie aggressiveness towards villagers - when lagging - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 038b990fcd72847e7ea43eda3c24c4ce1a3d5bc4..150aa7b0876b50e2e92bab18721c548b8e6d5515 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -207,6 +207,8 @@ public interface VanillaGoal extends Goal { - GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider")); - GoalKey FIND_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("find_crystal")); - GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal")); -+ GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager")); -+ GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager")); - // Purpur end - - /** diff --git a/patches/api/0024-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/api/0024-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch deleted file mode 100644 index af1641fb8..000000000 --- a/patches/api/0024-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 2 Oct 2020 17:43:24 -0500 -Subject: [PATCH] Add predicate to recipe's ExactChoice ingredient - - -diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java -index 523818cbb0d6c90481ec97123e7fe0e2ff4eea14..bfeb8171a723d84b94bfaacd8aaf7d4d48ecd051 100644 ---- a/src/main/java/org/bukkit/inventory/RecipeChoice.java -+++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java -@@ -10,6 +10,7 @@ import java.util.function.Predicate; - import org.bukkit.Material; - import org.bukkit.Tag; - import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; // Purpur - - /** - * Represents a potential item match within a recipe. All choices within a -@@ -150,6 +151,7 @@ public interface RecipeChoice extends Predicate, Cloneable { - public static class ExactChoice implements RecipeChoice { - - private List choices; -+ private Predicate predicate; // Purpur - - public ExactChoice(@NotNull ItemStack stack) { - this(Arrays.asList(stack)); -@@ -194,6 +196,7 @@ public interface RecipeChoice extends Predicate, Cloneable { - - @Override - public boolean test(@NotNull ItemStack t) { -+ if (predicate != null) return predicate.test(t); // Purpur - for (ItemStack match : choices) { - if (t.isSimilar(match)) { - return true; -@@ -203,6 +206,17 @@ public interface RecipeChoice extends Predicate, Cloneable { - return false; - } - -+ // Purpur start -+ @Nullable -+ public Predicate getPredicate() { -+ return predicate; -+ } -+ -+ public void setPredicate(@Nullable Predicate predicate) { -+ this.predicate = predicate; -+ } -+ // Purpur end -+ - @Override - public int hashCode() { - int hash = 7; diff --git a/patches/api/0025-Rabid-Wolf-API.patch b/patches/api/0025-Rabid-Wolf-API.patch deleted file mode 100644 index ae345b5d7..000000000 --- a/patches/api/0025-Rabid-Wolf-API.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 8 Dec 2020 17:15:15 -0500 -Subject: [PATCH] Rabid Wolf API - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 150aa7b0876b50e2e92bab18721c548b8e6d5515..54ecf2998eb7ee711fb56e3d8ed3f49129db4147 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -209,6 +209,7 @@ public interface VanillaGoal extends Goal { - GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal")); - GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager")); - GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager")); -+ GoalKey AVOID_RABID_WOLF = GoalKey.of(Wolf.class, NamespacedKey.minecraft("avoid_rabid_wolf")); - // Purpur end - - /** -diff --git a/src/main/java/org/bukkit/entity/Wolf.java b/src/main/java/org/bukkit/entity/Wolf.java -index 84db38388bf7a58e66d6cd29620b4fe64b0a897e..82ebd99549ce9f9e6427a50cef424e9007735708 100644 ---- a/src/main/java/org/bukkit/entity/Wolf.java -+++ b/src/main/java/org/bukkit/entity/Wolf.java -@@ -69,4 +69,20 @@ public interface Wolf extends Tameable, Sittable, io.papermc.paper.entity.Collar - * @param interested Whether the wolf is interested - */ - public void setInterested(boolean interested); -+ -+ // Purpur start -+ /** -+ * Checks if this wolf is rabid -+ * -+ * @return whether the wolf is rabid -+ */ -+ public boolean isRabid(); -+ -+ /** -+ * Sets this wolf to be rabid or not -+ * -+ * @param rabid whether the wolf should be rabid -+ */ -+ public void setRabid(boolean rabid); -+ // Purpur end - } diff --git a/patches/api/0026-PlayerBookTooLargeEvent.patch b/patches/api/0026-PlayerBookTooLargeEvent.patch deleted file mode 100644 index 937bb73c4..000000000 --- a/patches/api/0026-PlayerBookTooLargeEvent.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 23 Dec 2020 00:43:27 -0600 -Subject: [PATCH] PlayerBookTooLargeEvent - - -diff --git a/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java b/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c88394336bc9ab0f66a2af24d393f4a176a234d5 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java -@@ -0,0 +1,65 @@ -+package org.purpurmc.purpur.event.player; -+ -+import org.bukkit.Bukkit; -+import org.bukkit.entity.Player; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.player.PlayerEvent; -+import org.bukkit.inventory.ItemStack; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a player tries to bypass book limitations -+ */ -+public class PlayerBookTooLargeEvent extends PlayerEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final ItemStack book; -+ private boolean kickPlayer = true; -+ -+ /** -+ * @param player The player -+ * @param book The book -+ */ -+ public PlayerBookTooLargeEvent(@NotNull Player player, @NotNull ItemStack book) { -+ super(player, !Bukkit.isPrimaryThread()); -+ this.book = book; -+ } -+ -+ /** -+ * Get the book containing the wanted edits -+ * -+ * @return The book -+ */ -+ @NotNull -+ public ItemStack getBook() { -+ return book; -+ } -+ -+ /** -+ * Whether server should kick the player or not -+ * -+ * @return True to kick player -+ */ -+ public boolean shouldKickPlayer() { -+ return kickPlayer; -+ } -+ -+ /** -+ * Whether server should kick the player or not -+ * -+ * @param kickPlayer True to kick player -+ */ -+ public void setShouldKickPlayer(boolean kickPlayer) { -+ this.kickPlayer = kickPlayer; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0027-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0027-Full-netherite-armor-grants-fire-resistance.patch deleted file mode 100644 index 5082cfcd3..000000000 --- a/patches/api/0027-Full-netherite-armor-grants-fire-resistance.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 24 Dec 2020 11:00:04 -0600 -Subject: [PATCH] Full netherite armor grants fire resistance - - -diff --git a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java -index c9f395064656dd0126410eb3c6e197baa450c063..13156a12e5df50cdc1e465dc0bd9d94108275629 100644 ---- a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java -+++ b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java -@@ -217,6 +217,12 @@ public class EntityPotionEffectEvent extends EntityEvent implements Cancellable - * When all effects are removed due to a bucket of milk. - */ - MILK, -+ // Purpur start -+ /** -+ * When a player wears full netherite armor -+ */ -+ NETHERITE_ARMOR, -+ // Purpur end - /** - * When a player gets bad omen after killing a patrol captain. - */ diff --git a/patches/api/0028-Add-EntityTeleportHinderedEvent.patch b/patches/api/0028-Add-EntityTeleportHinderedEvent.patch deleted file mode 100644 index a172c1de1..000000000 --- a/patches/api/0028-Add-EntityTeleportHinderedEvent.patch +++ /dev/null @@ -1,141 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Mariell Hoversholm -Date: Sat, 9 Jan 2021 15:26:04 +0100 -Subject: [PATCH] Add EntityTeleportHinderedEvent - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c66eb163877e872f234d86dc244cab7efeb818cd ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java -@@ -0,0 +1,117 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.entity.Entity; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * Fired when an entity is hindered from teleporting. -+ */ -+public class EntityTeleportHinderedEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ -+ @NotNull -+ private final Reason reason; -+ -+ @Nullable -+ private final TeleportCause teleportCause; -+ -+ private boolean retry = false; -+ -+ public EntityTeleportHinderedEvent(@NotNull Entity what, @NotNull Reason reason, -+ @Nullable TeleportCause teleportCause) { -+ super(what); -+ this.reason = reason; -+ this.teleportCause = teleportCause; -+ } -+ -+ /** -+ * @return why the teleport was hindered. -+ */ -+ @NotNull -+ public Reason getReason() { -+ return reason; -+ } -+ -+ /** -+ * @return why the teleport occurred if cause was given, otherwise {@code null}. -+ */ -+ @Nullable -+ public TeleportCause getTeleportCause() { -+ return teleportCause; -+ } -+ -+ /** -+ * Whether the teleport should be retried. -+ *

-+ * Note that this can put the server in a never-ending loop of trying to teleport someone resulting in a stack -+ * overflow. Do not retry more than necessary. -+ *

-+ * -+ * @return whether the teleport should be retried. -+ */ -+ public boolean shouldRetry() { -+ return retry; -+ } -+ -+ /** -+ * Sets whether the teleport should be retried. -+ *

-+ * Note that this can put the server in a never-ending loop of trying to teleport someone resulting in a stack -+ * overflow. Do not retry more than necessary. -+ *

-+ * -+ * @param retry whether the teleport should be retried. -+ */ -+ public void setShouldRetry(boolean retry) { -+ this.retry = retry; -+ } -+ -+ /** -+ * Calls the event and tests if should retry. -+ * -+ * @return whether the teleport should be retried. -+ */ -+ @Override -+ public boolean callEvent() { -+ super.callEvent(); -+ return shouldRetry(); -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+ -+ /** -+ * Reason for hindrance in teleports. -+ */ -+ public enum Reason { -+ /** -+ * The teleported entity is a passenger of another entity. -+ */ -+ IS_PASSENGER, -+ -+ /** -+ * The teleported entity has passengers. -+ */ -+ IS_VEHICLE, -+ -+ /** -+ * The teleport event was cancelled. -+ *

-+ * This is only caused by players teleporting. -+ *

-+ */ -+ EVENT_CANCELLED, -+ } -+} diff --git a/patches/api/0029-Add-enchantment-target-for-bows-and-crossbows.patch b/patches/api/0029-Add-enchantment-target-for-bows-and-crossbows.patch deleted file mode 100644 index d9680f160..000000000 --- a/patches/api/0029-Add-enchantment-target-for-bows-and-crossbows.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 23 Mar 2021 15:01:03 -0500 -Subject: [PATCH] Add enchantment target for bows and crossbows - - -diff --git a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -index 455ff52d90565838fe7640c3f045b27082a6c2f1..5831ffe24eed01311c71989dcb1830dbc395607b 100644 ---- a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -+++ b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -@@ -227,6 +227,18 @@ public enum EnchantmentTarget { - public boolean includes(@NotNull Material item) { - return BREAKABLE.includes(item) || (WEARABLE.includes(item) && !item.equals(Material.ELYTRA)) || item.equals(Material.COMPASS); - } -+ // Purpur start -+ }, -+ -+ /** -+ * Allow the Enchantment to be placed on bows and crossbows. -+ */ -+ BOW_AND_CROSSBOW { -+ @Override -+ public boolean includes(@NotNull Material item) { -+ return item.equals(Material.BOW) || item.equals(Material.CROSSBOW); -+ } -+ // Purpur end - }; - - /** diff --git a/patches/api/0030-Iron-golem-poppy-calms-anger.patch b/patches/api/0030-Iron-golem-poppy-calms-anger.patch deleted file mode 100644 index 03f75e7c6..000000000 --- a/patches/api/0030-Iron-golem-poppy-calms-anger.patch +++ /dev/null @@ -1,18 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 13 May 2021 21:38:01 -0500 -Subject: [PATCH] Iron golem poppy calms anger - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 54ecf2998eb7ee711fb56e3d8ed3f49129db4147..b1f35c68373edfe666ca05b50f0ec022a1859ce9 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -210,6 +210,7 @@ public interface VanillaGoal extends Goal { - GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager")); - GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager")); - GoalKey AVOID_RABID_WOLF = GoalKey.of(Wolf.class, NamespacedKey.minecraft("avoid_rabid_wolf")); -+ GoalKey RECEIVE_FLOWER = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("receive_flower")); - // Purpur end - - /** diff --git a/patches/api/0031-API-for-any-mob-to-burn-daylight.patch b/patches/api/0031-API-for-any-mob-to-burn-daylight.patch deleted file mode 100644 index 38781b67f..000000000 --- a/patches/api/0031-API-for-any-mob-to-burn-daylight.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Tue, 25 May 2021 16:30:30 -0400 -Subject: [PATCH] API for any mob to burn daylight - -Co-authored by: Encode42 - -diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index de60e8773e58ef62c15d8f7e293313a62dff674b..52867495d0f746ff40e802c4f1018511e58fd03e 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1079,5 +1079,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent - * @return True if ridable in water - */ - boolean isRidableInWater(); -+ -+ /** -+ * Checks if the entity is in daylight -+ * -+ * @return True if in daylight -+ */ -+ boolean isInDaylight(); - // Purpur end - } -diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java -index accefccca9993edd00f72b4e5779fce0b3ee628f..994e026d68fcda9a4c34a8b161a06623f4437dff 100644 ---- a/src/main/java/org/bukkit/entity/LivingEntity.java -+++ b/src/main/java/org/bukkit/entity/LivingEntity.java -@@ -1214,5 +1214,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource - * @param slot Equipment slot to play break animation for - */ - void broadcastItemBreak(@NotNull org.bukkit.inventory.EquipmentSlot slot); -+ -+ /** -+ * If this mob will burn in the sunlight -+ * -+ * @return True if mob will burn in sunlight -+ */ -+ boolean shouldBurnInDay(); -+ -+ /** -+ * Set if this mob should burn in the sunlight -+ * -+ * @param shouldBurnInDay True to burn in sunlight -+ */ -+ void setShouldBurnInDay(boolean shouldBurnInDay); - // Purpur end - } diff --git a/patches/api/0033-Fix-default-permission-system.patch b/patches/api/0033-Fix-default-permission-system.patch deleted file mode 100644 index 754c4e1e3..000000000 --- a/patches/api/0033-Fix-default-permission-system.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 30 Jun 2021 17:44:27 -0500 -Subject: [PATCH] Fix default permission system - - -diff --git a/src/main/java/org/bukkit/permissions/PermissibleBase.java b/src/main/java/org/bukkit/permissions/PermissibleBase.java -index cd3296fea01648592d2af89b3d80135acb6d0958..45797a6fbae1d8edc4211cb30def24ad4f59bd49 100644 ---- a/src/main/java/org/bukkit/permissions/PermissibleBase.java -+++ b/src/main/java/org/bukkit/permissions/PermissibleBase.java -@@ -168,7 +168,7 @@ public class PermissibleBase implements Permissible { - - for (Permission perm : defaults) { - String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH); -- permissions.put(name, new PermissionAttachmentInfo(parent, name, null, true)); -+ permissions.put(name, new PermissionAttachmentInfo(parent, name, null, perm.getDefault().getValue(isOp()))); // Purpur - Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); - calculateChildPermissions(perm.getChildren(), false, null); - } -@@ -196,7 +196,7 @@ public class PermissibleBase implements Permissible { - String name = entry.getKey(); - - Permission perm = Bukkit.getServer().getPluginManager().getPermission(name); -- boolean value = entry.getValue() ^ invert; -+ boolean value = (entry.getValue() == null && perm != null ? perm.getDefault().getValue(isOp()) : entry.getValue()) ^ invert; // Purpur - String lname = name.toLowerCase(java.util.Locale.ENGLISH); - - permissions.put(lname, new PermissionAttachmentInfo(parent, lname, attachment, value)); -diff --git a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -index 8e481e3815f5645ee92f0d229e5ff25c8fc9a6c2..10627d2a11251a8cb01bbc3f6242d66f3505a16e 100644 ---- a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -+++ b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java -@@ -31,7 +31,7 @@ public final class DefaultPermissions { - - if (withLegacy) { - Permission legacy = new Permission(LEGACY_PREFIX + result.getName(), result.getDescription(), PermissionDefault.FALSE); -- legacy.getChildren().put(result.getName(), true); -+ legacy.getChildren().put(result.getName(), null); // Purpur - registerPermission(perm, false); - } - -@@ -40,7 +40,7 @@ public final class DefaultPermissions { - - @NotNull - public static Permission registerPermission(@NotNull Permission perm, @NotNull Permission parent) { -- parent.getChildren().put(perm.getName(), true); -+ parent.getChildren().put(perm.getName(), null); // Purpur - return registerPermission(perm); - } - -@@ -53,7 +53,7 @@ public final class DefaultPermissions { - @NotNull - public static Permission registerPermission(@NotNull String name, @Nullable String desc, @NotNull Permission parent) { - Permission perm = registerPermission(name, desc); -- parent.getChildren().put(perm.getName(), true); -+ parent.getChildren().put(perm.getName(), null); // Purpur - return perm; - } - -@@ -66,7 +66,7 @@ public final class DefaultPermissions { - @NotNull - public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @NotNull Permission parent) { - Permission perm = registerPermission(name, desc, def); -- parent.getChildren().put(perm.getName(), true); -+ parent.getChildren().put(perm.getName(), null); // Purpur - return perm; - } - -@@ -79,7 +79,7 @@ public final class DefaultPermissions { - @NotNull - public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @Nullable Map children, @NotNull Permission parent) { - Permission perm = registerPermission(name, desc, def, children); -- parent.getChildren().put(perm.getName(), true); -+ parent.getChildren().put(perm.getName(), null); // Purpur - return perm; - } - diff --git a/patches/api/0034-Summoner-API.patch b/patches/api/0034-Summoner-API.patch deleted file mode 100644 index bc94bd07e..000000000 --- a/patches/api/0034-Summoner-API.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 3 Jul 2021 18:45:01 -0500 -Subject: [PATCH] Summoner API - - -diff --git a/src/main/java/org/bukkit/entity/IronGolem.java b/src/main/java/org/bukkit/entity/IronGolem.java -index 655e37cb3a09610a3f3df805d6dcad17d722da62..09fd716c8fc9ea34a1cbf87bcbe22df035422a51 100644 ---- a/src/main/java/org/bukkit/entity/IronGolem.java -+++ b/src/main/java/org/bukkit/entity/IronGolem.java -@@ -19,4 +19,20 @@ public interface IronGolem extends Golem { - * player created, false if you want it to be a natural village golem. - */ - public void setPlayerCreated(boolean playerCreated); -+ -+ // Purpur start -+ /** -+ * Get the player that summoned this iron golem -+ * -+ * @return UUID of summoner -+ */ -+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); -+ -+ /** -+ * Set the player that summoned this iron golem -+ * -+ * @param summoner UUID of summoner -+ */ -+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/entity/Snowman.java b/src/main/java/org/bukkit/entity/Snowman.java -index 7fbfdb07585c7b28acea1f0c1f58ada0cc744441..21fcca092e2e31baa5ece0de9e44e3fade8c7123 100644 ---- a/src/main/java/org/bukkit/entity/Snowman.java -+++ b/src/main/java/org/bukkit/entity/Snowman.java -@@ -23,4 +23,20 @@ public interface Snowman extends Golem, RangedEntity, io.papermc.paper.entity.Sh - * @param derpMode True to remove the pumpkin, false to add a pumpkin - */ - void setDerp(boolean derpMode); -+ -+ // Purpur start -+ /** -+ * Get the player that summoned this snowman -+ * -+ * @return UUID of summoner -+ */ -+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); -+ -+ /** -+ * Set the player that summoned this snowman -+ * -+ * @param summoner UUID of summoner -+ */ -+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/entity/Wither.java b/src/main/java/org/bukkit/entity/Wither.java -index 14543c2238b45c526dd9aebea2aa5c22f5df54dc..5312daf33405704c74e2c9e109754285ea6cf734 100644 ---- a/src/main/java/org/bukkit/entity/Wither.java -+++ b/src/main/java/org/bukkit/entity/Wither.java -@@ -107,4 +107,20 @@ public interface Wither extends Monster, Boss, com.destroystokyo.paper.entity.Ra - */ - void enterInvulnerabilityPhase(); - // Paper end -+ -+ // Purpur start -+ /** -+ * Get the player that summoned this wither -+ * -+ * @return UUID of summoner -+ */ -+ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); -+ -+ /** -+ * Set the player that summoned this wither -+ * -+ * @param summoner UUID of summoner -+ */ -+ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); -+ // Purpur end - } diff --git a/patches/api/0035-Clean-up-version-command-output.patch b/patches/api/0035-Clean-up-version-command-output.patch deleted file mode 100644 index 1bd0a28dd..000000000 --- a/patches/api/0035-Clean-up-version-command-output.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 15 Jul 2021 23:43:04 -0500 -Subject: [PATCH] Clean up version command output - - -diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java -index a736d7bcdc5861a01b66ba36158db1c716339346..22fc165fd9c95f0f3ae1be7a0857e48cc50fad5b 100644 ---- a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java -+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java -@@ -26,6 +26,12 @@ public interface VersionFetcher { - @NotNull - Component getVersionMessage(@NotNull String serverVersion); - -+ // Purpur start -+ default int distance() { -+ return 0; -+ } -+ // Purpur end -+ - class DummyVersionFetcher implements VersionFetcher { - - @Override -diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -index fd5d9881abfd930bb883120f018f76dc78b62b14..d3dadad49df09e85c724c93e8cc88da2c985e9b4 100644 ---- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java -+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -@@ -214,7 +214,7 @@ public class VersionCommand extends BukkitCommand { - String version = Bukkit.getVersion(); - // Paper start - if (version.startsWith("null")) { // running from ide? -- setVersionMessage(Component.text("Unknown version, custom build?", NamedTextColor.YELLOW)); -+ setVersionMessage(Component.text("* Unknown version, custom build?", NamedTextColor.RED)); // Purpur - return; - } - setVersionMessage(getVersionFetcher().getVersionMessage(version)); -@@ -255,9 +255,11 @@ public class VersionCommand extends BukkitCommand { - // Paper start - private void setVersionMessage(final @NotNull Component msg) { - lastCheck = System.currentTimeMillis(); -- final Component message = Component.textOfChildren( -- Component.text(Bukkit.getVersionMessage(), NamedTextColor.WHITE), -- Component.newline(), -+ // Purpur start -+ int distance = getVersionFetcher().distance(); -+ final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), -+ ChatColor.parseMM("Current: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), -+ // Purpur end - msg - ); - this.versionMessage = Component.text() diff --git a/patches/api/0037-Added-the-ability-to-add-combustible-items.patch b/patches/api/0037-Added-the-ability-to-add-combustible-items.patch deleted file mode 100644 index 58ef1ffd8..000000000 --- a/patches/api/0037-Added-the-ability-to-add-combustible-items.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 9 Aug 2021 13:22:03 +0200 -Subject: [PATCH] Added the ability to add combustible items - - -diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index a32155e0cc63d4e4714a953edbae0b19544ff3ef..222dcb75a850d224ac22b2e06895233527cffc76 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2775,5 +2775,24 @@ public final class Bukkit { - public static boolean isLagging() { - return server.isLagging(); - } -+ -+ /** -+ * Add an Item as fuel for furnaces -+ * -+ * @param material The material that will be the fuel -+ * @param burnTime The time (in ticks) this item will burn for -+ */ -+ public static void addFuel(@NotNull Material material, int burnTime) { -+ server.addFuel(material, burnTime); -+ } -+ -+ /** -+ * Remove an item as fuel for furnaces -+ * -+ * @param material The material that will no longer be a fuel -+ */ -+ public static void removeFuel(@NotNull Material material) { -+ server.removeFuel(material); -+ } - // Purpur end - } -diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 5b8ce8a5658021c6fdfeabca6c7bb31ebc58e71d..3bc9fa8b68b284516ddbf0ace0c1dc52768307cb 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2431,5 +2431,20 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - * @return True if lagging - */ - boolean isLagging(); -+ -+ /** -+ * Add an Item as fuel for furnaces -+ * -+ * @param material The material that will be the fuel -+ * @param burnTime The time (in ticks) this item will burn for -+ */ -+ public void addFuel(@NotNull Material material, int burnTime); -+ -+ /** -+ * Remove an item as fuel for furnaces -+ * -+ * @param material The material that will no longer be a fuel -+ */ -+ public void removeFuel(@NotNull Material material); - // Purpur end - } diff --git a/patches/api/0039-Grindstone-API.patch b/patches/api/0039-Grindstone-API.patch deleted file mode 100644 index 5a699b1c1..000000000 --- a/patches/api/0039-Grindstone-API.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 27 Dec 2021 08:10:50 -0600 -Subject: [PATCH] Grindstone API - - -diff --git a/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java b/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..eebb5d124456b8209d1b8e8cc4cb772dd3714f04 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java -@@ -0,0 +1,72 @@ -+package org.purpurmc.purpur.event.inventory; -+ -+import org.bukkit.entity.HumanEntity; -+import org.bukkit.entity.Player; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.inventory.InventoryEvent; -+import org.bukkit.inventory.GrindstoneInventory; -+import org.bukkit.inventory.InventoryView; -+import org.bukkit.inventory.ItemStack; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a player takes the result item out of a Grindstone -+ */ -+public class GrindstoneTakeResultEvent extends InventoryEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Player player; -+ private final ItemStack result; -+ private int experienceAmount; -+ -+ public GrindstoneTakeResultEvent(@NotNull HumanEntity player, @NotNull InventoryView view, @NotNull ItemStack result, int experienceAmount) { -+ super(view); -+ this.player = (Player) player; -+ this.result = result; -+ this.experienceAmount = experienceAmount; -+ } -+ -+ @NotNull -+ public Player getPlayer() { -+ return player; -+ } -+ -+ @NotNull -+ public ItemStack getResult() { -+ return result; -+ } -+ -+ @NotNull -+ @Override -+ public GrindstoneInventory getInventory() { -+ return (GrindstoneInventory) super.getInventory(); -+ } -+ -+ /** -+ * Get the amount of experience this transaction will give -+ * -+ * @return Amount of experience to give -+ */ -+ public int getExperienceAmount() { -+ return this.experienceAmount; -+ } -+ -+ /** -+ * Set the amount of experience this transaction will give -+ * -+ * @param experienceAmount Amount of experience to give -+ */ -+ public void setExperienceAmount(int experienceAmount) { -+ this.experienceAmount = experienceAmount; -+ } -+ -+ @NotNull -+ @Override -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0040-Shears-can-have-looting-enchantment.patch b/patches/api/0040-Shears-can-have-looting-enchantment.patch deleted file mode 100644 index 53f2a3f93..000000000 --- a/patches/api/0040-Shears-can-have-looting-enchantment.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 3 Jan 2022 02:00:50 -0600 -Subject: [PATCH] Shears can have looting enchantment - - -diff --git a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -index 5831ffe24eed01311c71989dcb1830dbc395607b..45f5493eebfecf56b7c0ef4659c078dfc62c0612 100644 ---- a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -+++ b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java -@@ -238,6 +238,16 @@ public enum EnchantmentTarget { - public boolean includes(@NotNull Material item) { - return item.equals(Material.BOW) || item.equals(Material.CROSSBOW); - } -+ }, -+ -+ /** -+ * Allow the Enchantment to be placed on shears. -+ */ -+ WEAPON_AND_SHEARS { -+ @Override -+ public boolean includes(@NotNull Material item) { -+ return WEAPON.includes(item) || item.equals(Material.SHEARS); -+ } - // Purpur end - }; - diff --git a/patches/api/0041-Lobotomize-stuck-villagers.patch b/patches/api/0041-Lobotomize-stuck-villagers.patch deleted file mode 100644 index 03b9bf50d..000000000 --- a/patches/api/0041-Lobotomize-stuck-villagers.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 24 Jan 2022 20:42:22 -0600 -Subject: [PATCH] Lobotomize stuck villagers - - -diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java -index 3bc24457d143449e6a338d79becf7c39b9f81054..4a5edf4e72e81b22c1abb2ade244f7f4292e993c 100644 ---- a/src/main/java/org/bukkit/entity/Villager.java -+++ b/src/main/java/org/bukkit/entity/Villager.java -@@ -328,4 +328,14 @@ public interface Villager extends AbstractVillager { - */ - public void clearReputations(); - // Paper end -+ -+ // Purpur start -+ -+ /** -+ * Check if villager is currently lobotomized -+ * -+ * @return True if lobotomized -+ */ -+ boolean isLobotomized(); -+ // Purpur end - } diff --git a/patches/api/0042-Add-local-difficulty-api.patch b/patches/api/0042-Add-local-difficulty-api.patch deleted file mode 100644 index 3b9825738..000000000 --- a/patches/api/0042-Add-local-difficulty-api.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 9 Jul 2022 00:57:26 -0500 -Subject: [PATCH] Add local difficulty api - - -diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 6917931966377c51db88a3364997a110dd987970..1fba792419ea6b5e8c640a2599e4b2dd16ee87d0 100644 ---- a/src/main/java/org/bukkit/World.java -+++ b/src/main/java/org/bukkit/World.java -@@ -3992,6 +3992,16 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient - @Nullable - public DragonBattle getEnderDragonBattle(); - -+ // Purpur start -+ /** -+ * Gets the local difficulty (based on inhabited time) at a location -+ * -+ * @param location Location to check -+ * @return The local difficulty -+ */ -+ public float getLocalDifficultyAt(@NotNull Location location); -+ // Purpur end -+ - /** - * Get all {@link FeatureFlag} enabled in this world. - * diff --git a/patches/api/0043-Remove-Timings.patch b/patches/api/0043-Remove-Timings.patch deleted file mode 100644 index 75f6b7465..000000000 --- a/patches/api/0043-Remove-Timings.patch +++ /dev/null @@ -1,168 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 1 Jul 2022 04:03:26 -0500 -Subject: [PATCH] Remove Timings - - -diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java -index 8f29c1561ba5916cb5634392edd8bd2a5a294a51..6fbc64e0f214d0c8e5afcbe385e414a4e1fe1c72 100644 ---- a/src/main/java/co/aikar/timings/TimedEventExecutor.java -+++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java -@@ -77,9 +77,9 @@ public class TimedEventExecutor implements EventExecutor { - executor.execute(listener, event); - return; - } -- try (Timing ignored = timings.startTiming()){ -+ //try (Timing ignored = timings.startTiming()){ // Purpur - executor.execute(listener, event); -- } -+ //} // Purpur - } - - @Override -diff --git a/src/main/java/co/aikar/timings/Timing.java b/src/main/java/co/aikar/timings/Timing.java -index 7514fad26f955329f8bf17ff17db75f0c8301ee5..1d866e980abc542bdfee1ce082cd9cdd7761e9f7 100644 ---- a/src/main/java/co/aikar/timings/Timing.java -+++ b/src/main/java/co/aikar/timings/Timing.java -@@ -39,6 +39,7 @@ public interface Timing extends AutoCloseable { - * @return Timing - */ - @NotNull -+ @io.papermc.paper.annotation.DoNotUse // Purpur - Timing startTiming(); - - /** -@@ -46,6 +47,7 @@ public interface Timing extends AutoCloseable { - * - * Will automatically be called when this Timing is used with try-with-resources - */ -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void stopTiming(); - - /** -@@ -56,6 +58,7 @@ public interface Timing extends AutoCloseable { - * @return Timing - */ - @NotNull -+ @io.papermc.paper.annotation.DoNotUse // Purpur - Timing startTimingIfSync(); - - /** -@@ -65,12 +68,14 @@ public interface Timing extends AutoCloseable { - * - * But only if we are on the primary thread. - */ -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void stopTimingIfSync(); - - /** - * @deprecated Doesn't do anything - Removed - */ - @Deprecated -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void abort(); - - /** -@@ -82,5 +87,6 @@ public interface Timing extends AutoCloseable { - TimingHandler getTimingHandler(); - - @Override -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void close(); - } -diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java -index 9812d668ad945aba486fbf6d5bf83c4292cb5d03..752d54830aa8baa1450bf72da03ae55ed30293c2 100644 ---- a/src/main/java/co/aikar/timings/Timings.java -+++ b/src/main/java/co/aikar/timings/Timings.java -@@ -124,7 +124,7 @@ public final class Timings { - @NotNull - public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) { - Timing timing = of(plugin, name, groupHandler); -- timing.startTiming(); -+ //timing.startTiming(); // Purpur - return timing; - } - -@@ -145,9 +145,11 @@ public final class Timings { - * @param enabled Should timings be reported - */ - public static void setTimingsEnabled(boolean enabled) { -- timingsEnabled = enabled; -- warnAboutDeprecationOnEnable(); -- reset(); -+ // Purpur start - we don't do that here... -+ timingsEnabled = false; -+ //warnAboutDeprecationOnEnable(); -+ //reset(); -+ // Purpur end - } - - private static void warnAboutDeprecationOnEnable() { -diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java -index e801e79fa57c44b2e5d359647c920f88064826f1..1abfcee0f6d632f4cd8d74b4994a90c9ea9d254c 100644 ---- a/src/main/java/co/aikar/timings/TimingsCommand.java -+++ b/src/main/java/co/aikar/timings/TimingsCommand.java -@@ -45,7 +45,7 @@ public class TimingsCommand extends BukkitCommand { - public TimingsCommand(@NotNull String name) { - super(name); - this.description = "Manages Spigot Timings data to see performance of the server."; -- this.usageMessage = "/timings "; -+ this.usageMessage = "/timings";// "; // Purpur - this.setPermission("bukkit.command.timings"); - } - -@@ -54,8 +54,12 @@ public class TimingsCommand extends BukkitCommand { - if (!testPermission(sender)) { - return true; - } -- if (false) { -- sender.sendMessage(Timings.deprecationMessage()); -+ if (true) { -+ net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(); -+ sender.sendMessage(mm.deserialize("Purpur has removed timings to save your performance. Please use /spark instead")); -+ sender.sendMessage(mm.deserialize("For more information, view its documentation at")); -+ sender.sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage")); -+ return true; - } - if (args.length < 1) { - sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); -@@ -115,7 +119,7 @@ public class TimingsCommand extends BukkitCommand { - Preconditions.checkNotNull(args, "Arguments cannot be null"); - Preconditions.checkNotNull(alias, "Alias cannot be null"); - -- if (args.length == 1) { -+ if (false && args.length == 1) { // Purpur - return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, - new ArrayList(TIMINGS_SUBCOMMANDS.size())); - } -diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java -index e842d13febca67ffa1c89fb2c1324d2609fb81fd..5349f16136d9348c374a7dfe5b89a71dfcb0e66d 100644 ---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java -+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -163,10 +163,10 @@ public class SimpleCommandMap implements CommandMap { - // Paper end - - try { -- try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources -+ //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur - // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) - target.execute(sender, sentCommandLabel, parsedArgs); // Purpur -- } // target.timings.stopTiming(); // Spigot // Paper -+ //} // target.timings.stopTiming(); // Spigot // Paper // Purpur - } catch (CommandException ex) { - server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper - //target.timings.stopTiming(); // Spigot // Paper -diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java -index 12946bd55fcf7c40d39081779a7fa30049ee6165..9c2d605c50cbf9aefa56ec209df9f6cea1392e89 100644 ---- a/src/main/java/org/spigotmc/CustomTimingsHandler.java -+++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java -@@ -61,7 +61,7 @@ public final class CustomTimingsHandler { - handler = timing; - } - -- public void startTiming() { handler.startTiming(); } -- public void stopTiming() { handler.stopTiming(); } -+ public void startTiming() { /*handler.startTiming();*/ } // Purpur -+ public void stopTiming() { /*handler.stopTiming();*/ } // Purpur - - } diff --git a/patches/api/0044-Add-Bee-API.patch b/patches/api/0044-Add-Bee-API.patch deleted file mode 100644 index 6efae8520..000000000 --- a/patches/api/0044-Add-Bee-API.patch +++ /dev/null @@ -1,179 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Mon, 25 Jul 2022 19:33:49 +0200 -Subject: [PATCH] Add Bee API - - -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..833f46d1941f377765132fc528c45567ee0290d2 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java -@@ -0,0 +1,48 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.Location; -+import org.bukkit.entity.Bee; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * Called when a bee targets a flower -+ */ -+public class BeeFoundFlowerEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Location location; -+ -+ public BeeFoundFlowerEvent(@NotNull Bee bee, @Nullable Location location) { -+ super(bee); -+ this.location = location; -+ } -+ -+ @Override -+ @NotNull -+ public Bee getEntity() { -+ return (Bee) super.getEntity(); -+ } -+ -+ /** -+ * Returns the location of the flower that the bee targets -+ * -+ * @return The location of the flower -+ */ -+ @Nullable -+ public Location getLocation() { -+ return location; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ae0bb654745724889c67fae9072ae90ea3778ba4 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java -@@ -0,0 +1,47 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.Location; -+import org.bukkit.entity.Bee; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a bee starts pollinating -+ */ -+public class BeeStartedPollinatingEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Location location; -+ -+ public BeeStartedPollinatingEvent(@NotNull Bee bee, @NotNull Location location) { -+ super(bee); -+ this.location = location; -+ } -+ -+ @Override -+ @NotNull -+ public Bee getEntity() { -+ return (Bee) super.getEntity(); -+ } -+ -+ /** -+ * Returns the location of the flower that the bee pollinates -+ * -+ * @return The location of the flower -+ */ -+ @NotNull -+ public Location getLocation() { -+ return this.location; -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ff3c9f075be2f624af8b0ce5fffc5ea69a41f32e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java -@@ -0,0 +1,60 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.Location; -+import org.bukkit.entity.Bee; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * Called when a bee stops pollinating -+ */ -+public class BeeStopPollinatingEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ private final Location location; -+ private final boolean success; -+ -+ public BeeStopPollinatingEvent(@NotNull Bee bee, @Nullable Location location, boolean success) { -+ super(bee); -+ this.location = location; -+ this.success = success; -+ } -+ -+ @Override -+ @NotNull -+ public Bee getEntity() { -+ return (Bee) super.getEntity(); -+ } -+ -+ /** -+ * Returns the location of the flower that the bee stopped pollinating -+ * -+ * @return The location of the flower -+ */ -+ @Nullable -+ public Location getLocation() { -+ return location; -+ } -+ -+ /** -+ * Returns whether the bee successfully pollinated the flower -+ * -+ * @return True if the pollination was successful -+ */ -+ public boolean wasSuccessful() { -+ return success; -+ } -+ -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0045-Debug-Marker-API.patch b/patches/api/0045-Debug-Marker-API.patch deleted file mode 100644 index f5194ac9e..000000000 --- a/patches/api/0045-Debug-Marker-API.patch +++ /dev/null @@ -1,341 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Sat, 23 Jul 2022 14:40:17 +0200 -Subject: [PATCH] Debug Marker API - - -diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 222dcb75a850d224ac22b2e06895233527cffc76..584e3b08935f43beb27f478cc72229b6a5f40689 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2794,5 +2794,89 @@ public final class Bukkit { - public static void removeFuel(@NotNull Material material) { - server.removeFuel(material); - } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration) { -+ server.sendBlockHighlight(location, duration); -+ } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration, int argb) { -+ server.sendBlockHighlight(location, duration, argb); -+ } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text) { -+ server.sendBlockHighlight(location, duration, text); -+ } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb) { -+ server.sendBlockHighlight(location, duration, text, argb); -+ } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency) { -+ server.sendBlockHighlight(location, duration, color, transparency); -+ } -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency) { -+ server.sendBlockHighlight(location, duration, text, color, transparency); -+ } -+ -+ /** -+ * Clears all debug block highlights for all players on the server. -+ */ -+ public static void clearBlockHighlights() { -+ server.clearBlockHighlights(); -+ } - // Purpur end - } -diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 3bc9fa8b68b284516ddbf0ace0c1dc52768307cb..aaef58468a3c31f35e5067ed4263e9dd3fbddddd 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2446,5 +2446,75 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - * @param material The material that will no longer be a fuel - */ - public void removeFuel(@NotNull Material material); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on the server. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Clears all debug block highlights for all players on the server. -+ */ -+ void clearBlockHighlights(); - // Purpur end - } -diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 1fba792419ea6b5e8c640a2599e4b2dd16ee87d0..bf39c6602cfca70a6352519fa26059cd79143cdd 100644 ---- a/src/main/java/org/bukkit/World.java -+++ b/src/main/java/org/bukkit/World.java -@@ -4000,6 +4000,76 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient - * @return The local difficulty - */ - public float getLocalDifficultyAt(@NotNull Location location); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to all players on this world. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Clears all debug block highlights for all players on this world. -+ */ -+ void clearBlockHighlights(); - // Purpur end - - /** -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 1994acf66919c92f59e2a9f2f873f50b73d76d1a..b1a5baf12343f5db11ef58666bf6393b008c7dcf 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3319,5 +3319,75 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * @param invulnerableTicks Invulnerable ticks remaining - */ - void setSpawnInvulnerableTicks(int invulnerableTicks); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Creates debug block highlight on specified block location and show it to this player. -+ *

-+ * Clients may be inconsistent in displaying it. -+ * @param location Location to highlight -+ * @param duration Duration for highlight to show in milliseconds -+ * @param text Text to show above the highlight -+ * @param color Color of the highlight. Will be ignored on some versions of vanilla client -+ * @param transparency Transparency of the highlight -+ * @throws IllegalArgumentException If transparency is outside 0-255 range -+ */ -+ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency); -+ -+ /** -+ * Clears all debug block highlights -+ */ -+ void clearBlockHighlights(); - // Purpur end - } diff --git a/patches/api/0046-Add-death-screen-API.patch b/patches/api/0046-Add-death-screen-API.patch deleted file mode 100644 index 1256eaa99..000000000 --- a/patches/api/0046-Add-death-screen-API.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Fri, 23 Sep 2022 18:35:28 -0700 -Subject: [PATCH] Add death screen API - - -diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index b1a5baf12343f5db11ef58666bf6393b008c7dcf..9193288438671409cc3cf92033ef7cb60d798b69 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3389,5 +3389,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * Clears all debug block highlights - */ - void clearBlockHighlights(); -+ -+ /** -+ * Sends a player the death screen with a specified death message. -+ * -+ * @param message The death message to show the player -+ */ -+ void sendDeathScreen(@NotNull net.kyori.adventure.text.Component message); -+ -+ /** -+ * Sends a player the death screen with a specified death message, -+ * along with the entity that caused the death. -+ * -+ * @param message The death message to show the player -+ * @param killer The entity that killed the player -+ * @deprecated Use {@link #sendDeathScreen(net.kyori.adventure.text.Component)} instead, as 1.20 removed the killer ID from the packet. -+ */ -+ @Deprecated(since = "1.20") -+ default void sendDeathScreen(@NotNull net.kyori.adventure.text.Component message, @Nullable Entity killer) { -+ sendDeathScreen(message); -+ } - // Purpur end - } diff --git a/patches/api/0048-Language-API.patch b/patches/api/0048-Language-API.patch deleted file mode 100644 index 1fbe2ebee..000000000 --- a/patches/api/0048-Language-API.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Sat, 1 Oct 2022 17:08:23 -0700 -Subject: [PATCH] Language API - - -diff --git a/src/main/java/org/purpurmc/purpur/language/Language.java b/src/main/java/org/purpurmc/purpur/language/Language.java -new file mode 100644 -index 0000000000000000000000000000000000000000..38483d908ed830e97883733bee2370f87060f4c7 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/language/Language.java -@@ -0,0 +1,60 @@ -+package org.purpurmc.purpur.language; -+ -+import net.kyori.adventure.translation.Translatable; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Represents a language that can translate translation keys -+ */ -+public abstract class Language { -+ private static Language language; -+ -+ /** -+ * Returns the default language of the server -+ */ -+ @NotNull -+ public static Language getLanguage() { -+ return language; -+ } -+ -+ public static void setLanguage(@NotNull Language language) { -+ if (Language.language != null) { -+ throw new UnsupportedOperationException("Cannot redefine singleton Language"); -+ } -+ Language.language = language; -+ } -+ -+ /** -+ * Checks if a certain translation key is translatable with this language -+ * @param key The translation key -+ * @return Whether this language can translate the key -+ */ -+ abstract public boolean has(@NotNull String key); -+ -+ /** -+ * Checks if a certain translation key is translatable with this language -+ * @param key The translation key -+ * @return Whether this language can translate the key -+ */ -+ public boolean has(@NotNull Translatable key) { -+ return has(key.translationKey()); -+ } -+ -+ /** -+ * Translates a translation key to this language -+ * @param key The translation key -+ * @return The translated key, or the translation key if it couldn't be translated -+ */ -+ @NotNull -+ abstract public String getOrDefault(@NotNull String key); -+ -+ /** -+ * Translates a translation key to this language -+ * @param key The translation key -+ * @return The translated key, or the translation key if it couldn't be translated -+ */ -+ @NotNull -+ public String getOrDefault(@NotNull Translatable key) { -+ return getOrDefault(key.translationKey()); -+ } -+} diff --git a/patches/api/0049-Add-log-suppression-for-LibraryLoader.patch b/patches/api/0049-Add-log-suppression-for-LibraryLoader.patch deleted file mode 100644 index 95e4cd2ac..000000000 --- a/patches/api/0049-Add-log-suppression-for-LibraryLoader.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Krakenied -Date: Fri, 14 Oct 2022 23:11:27 +0200 -Subject: [PATCH] Add log suppression for LibraryLoader - - -diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -index 301e82369603f3dd6e6c1bd380da4bacacd7ef6c..0c6ca7588fb3d6b6497ddf032fe75e5c6c9719e5 100644 ---- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -@@ -55,6 +55,7 @@ public final class JavaPluginLoader implements PluginLoader { - private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")}; - private final List loaders = new CopyOnWriteArrayList(); - private final LibraryLoader libraryLoader; -+ public static boolean SuppressLibraryLoaderLogger = false; // Purpur - - /** - * This class was not meant to be constructed explicitly -diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -index e4b6f278a811acbb0070e311c5c3bdaff7b00474..ee83ecb054099cb85168a9499dfe967a0a9ec796 100644 ---- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -@@ -65,6 +65,7 @@ public class LibraryLoader - @Override - public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException - { -+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() ); - } - } ); -@@ -80,6 +81,7 @@ public class LibraryLoader - { - return null; - } -+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - logger.log( Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[] - { - java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), desc.getLibraries().size() // Paper - use configured log prefix -@@ -118,6 +120,7 @@ public class LibraryLoader - } - - jarFiles.add( url ); -+ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - logger.log( Level.INFO, "[{0}] Loaded library {1}", new Object[] - { - java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), file // Paper - use configured log prefix diff --git a/patches/api/0050-Fire-Immunity-API.patch b/patches/api/0050-Fire-Immunity-API.patch deleted file mode 100644 index e5622d4ca..000000000 --- a/patches/api/0050-Fire-Immunity-API.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Racci <90304606+DaRacci@users.noreply.github.com> -Date: Fri, 4 Feb 2022 16:09:47 +1100 -Subject: [PATCH] Fire Immunity API - - -diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index 52867495d0f746ff40e802c4f1018511e58fd03e..98de85d1382fe84cdc2e2c9db04bf1b4f157291c 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1086,5 +1086,18 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent - * @return True if in daylight - */ - boolean isInDaylight(); -+ -+ /** -+ * Checks if the entity is fire immune -+ * -+ * @return True if fire immune -+ */ -+ boolean isImmuneToFire(); -+ -+ /** -+ * Sets if the entity is fire immune -+ * Set this to null to restore the entity type default -+ */ -+ void setImmuneToFire(@Nullable Boolean fireImmune); - // Purpur end - } diff --git a/patches/api/0051-Added-goat-ram-event.patch b/patches/api/0051-Added-goat-ram-event.patch deleted file mode 100644 index 7665c860d..000000000 --- a/patches/api/0051-Added-goat-ram-event.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Sat, 29 Oct 2022 00:06:05 +0200 -Subject: [PATCH] Added goat ram event - - -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f62c14f3d4999e9112c1c73642aa337d97b94b5a ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java -@@ -0,0 +1,59 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.entity.Goat; -+import org.bukkit.entity.LivingEntity; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityEvent; -+import org.jetbrains.annotations.NotNull; -+ -+/** -+ * Called when a goat rams an entity -+ */ -+public class GoatRamEntityEvent extends EntityEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private final LivingEntity rammedEntity; -+ private boolean cancelled; -+ -+ public GoatRamEntityEvent(@NotNull Goat goat, @NotNull LivingEntity rammedEntity) { -+ super(goat); -+ this.rammedEntity = rammedEntity; -+ } -+ -+ /** -+ * Returns the entity that was rammed by the goat -+ * -+ * @return The rammed entity -+ */ -+ @NotNull -+ public LivingEntity getRammedEntity() { -+ return this.rammedEntity; -+ } -+ -+ @Override -+ @NotNull -+ public Goat getEntity() { -+ return (Goat) super.getEntity(); -+ } -+ -+ @Override -+ @NotNull -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return this.cancelled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancelled = cancel; -+ } -+} diff --git a/patches/api/0052-Add-PreExplodeEvents.patch b/patches/api/0052-Add-PreExplodeEvents.patch deleted file mode 100644 index 5ba7e435d..000000000 --- a/patches/api/0052-Add-PreExplodeEvents.patch +++ /dev/null @@ -1,136 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Mon, 26 Dec 2022 23:40:13 +0100 -Subject: [PATCH] Add PreExplodeEvents - - -diff --git a/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java b/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..32602b398ede24a35ed8a996faa2b23455615a7b ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java -@@ -0,0 +1,54 @@ -+package org.purpurmc.purpur.event; -+ -+import org.bukkit.block.Block; -+import org.bukkit.block.BlockState; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.block.BlockExplodeEvent; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+import java.util.Collections; -+ -+/** -+ * Called before a block's explosion is processed -+ */ -+public class PreBlockExplodeEvent extends BlockExplodeEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean cancelled; -+ private final float yield; -+ -+ public PreBlockExplodeEvent(@NotNull final Block what, final float yield, @Nullable BlockState explodedBlockState) { -+ super(what, Collections.emptyList(), yield, explodedBlockState); -+ this.yield = yield; -+ this.cancelled = false; -+ } -+ -+ /** -+ * Returns the percentage of blocks to drop from this explosion -+ * -+ * @return The yield. -+ */ -+ public float getYield() { -+ return yield; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return this.cancelled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancelled = cancel; -+ } -+ -+ @Override -+ public @NotNull HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java b/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2d4f68228861492baaea0bcc604dfef623b337ba ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java -@@ -0,0 +1,64 @@ -+package org.purpurmc.purpur.event.entity; -+ -+import org.bukkit.Location; -+import org.bukkit.event.Cancellable; -+import org.bukkit.event.HandlerList; -+import org.bukkit.event.entity.EntityExplodeEvent; -+import org.jetbrains.annotations.NotNull; -+import java.util.Collections; -+ -+/** -+ * Called before an entity's explosion is processed -+ */ -+public class PreEntityExplodeEvent extends EntityExplodeEvent implements Cancellable { -+ private static final HandlerList handlers = new HandlerList(); -+ private boolean cancelled; -+ private final float yield; -+ private final Location location; -+ -+ public PreEntityExplodeEvent(@NotNull org.bukkit.entity.Entity what, @NotNull final Location location, final float yield) { -+ super(what, location, Collections.emptyList(), yield); -+ this.cancelled = false; -+ this.yield = yield; -+ this.location = location; -+ } -+ -+ /** -+ * Returns the percentage of blocks to drop from this explosion -+ * -+ * @return The yield. -+ */ -+ public float getYield() { -+ return yield; -+ } -+ -+ /** -+ * Returns the location where the explosion happened. -+ * -+ * @return The location of the explosion -+ */ -+ @NotNull -+ public Location getLocation() { -+ return location; -+ } -+ -+ @Override -+ public boolean isCancelled() { -+ return this.cancelled; -+ } -+ -+ @Override -+ public void setCancelled(boolean cancel) { -+ this.cancelled = cancel; -+ } -+ -+ @Override -+ public @NotNull HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ @NotNull -+ public static HandlerList getHandlerList() { -+ return handlers; -+ } -+} diff --git a/patches/api/0053-Stored-Bee-API.patch b/patches/api/0053-Stored-Bee-API.patch deleted file mode 100644 index e155a6a0f..000000000 --- a/patches/api/0053-Stored-Bee-API.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: EOT3000 -Date: Sat, 10 Jun 2023 20:27:14 -0400 -Subject: [PATCH] Stored Bee API - - -diff --git a/src/main/java/org/bukkit/block/EntityBlockStorage.java b/src/main/java/org/bukkit/block/EntityBlockStorage.java -index 739911cda33b373f99df627a3a378b37d7d461aa..51e78c22cd021722b963fe31d1d9175d141add1a 100644 ---- a/src/main/java/org/bukkit/block/EntityBlockStorage.java -+++ b/src/main/java/org/bukkit/block/EntityBlockStorage.java -@@ -47,6 +47,24 @@ public interface EntityBlockStorage extends TileState { - @NotNull - List releaseEntities(); - -+ // Purpur start -+ /** -+ * Releases a stored entity, and returns the entity in the world. -+ * -+ * @param entity Entity to release -+ * @return The entity which was released, or null if the stored entity is not in the hive -+ */ -+ @org.jetbrains.annotations.Nullable -+ T releaseEntity(@NotNull org.purpurmc.purpur.entity.StoredEntity entity); -+ -+ /** -+ * Gets all the entities currently stored in the block. -+ * -+ * @return List of all entities which are stored in the block -+ */ -+ @NotNull -+ List> getEntities(); -+ //Purpur end - /** - * Add an entity to the block. - * -diff --git a/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java b/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java -new file mode 100644 -index 0000000000000000000000000000000000000000..29540d55532197d2381a52ea9222b5785d224ef8 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java -@@ -0,0 +1,52 @@ -+package org.purpurmc.purpur.entity; -+ -+import org.bukkit.Nameable; -+import org.bukkit.block.EntityBlockStorage; -+import org.bukkit.entity.Entity; -+import org.bukkit.entity.EntityType; -+import org.bukkit.persistence.PersistentDataHolder; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+/** -+ * Represents an entity stored in a block -+ * -+ * @see org.bukkit.block.EntityBlockStorage -+ */ -+public interface StoredEntity extends PersistentDataHolder, Nameable { -+ /** -+ * Checks if this entity has been released yet -+ * -+ * @return if this entity has been released -+ */ -+ boolean hasBeenReleased(); -+ -+ /** -+ * Releases the entity from its stored block -+ * -+ * @return the released entity, or null if unsuccessful (including if this entity has already been released) -+ */ -+ @Nullable -+ T release(); -+ -+ /** -+ * Returns the block in which this entity is stored -+ * -+ * @return the EntityBlockStorage in which this entity is stored, or null if it has been released -+ */ -+ @Nullable -+ EntityBlockStorage getBlockStorage(); -+ -+ /** -+ * Gets the entity type of this stored entity -+ * -+ * @return the type of entity this stored entity represents -+ */ -+ @NotNull -+ EntityType getType(); -+ -+ /** -+ * Writes data to the block entity snapshot. {@link EntityBlockStorage#update()} must be run in order to update the block in game. -+ */ -+ void update(); -+} diff --git a/patches/api/0054-Explorer-Map-API.patch b/patches/api/0054-Explorer-Map-API.patch deleted file mode 100644 index 91241d8f1..000000000 --- a/patches/api/0054-Explorer-Map-API.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 5 Jul 2023 12:48:08 -0500 -Subject: [PATCH] Explorer Map API - - -diff --git a/src/main/java/org/bukkit/map/MapRenderer.java b/src/main/java/org/bukkit/map/MapRenderer.java -index cb7040876a99a5a7e49b81684ef0f3b79584c376..22d8f31b1b8a5dbb5ab3275068642937c097abfe 100644 ---- a/src/main/java/org/bukkit/map/MapRenderer.java -+++ b/src/main/java/org/bukkit/map/MapRenderer.java -@@ -54,4 +54,12 @@ public abstract class MapRenderer { - */ - public abstract void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player); - -+ // Purpur - start -+ /** -+ * Check if this is an explorer (aka treasure) map. -+ * -+ * @return True if explorer map -+ */ -+ public abstract boolean isExplorerMap(); -+ // Purpur - end - } diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch deleted file mode 100644 index 176573ded..000000000 --- a/patches/server/0001-Pufferfish-Server-Changes.patch +++ /dev/null @@ -1,3628 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Raneri -Date: Wed, 3 Feb 2021 23:02:38 -0600 -Subject: [PATCH] Pufferfish Server Changes - -Pufferfish -Copyright (C) 2022 Pufferfish Studios LLC - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -diff --git a/build.gradle.kts b/build.gradle.kts -index fb98936bb8a5488db75d676c5bcb4060597fbbf8..f6cd7b910ce41a254e71bf0fcfe93c38abbb1445 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -13,8 +13,12 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { - val alsoShade: Configuration by configurations.creating - - dependencies { -- implementation(project(":paper-api")) -- implementation(project(":paper-mojangapi")) -+ implementation(project(":pufferfish-api")) // Pufferfish // Paper -+ // Pufferfish start -+ implementation("io.papermc.paper:paper-mojangapi:1.19.2-R0.1-SNAPSHOT") { -+ exclude("io.papermc.paper", "paper-api") -+ } -+ // Pufferfish end - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -52,6 +56,13 @@ dependencies { - runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") - runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") - -+ // Pufferfish start -+ implementation("org.yaml:snakeyaml:1.32") -+ implementation ("com.github.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { -+ exclude(group="org.yaml", module="snakeyaml") -+ } -+ // Pufferfish end -+ - testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test - testImplementation("junit:junit:4.13.2") - testImplementation("org.hamcrest:hamcrest-library:1.3") -@@ -60,6 +71,14 @@ dependencies { - } - - val craftbukkitPackageVersion = "1_20_R1" // Paper -+ -+// Pufferfish Start -+tasks.withType { -+ val compilerArgs = options.compilerArgs -+ compilerArgs.add("--add-modules=jdk.incubator.vector") -+} -+// Pufferfish End -+ - tasks.jar { - archiveClassifier.set("dev") - -@@ -72,7 +91,7 @@ tasks.jar { - attributes( - "Main-Class" to "org.bukkit.craftbukkit.Main", - "Implementation-Title" to "CraftBukkit", -- "Implementation-Version" to "git-Paper-$implementationVersion", -+ "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish - "Implementation-Vendor" to date, // Paper - "Specification-Title" to "Bukkit", - "Specification-Version" to project.version, -diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -index a2f71a6d1a9e98133dff6cd0f625da9435a8af14..ff940e43ca35094bbcae6c7d471d3c4aeb7c1727 100644 ---- a/src/main/java/co/aikar/timings/TimingsExport.java -+++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -242,7 +242,8 @@ public class TimingsExport extends Thread { - parent.put("config", createObject( - pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), - pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), -- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) -+ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Pufferfish -+ pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)) // Pufferfish - )); - - new TimingsExport(listeners, parent, history).start(); -diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 4b002e8b75d117b726b0de274a76d3596fce015b..692c962193cf9fcc6801fc93f3220bdc673d527b 100644 ---- a/src/main/java/com/destroystokyo/paper/Metrics.java -+++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -593,7 +593,7 @@ public class Metrics { - boolean logFailedRequests = config.getBoolean("logFailedRequests", false); - // Only start Metrics, if it's enabled in the config - if (config.getBoolean("enabled", true)) { -- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); -+ Metrics metrics = new Metrics("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish - - metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { - String minecraftVersion = Bukkit.getVersion(); -@@ -607,11 +607,11 @@ public class Metrics { - final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); - if (implVersion != null) { - final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); -- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); -+ paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish - } else { - paperVersion = "unknown"; - } -- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion)); -+ metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish - - metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { - Map> map = new HashMap<>(); -diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -index 41b9405d6759d865e0d14dd4f95163e9690e967d..091b1ae822e1c0517e59572e7a9bda11e998c0ee 100644 ---- a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -+++ b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java -@@ -26,7 +26,7 @@ public abstract class AreaMap { - - // we use linked for better iteration. - // map of: coordinate to set of objects in coordinate -- protected final Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); -+ protected Long2ObjectOpenHashMap> areaMap = new Long2ObjectOpenHashMap<>(1024, 0.7f); // Pufferfish - not actually final - protected final PooledLinkedHashSets pooledHashSets; - - protected final ChangeCallback addCallback; -@@ -160,7 +160,8 @@ public abstract class AreaMap { - protected abstract PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getEmptySetFor(final E object); - - // expensive op, only for debug -- protected void validate(final E object, final int viewDistance) { -+ protected void validate0(final E object, final int viewDistance) { // Pufferfish - rename this thing just in case it gets used I'd rather a compile time error. -+ if (true) throw new UnsupportedOperationException(); // Pufferfish - not going to put in the effort to fix this if it doesn't ever get used. - int entiesGot = 0; - int expectedEntries = (2 * viewDistance + 1); - expectedEntries *= expectedEntries; -diff --git a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -index 46954db7ecd35ac4018fdf476df7c8020d7ce6c8..1ad890a244bdf6df48a8db68cb43450e08c788a6 100644 ---- a/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -+++ b/src/main/java/com/destroystokyo/paper/util/misc/PlayerAreaMap.java -@@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer; - /** - * @author Spottedleaf - */ --public final class PlayerAreaMap extends AreaMap { -+public class PlayerAreaMap extends AreaMap { // Pufferfish - not actually final - - public PlayerAreaMap() { - super(); -diff --git a/src/main/java/gg/airplane/structs/FluidDirectionCache.java b/src/main/java/gg/airplane/structs/FluidDirectionCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..aa8467b9dda1f7707e41f50ac7b3e9d7343723ec ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/FluidDirectionCache.java -@@ -0,0 +1,136 @@ -+package gg.airplane.structs; -+ -+import it.unimi.dsi.fastutil.HashCommon; -+ -+/** -+ * This is a replacement for the cache used in FluidTypeFlowing. -+ * The requirements for the previous cache were: -+ * - Store 200 entries -+ * - Look for the flag in the cache -+ * - If it exists, move to front of cache -+ * - If it doesn't exist, remove last entry in cache and insert in front -+ * -+ * This class accomplishes something similar, however has a few different -+ * requirements put into place to make this more optimize: -+ * -+ * - maxDistance is the most amount of entries to be checked, instead -+ * of having to check the entire list. -+ * - In combination with that, entries are all tracked by age and how -+ * frequently they're used. This enables us to remove old entries, -+ * without constantly shifting any around. -+ * -+ * Usage of the previous map would have to reset the head every single usage, -+ * shifting the entire map. Here, nothing happens except an increment when -+ * the cache is hit, and when it needs to replace an old element only a single -+ * element is modified. -+ */ -+public class FluidDirectionCache { -+ -+ private static class FluidDirectionEntry { -+ private final T data; -+ private final boolean flag; -+ private int uses = 0; -+ private int age = 0; -+ -+ private FluidDirectionEntry(T data, boolean flag) { -+ this.data = data; -+ this.flag = flag; -+ } -+ -+ public int getValue() { -+ return this.uses - (this.age >> 1); // age isn't as important as uses -+ } -+ -+ public void incrementUses() { -+ this.uses = this.uses + 1 & Integer.MAX_VALUE; -+ } -+ -+ public void incrementAge() { -+ this.age = this.age + 1 & Integer.MAX_VALUE; -+ } -+ } -+ -+ private final FluidDirectionEntry[] entries; -+ private final int mask; -+ private final int maxDistance; // the most amount of entries to check for a value -+ -+ public FluidDirectionCache(int size) { -+ int arraySize = HashCommon.nextPowerOfTwo(size); -+ this.entries = new FluidDirectionEntry[arraySize]; -+ this.mask = arraySize - 1; -+ this.maxDistance = Math.min(arraySize, 4); -+ } -+ -+ public Boolean getValue(T data) { -+ FluidDirectionEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { -+ return null; -+ } else if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return curr.flag; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return curr.flag; -+ } else if (++checked >= this.maxDistance) { -+ break; -+ } -+ } -+ -+ return null; -+ } -+ -+ public void putValue(T data, boolean flag) { -+ FluidDirectionEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) { -+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add -+ return; -+ } else if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data.equals(curr.data)) { -+ curr.incrementUses(); -+ return; -+ } else if (++checked >= this.maxDistance) { -+ this.forceAdd(data, flag); -+ return; -+ } -+ } -+ -+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add -+ } -+ -+ private void forceAdd(T data, boolean flag) { -+ int expectedPos = HashCommon.mix(data.hashCode()) & this.mask; -+ -+ int toRemovePos = expectedPos; -+ FluidDirectionEntry entryToRemove = this.entries[toRemovePos]; -+ -+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { -+ int pos = i & this.mask; -+ FluidDirectionEntry entry = this.entries[pos]; -+ if (entry.getValue() < entryToRemove.getValue()) { -+ toRemovePos = pos; -+ entryToRemove = entry; -+ } -+ -+ entry.incrementAge(); // use this as a mechanism to age the other entries -+ } -+ -+ // remove the least used/oldest entry -+ this.entries[toRemovePos] = new FluidDirectionEntry(data, flag); -+ } -+} -diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1b7a4ee47f4445d7f2ac91d3a73ae113edbdddb2 ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -@@ -0,0 +1,114 @@ -+package gg.airplane.structs; -+ -+import net.minecraft.core.NonNullList; -+import net.minecraft.world.item.ItemStack; -+import org.apache.commons.lang.Validate; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.AbstractList; -+import java.util.Arrays; -+import java.util.List; -+ -+public class ItemListWithBitset extends AbstractList { -+ public static ItemListWithBitset fromList(List list) { -+ if (list instanceof ItemListWithBitset ours) { -+ return ours; -+ } -+ return new ItemListWithBitset(list); -+ } -+ -+ private static ItemStack[] createArray(int size) { -+ ItemStack[] array = new ItemStack[size]; -+ Arrays.fill(array, ItemStack.EMPTY); -+ return array; -+ } -+ -+ private final ItemStack[] items; -+ -+ private long bitSet = 0; -+ private final long allBits; -+ -+ private static class OurNonNullList extends NonNullList { -+ protected OurNonNullList(List delegate) { -+ super(delegate, ItemStack.EMPTY); -+ } -+ } -+ -+ public final NonNullList nonNullList = new OurNonNullList(this); -+ -+ private ItemListWithBitset(List list) { -+ this(list.size()); -+ -+ for (int i = 0; i < list.size(); i++) { -+ this.set(i, list.get(i)); -+ } -+ } -+ -+ public ItemListWithBitset(int size) { -+ Validate.isTrue(size < Long.BYTES * 8, "size is too large"); -+ -+ this.items = createArray(size); -+ this.allBits = ((1L << size) - 1); -+ } -+ -+ public boolean isCompletelyEmpty() { -+ return this.bitSet == 0; -+ } -+ -+ public boolean hasFullStacks() { -+ return (this.bitSet & this.allBits) == allBits; -+ } -+ -+ @Override -+ public ItemStack set(int index, @NotNull ItemStack itemStack) { -+ ItemStack existing = this.items[index]; -+ -+ this.items[index] = itemStack; -+ -+ if (itemStack == ItemStack.EMPTY) { -+ this.bitSet &= ~(1L << index); -+ } else { -+ this.bitSet |= 1L << index; -+ } -+ -+ return existing; -+ } -+ -+ @NotNull -+ @Override -+ public ItemStack get(int var0) { -+ return this.items[var0]; -+ } -+ -+ @Override -+ public int size() { -+ return this.items.length; -+ } -+ -+ @Override -+ public void clear() { -+ Arrays.fill(this.items, ItemStack.EMPTY); -+ } -+ -+ // these are unsupported for block inventories which have a static size -+ @Override -+ public void add(int var0, ItemStack var1) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public ItemStack remove(int var0) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public String toString() { -+ return "ItemListWithBitset{" + -+ "items=" + Arrays.toString(items) + -+ ", bitSet=" + Long.toString(bitSet, 2) + -+ ", allBits=" + Long.toString(allBits, 2) + -+ ", size=" + this.items.length + -+ '}'; -+ } -+} -diff --git a/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be70013a714 ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/Long2FloatAgingCache.java -@@ -0,0 +1,119 @@ -+package gg.airplane.structs; -+ -+import it.unimi.dsi.fastutil.HashCommon; -+ -+/** -+ * A replacement for the cache used in Biome. -+ */ -+public class Long2FloatAgingCache { -+ -+ private static class AgingEntry { -+ private long data; -+ private float value; -+ private int uses = 0; -+ private int age = 0; -+ -+ private AgingEntry(long data, float value) { -+ this.data = data; -+ this.value = value; -+ } -+ -+ public void replace(long data, float flag) { -+ this.data = data; -+ this.value = flag; -+ } -+ -+ public int getValue() { -+ return this.uses - (this.age >> 1); // age isn't as important as uses -+ } -+ -+ public void incrementUses() { -+ this.uses = this.uses + 1 & Integer.MAX_VALUE; -+ } -+ -+ public void incrementAge() { -+ this.age = this.age + 1 & Integer.MAX_VALUE; -+ } -+ } -+ -+ private final AgingEntry[] entries; -+ private final int mask; -+ private final int maxDistance; // the most amount of entries to check for a value -+ -+ public Long2FloatAgingCache(int size) { -+ int arraySize = HashCommon.nextPowerOfTwo(size); -+ this.entries = new AgingEntry[arraySize]; -+ this.mask = arraySize - 1; -+ this.maxDistance = Math.min(arraySize, 4); -+ } -+ -+ public float getValue(long data) { -+ AgingEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { -+ return Float.NaN; -+ } else if (data == curr.data) { -+ curr.incrementUses(); -+ return curr.value; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data == curr.data) { -+ curr.incrementUses(); -+ return curr.value; -+ } else if (++checked >= this.maxDistance) { -+ break; -+ } -+ } -+ -+ return Float.NaN; -+ } -+ -+ public void putValue(long data, float value) { -+ AgingEntry curr; -+ int pos; -+ -+ if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { -+ this.entries[pos] = new AgingEntry(data, value); // add -+ return; -+ } else if (data == curr.data) { -+ curr.incrementUses(); -+ return; -+ } -+ -+ int checked = 1; // start at 1 because we already checked the first spot above -+ -+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { -+ if (data == curr.data) { -+ curr.incrementUses(); -+ return; -+ } else if (++checked >= this.maxDistance) { -+ this.forceAdd(data, value); -+ return; -+ } -+ } -+ -+ this.entries[pos] = new AgingEntry(data, value); // add -+ } -+ -+ private void forceAdd(long data, float value) { -+ int expectedPos = HashCommon.mix(HashCommon.long2int(data)) & this.mask; -+ AgingEntry entryToRemove = this.entries[expectedPos]; -+ -+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { -+ int pos = i & this.mask; -+ AgingEntry entry = this.entries[pos]; -+ if (entry.getValue() < entryToRemove.getValue()) { -+ entryToRemove = entry; -+ } -+ -+ entry.incrementAge(); // use this as a mechanism to age the other entries -+ } -+ -+ // remove the least used/oldest entry -+ entryToRemove.replace(data, value); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..020368da69b9a492155f6de6297f74732f4ab6ea ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java -@@ -0,0 +1,68 @@ -+package gg.pufferfish.pufferfish; -+ -+import java.io.IOException; -+import java.util.Collections; -+import java.util.List; -+import java.util.stream.Collectors; -+import java.util.stream.Stream; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.md_5.bungee.api.ChatColor; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.Bukkit; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+ -+public class PufferfishCommand extends Command { -+ -+ public PufferfishCommand() { -+ super("pufferfish"); -+ this.description = "Pufferfish related commands"; -+ this.usageMessage = "/pufferfish [reload | version]"; -+ this.setPermission("bukkit.command.pufferfish"); -+ } -+ -+ public static void init() { -+ MinecraftServer.getServer().server.getCommandMap().register("pufferfish", "Pufferfish", new PufferfishCommand()); -+ } -+ -+ @Override -+ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { -+ if (args.length == 1) { -+ return Stream.of("reload", "version") -+ .filter(arg -> arg.startsWith(args[0].toLowerCase())) -+ .collect(Collectors.toList()); -+ } -+ return Collections.emptyList(); -+ } -+ -+ @Override -+ public boolean execute(CommandSender sender, String commandLabel, String[] args) { -+ if (!testPermission(sender)) return true; -+ String prefix = ChatColor.of("#12fff6") + "" + ChatColor.BOLD + "Pufferfish » " + ChatColor.of("#e8f9f9"); -+ -+ if (args.length != 1) { -+ sender.sendMessage(prefix + "Usage: " + usageMessage); -+ args = new String[]{"version"}; -+ } -+ -+ if (args[0].equalsIgnoreCase("reload")) { -+ MinecraftServer console = MinecraftServer.getServer(); -+ try { -+ PufferfishConfig.load(); -+ } catch (IOException e) { -+ sender.sendMessage(Component.text("Failed to reload.", NamedTextColor.RED)); -+ e.printStackTrace(); -+ return true; -+ } -+ console.server.reloadCount++; -+ -+ Command.broadcastCommandMessage(sender, prefix + "Pufferfish configuration has been reloaded."); -+ } else if (args[0].equalsIgnoreCase("version")) { -+ Command.broadcastCommandMessage(sender, prefix + "This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); -+ } -+ -+ return true; -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3d4bb28fe686a9ad2e4c0f75f21e6289c2ea5cf9 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -0,0 +1,296 @@ -+package gg.pufferfish.pufferfish; -+ -+import gg.pufferfish.pufferfish.simd.SIMDDetection; -+import java.io.File; -+import java.io.IOException; -+import java.util.Collections; -+import net.minecraft.core.registries.BuiltInRegistries; -+import java.util.Locale; -+import java.util.Map; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.tags.TagKey; -+import org.apache.logging.log4j.Level; -+import org.bukkit.configuration.ConfigurationSection; -+import net.minecraft.world.entity.EntityType; -+import java.lang.reflect.Method; -+import java.lang.reflect.Modifier; -+import java.util.List; -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Level; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.MemoryConfiguration; -+import org.jetbrains.annotations.Nullable; -+import org.simpleyaml.configuration.comments.CommentType; -+import org.simpleyaml.configuration.file.YamlFile; -+import org.simpleyaml.exceptions.InvalidConfigurationException; -+ -+public class PufferfishConfig { -+ -+ private static final YamlFile config = new YamlFile(); -+ private static int updates = 0; -+ -+ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { -+ ConfigurationSection newSection = new MemoryConfiguration(); -+ for (String key : section.getKeys(false)) { -+ if (section.isConfigurationSection(key)) { -+ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); -+ } else { -+ newSection.set(key, section.get(key)); -+ } -+ } -+ return newSection; -+ } -+ -+ public static ConfigurationSection getConfigCopy() { -+ return convertToBukkit(config); -+ } -+ -+ public static int getUpdates() { -+ return updates; -+ } -+ -+ public static void load() throws IOException { -+ File configFile = new File("pufferfish.yml"); -+ -+ if (configFile.exists()) { -+ try { -+ config.load(configFile); -+ } catch (InvalidConfigurationException e) { -+ throw new IOException(e); -+ } -+ } -+ -+ getString("info.version", "1.0"); -+ setComment("info", -+ "Pufferfish Configuration", -+ "Check out Pufferfish Host for maximum performance server hosting: https://pufferfish.host", -+ "Join our Discord for support: https://discord.gg/reZw4vQV9H", -+ "Download new builds at https://ci.pufferfish.host/job/Pufferfish"); -+ -+ for (Method method : PufferfishConfig.class.getDeclaredMethods()) { -+ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && -+ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { -+ method.setAccessible(true); -+ try { -+ method.invoke(null); -+ } catch (Throwable t) { -+ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); -+ } -+ } -+ } -+ -+ updates++; -+ -+ config.save(configFile); -+ -+ // Attempt to detect vectorization -+ try { -+ SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER); -+ SIMDDetection.versionLimited = SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19; -+ } catch (NoClassDefFoundError | Exception ignored) { -+ ignored.printStackTrace(); -+ } -+ -+ if (SIMDDetection.isEnabled) { -+ PufferfishLogger.LOGGER.info("SIMD operations detected as functional. Will replace some operations with faster versions."); -+ } else if (SIMDDetection.versionLimited) { -+ PufferfishLogger.LOGGER.warning("Will not enable SIMD! These optimizations are only safely supported on Java 17, Java 18, and Java 19."); -+ } else { -+ PufferfishLogger.LOGGER.warning("SIMD operations are available for your server, but are not configured!"); -+ PufferfishLogger.LOGGER.warning("To enable additional optimizations, add \"--add-modules=jdk.incubator.vector\" to your startup flags, BEFORE the \"-jar\"."); -+ PufferfishLogger.LOGGER.warning("If you have already added this flag, then SIMD operations are not supported on your JVM or CPU."); -+ PufferfishLogger.LOGGER.warning("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun); -+ } -+ } -+ -+ private static void setComment(String key, String... comment) { -+ if (config.contains(key)) { -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); -+ } -+ } -+ -+ private static void ensureDefault(String key, Object defaultValue, String... comment) { -+ if (!config.contains(key)) { -+ config.set(key, defaultValue); -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); -+ } -+ } -+ -+ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { -+ return getBoolean(key, null, defaultValue, comment); -+ } -+ -+ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getBoolean(key, defaultValue); -+ } -+ -+ private static int getInt(String key, int defaultValue, String... comment) { -+ return getInt(key, null, defaultValue, comment); -+ } -+ -+ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getInt(key, defaultValue); -+ } -+ -+ private static double getDouble(String key, double defaultValue, String... comment) { -+ return getDouble(key, null, defaultValue, comment); -+ } -+ -+ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getDouble(key, defaultValue); -+ } -+ -+ private static String getString(String key, String defaultValue, String... comment) { -+ return getOldString(key, null, defaultValue, comment); -+ } -+ -+ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getString(key, defaultValue); -+ } -+ -+ private static List getStringList(String key, List defaultValue, String... comment) { -+ return getStringList(key, null, defaultValue, comment); -+ } -+ -+ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getStringList(key); -+ } -+ -+ public static String sentryDsn; -+ private static void sentry() { -+ String sentryEnvironment = System.getenv("SENTRY_DSN"); -+ String sentryConfig = getString("sentry-dsn", "", "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/"); -+ -+ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; -+ if (sentryDsn != null && !sentryDsn.isBlank()) { -+ gg.pufferfish.pufferfish.sentry.SentryManager.init(); -+ } -+ } -+ -+ public static boolean enableBooks; -+ private static void books() { -+ enableBooks = getBoolean("enable-books", true, -+ "Whether or not books should be writeable.", -+ "Servers that anticipate being a target for duping may want to consider", -+ "disabling this option.", -+ "This can be overridden per-player with the permission pufferfish.usebooks"); -+ } -+ -+ public static boolean enableSuffocationOptimization; -+ private static void suffocationOptimization() { -+ enableSuffocationOptimization = getBoolean("enable-suffocation-optimization", true, -+ "Optimizes the suffocation check by selectively skipping", -+ "the check in a way that still appears vanilla. This should", -+ "be left enabled on most servers, but is provided as a", -+ "configuration option if the vanilla deviation is undesirable."); -+ } -+ -+ public static boolean enableAsyncMobSpawning; -+ public static boolean asyncMobSpawningInitialized; -+ private static void asyncMobSpawning() { -+ boolean temp = getBoolean("enable-async-mob-spawning", true, -+ "Whether or not asynchronous mob spawning should be enabled.", -+ "On servers with many entities, this can improve performance by up to 15%. You must have", -+ "paper's per-player-mob-spawns setting set to true for this to work.", -+ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", -+ "This just offloads some expensive calculations that are required for mob spawning."); -+ -+ // This prevents us from changing the value during a reload. -+ if (!asyncMobSpawningInitialized) { -+ asyncMobSpawningInitialized = true; -+ enableAsyncMobSpawning = temp; -+ } -+ } -+ -+ public static int maxProjectileLoadsPerTick; -+ public static int maxProjectileLoadsPerProjectile; -+ private static void projectileLoading() { -+ maxProjectileLoadsPerTick = getInt("projectile.max-loads-per-tick", 10, "Controls how many chunks are allowed", "to be sync loaded by projectiles in a tick."); -+ maxProjectileLoadsPerProjectile = getInt("projectile.max-loads-per-projectile", 10, "Controls how many chunks a projectile", "can load in its lifetime before it gets", "automatically removed."); -+ -+ setComment("projectile", "Optimizes projectile settings"); -+ } -+ -+ -+ public static boolean dearEnabled; -+ public static int startDistance; -+ public static int startDistanceSquared; -+ public static int maximumActivationPrio; -+ public static int activationDistanceMod; -+ -+ private static void dynamicActivationOfBrains() throws IOException { -+ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); -+ startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, -+ "This value determines how far away an entity has to be", -+ "from the player to start being effected by DEAR."); -+ startDistanceSquared = startDistance * startDistance; -+ maximumActivationPrio = getInt("dab.max-tick-freq", "activation-range.max-tick-freq", 20, -+ "This value defines how often in ticks, the furthest entity", -+ "will get their pathfinders and behaviors ticked. 20 = 1s"); -+ activationDistanceMod = getInt("dab.activation-dist-mod", "activation-range.activation-dist-mod", 8, -+ "This value defines how much distance modifies an entity's", -+ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", -+ "If you want further away entities to tick less often, use 7.", -+ "If you want further away entities to tick more often, try 9."); -+ -+ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { -+ entityType.dabEnabled = true; // reset all, before setting the ones to true -+ } -+ getStringList("dab.blacklisted-entities", "activation-range.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") -+ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { -+ entityType.dabEnabled = false; -+ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); -+ -+ setComment("dab", "Optimizes entity brains when", "they're far away from the player"); -+ } -+ -+ public static Map projectileTimeouts; -+ private static void projectileTimeouts() { -+ // Set some defaults -+ getInt("entity_timeouts.SNOWBALL", -1); -+ getInt("entity_timeouts.LLAMA_SPIT", -1); -+ setComment("entity_timeouts", -+ "These values define a entity's maximum lifespan. If an", -+ "entity is in this list and it has survived for longer than", -+ "that number of ticks, then it will be removed. Setting a value to", -+ "-1 disables this feature."); -+ -+ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { -+ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); -+ entityType.ttl = config.getInt("entity_timeouts." + type, -1); -+ } -+ } -+ -+ public static boolean throttleInactiveGoalSelectorTick; -+ private static void inactiveGoalSelectorThrottle() { -+ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, -+ "Throttles the AI goal selector in entity inactive ticks.", -+ "This can improve performance by a few percent, but has minor gameplay implications."); -+ } -+ -+ public static boolean allowEndCrystalRespawn; -+ private static void allowEndCrystalRespawn() { -+ allowEndCrystalRespawn = getBoolean("allow-end-crystal-respawn", true, -+ "Allows end crystals to respawn the ender dragon.", -+ "On servers that expect end crystal fights in the end dimension, disabling this", -+ "will prevent the server from performing an expensive search to attempt respawning", -+ "the ender dragon whenever a player places an end crystal."); -+ } -+ -+ -+ public static boolean disableMethodProfiler; -+ public static boolean disableOutOfOrderChat; -+ public static boolean suppressNullIdDisconnections; -+ private static void miscSettings() { -+ disableMethodProfiler = getBoolean("misc.disable-method-profiler", true); -+ disableOutOfOrderChat = getBoolean("misc.disable-out-of-order-chat", false); -+ suppressNullIdDisconnections = getBoolean("misc.suppress-null-id-disconnections", false); -+ setComment("misc", "Settings for things that don't belong elsewhere"); -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java -new file mode 100644 -index 0000000000000000000000000000000000000000..53f2df00c6809618a9ee3d2ea72e85e8052fbcf1 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java -@@ -0,0 +1,16 @@ -+package gg.pufferfish.pufferfish; -+ -+import java.util.logging.Level; -+import java.util.logging.Logger; -+import org.bukkit.Bukkit; -+ -+public class PufferfishLogger extends Logger { -+ public static final PufferfishLogger LOGGER = new PufferfishLogger(); -+ -+ private PufferfishLogger() { -+ super("Pufferfish", null); -+ -+ setParent(Bukkit.getLogger()); -+ setLevel(Level.ALL); -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -new file mode 100644 -index 0000000000000000000000000000000000000000..893d8c0946ef71a0561221dd76bffff0dc940d56 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java -@@ -0,0 +1,136 @@ -+package gg.pufferfish.pufferfish; -+ -+import static net.kyori.adventure.text.Component.text; -+import static net.kyori.adventure.text.format.NamedTextColor.GREEN; -+import static net.kyori.adventure.text.format.NamedTextColor.RED; -+ -+import com.destroystokyo.paper.VersionHistoryManager; -+import com.destroystokyo.paper.util.VersionFetcher; -+import com.google.gson.Gson; -+import com.google.gson.JsonObject; -+import java.io.IOException; -+import java.net.URI; -+import java.net.http.HttpClient; -+import java.net.http.HttpRequest; -+import java.net.http.HttpResponse; -+import java.nio.charset.StandardCharsets; -+import java.util.concurrent.TimeUnit; -+import java.util.logging.Level; -+import java.util.logging.Logger; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.JoinConfiguration; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.format.TextDecoration; -+import org.bukkit.craftbukkit.CraftServer; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+public class PufferfishVersionFetcher implements VersionFetcher { -+ -+ private static final Logger LOGGER = Logger.getLogger("PufferfishVersionFetcher"); -+ private static final HttpClient client = HttpClient.newHttpClient(); -+ -+ private static final URI JENKINS_URI = URI.create("https://ci.pufferfish.host/job/Pufferfish-1.20/lastSuccessfulBuild/buildNumber"); -+ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.20...%s"; -+ -+ private static final HttpResponse.BodyHandler JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers -+ .mapping( -+ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), -+ string -> new Gson().fromJson(string, JsonObject.class) -+ ); -+ -+ @Override -+ public long getCacheTime() { -+ return TimeUnit.MINUTES.toMillis(30); -+ } -+ -+ @Override -+ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) { -+ final String[] parts = CraftServer.class.getPackage().getImplementationVersion().split("-"); -+ @NotNull Component component; -+ -+ if (parts.length != 3) { -+ component = text("Unknown server version.", RED); -+ } else { -+ final String versionString = parts[2]; -+ -+ try { -+ component = this.fetchJenkinsVersion(Integer.parseInt(versionString)); -+ } catch (NumberFormatException e) { -+ component = this.fetchGithubVersion(versionString.substring(1, versionString.length() - 1)); -+ } -+ } -+ -+ final @Nullable Component history = this.getHistory(); -+ return history != null ? Component -+ .join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component; -+ } -+ -+ private @NotNull Component fetchJenkinsVersion(final int versionNumber) { -+ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build(); -+ try { -+ final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); -+ if (response.statusCode() != 200) { -+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); -+ } -+ -+ int latestVersionNumber; -+ try { -+ latestVersionNumber = Integer.parseInt(response.body()); -+ } catch (NumberFormatException e) { -+ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\"."); -+ return text("Received invalid response from server.", RED); -+ } -+ -+ final int versionDiff = latestVersionNumber - versionNumber; -+ return this.getResponseMessage(versionDiff); -+ } catch (IOException | InterruptedException e) { -+ LOGGER.log(Level.WARNING, "Failed to look up version from Jenkins", e); -+ return text("Failed to retrieve version from server.", RED); -+ } -+ } -+ -+ // Based off code contributed by Techcable in Paper/GH-65 -+ private @NotNull Component fetchGithubVersion(final @NotNull String hash) { -+ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash)); -+ final HttpRequest request = HttpRequest.newBuilder(uri).build(); -+ try { -+ final HttpResponse response = client.send(request, JSON_OBJECT_BODY_HANDLER); -+ if (response.statusCode() != 200) { -+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); -+ } -+ -+ final JsonObject obj = response.body(); -+ final int versionDiff = obj.get("behind_by").getAsInt(); -+ -+ return this.getResponseMessage(versionDiff); -+ } catch (IOException | InterruptedException e) { -+ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e); -+ return text("Failed to retrieve version from server.", RED); -+ } -+ } -+ -+ private @NotNull Component getResponseMessage(final int versionDiff) { -+ return switch (Math.max(-1, Math.min(1, versionDiff))) { -+ case -1 -> text("You are running an unsupported version of Pufferfish.", RED); -+ case 0 -> text("You are on the latest version!", GREEN); -+ default -> text("You are running " + versionDiff + " version" + (versionDiff == 1 ? "" : "s") + " beyond. " + -+ "Please update your server when possible to maintain stability, security, and receive the latest optimizations.", -+ RED); -+ }; -+ } -+ -+ private @Nullable Component getHistory() { -+ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); -+ if (data == null) { -+ return null; -+ } -+ -+ final String oldVersion = data.getOldVersion(); -+ if (oldVersion == null) { -+ return null; -+ } -+ -+ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); -+ } -+} -\ No newline at end of file -diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java -new file mode 100644 -index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834733d0621 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java -@@ -0,0 +1,135 @@ -+package gg.pufferfish.pufferfish.sentry; -+ -+import com.google.common.reflect.TypeToken; -+import com.google.gson.Gson; -+import io.sentry.Breadcrumb; -+import io.sentry.Sentry; -+import io.sentry.SentryEvent; -+import io.sentry.SentryLevel; -+import io.sentry.protocol.Message; -+import io.sentry.protocol.User; -+import java.util.Map; -+import org.apache.logging.log4j.Level; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Marker; -+import org.apache.logging.log4j.core.LogEvent; -+import org.apache.logging.log4j.core.Logger; -+import org.apache.logging.log4j.core.appender.AbstractAppender; -+import org.apache.logging.log4j.core.filter.AbstractFilter; -+ -+public class PufferfishSentryAppender extends AbstractAppender { -+ -+ private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(PufferfishSentryAppender.class); -+ private static final Gson GSON = new Gson(); -+ -+ public PufferfishSentryAppender() { -+ super("PufferfishSentryAdapter", new SentryFilter(), null); -+ } -+ -+ @Override -+ public void append(LogEvent logEvent) { -+ if (logEvent.getThrown() != null && logEvent.getLevel().isMoreSpecificThan(Level.WARN)) { -+ try { -+ logException(logEvent); -+ } catch (Exception e) { -+ logger.warn("Failed to log event with sentry", e); -+ } -+ } else { -+ try { -+ logBreadcrumb(logEvent); -+ } catch (Exception e) { -+ logger.warn("Failed to log event with sentry", e); -+ } -+ } -+ } -+ -+ private void logException(LogEvent e) { -+ SentryEvent event = new SentryEvent(e.getThrown()); -+ -+ Message sentryMessage = new Message(); -+ sentryMessage.setMessage(e.getMessage().getFormattedMessage()); -+ -+ event.setThrowable(e.getThrown()); -+ event.setLevel(getLevel(e.getLevel())); -+ event.setLogger(e.getLoggerName()); -+ event.setTransaction(e.getLoggerName()); -+ event.setExtra("thread_name", e.getThreadName()); -+ -+ boolean hasContext = e.getContextData() != null; -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_playerid")) { -+ User user = new User(); -+ user.setId(e.getContextData().getValue("pufferfishsentry_playerid")); -+ user.setUsername(e.getContextData().getValue("pufferfishsentry_playername")); -+ event.setUser(user); -+ } -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_pluginname")) { -+ event.setExtra("plugin.name", e.getContextData().getValue("pufferfishsentry_pluginname")); -+ event.setExtra("plugin.version", e.getContextData().getValue("pufferfishsentry_pluginversion")); -+ event.setTransaction(e.getContextData().getValue("pufferfishsentry_pluginname")); -+ } -+ -+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_eventdata")) { -+ Map eventFields = GSON.fromJson((String) e.getContextData().getValue("pufferfishsentry_eventdata"), new TypeToken>() {}.getType()); -+ if (eventFields != null) { -+ event.setExtra("event", eventFields); -+ } -+ } -+ -+ Sentry.captureEvent(event); -+ } -+ -+ private void logBreadcrumb(LogEvent e) { -+ Breadcrumb breadcrumb = new Breadcrumb(); -+ -+ breadcrumb.setLevel(getLevel(e.getLevel())); -+ breadcrumb.setCategory(e.getLoggerName()); -+ breadcrumb.setType(e.getLoggerName()); -+ breadcrumb.setMessage(e.getMessage().getFormattedMessage()); -+ -+ Sentry.addBreadcrumb(breadcrumb); -+ } -+ -+ private SentryLevel getLevel(Level level) { -+ switch (level.getStandardLevel()) { -+ case TRACE: -+ case DEBUG: -+ return SentryLevel.DEBUG; -+ case WARN: -+ return SentryLevel.WARNING; -+ case ERROR: -+ return SentryLevel.ERROR; -+ case FATAL: -+ return SentryLevel.FATAL; -+ case INFO: -+ default: -+ return SentryLevel.INFO; -+ } -+ } -+ -+ private static class SentryFilter extends AbstractFilter { -+ -+ @Override -+ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, String msg, -+ Object... params) { -+ return this.filter(logger.getName()); -+ } -+ -+ @Override -+ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, Object msg, Throwable t) { -+ return this.filter(logger.getName()); -+ } -+ -+ @Override -+ public Result filter(LogEvent event) { -+ return this.filter(event == null ? null : event.getLoggerName()); -+ } -+ -+ private Result filter(String loggerName) { -+ return loggerName != null && loggerName.startsWith("gg.castaway.pufferfish.sentry") ? Result.DENY -+ : Result.NEUTRAL; -+ } -+ -+ } -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1b29210ad0bbb4ada150f23357f0c80d331c996d ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java -@@ -0,0 +1,40 @@ -+package gg.pufferfish.pufferfish.sentry; -+ -+import gg.pufferfish.pufferfish.PufferfishConfig; -+import io.sentry.Sentry; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+ -+public class SentryManager { -+ -+ private static final Logger logger = LogManager.getLogger(SentryManager.class); -+ -+ private SentryManager() { -+ -+ } -+ -+ private static boolean initialized = false; -+ -+ public static synchronized void init() { -+ if (initialized) { -+ return; -+ } -+ try { -+ initialized = true; -+ -+ Sentry.init(options -> { -+ options.setDsn(PufferfishConfig.sentryDsn); -+ options.setMaxBreadcrumbs(100); -+ }); -+ -+ PufferfishSentryAppender appender = new PufferfishSentryAppender(); -+ appender.start(); -+ ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addAppender(appender); -+ logger.info("Sentry logging started!"); -+ } catch (Exception e) { -+ logger.warn("Failed to initialize sentry!", e); -+ initialized = false; -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cfc54384cf ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java -@@ -0,0 +1,73 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import com.google.common.collect.Queues; -+import gg.pufferfish.pufferfish.PufferfishLogger; -+import java.util.Queue; -+import java.util.concurrent.locks.Condition; -+import java.util.concurrent.locks.Lock; -+import java.util.concurrent.locks.ReentrantLock; -+import java.util.logging.Level; -+ -+public class AsyncExecutor implements Runnable { -+ -+ private final Queue jobs = Queues.newArrayDeque(); -+ private final Lock mutex = new ReentrantLock(); -+ private final Condition cond = mutex.newCondition(); -+ private final Thread thread; -+ private volatile boolean killswitch = false; -+ -+ public AsyncExecutor(String threadName) { -+ this.thread = new Thread(this, threadName); -+ } -+ -+ public void start() { -+ thread.start(); -+ } -+ -+ public void kill() { -+ killswitch = true; -+ cond.signalAll(); -+ } -+ -+ public void submit(Runnable runnable) { -+ mutex.lock(); -+ try { -+ jobs.offer(runnable); -+ cond.signalAll(); -+ } finally { -+ mutex.unlock(); -+ } -+ } -+ -+ @Override -+ public void run() { -+ while (!killswitch) { -+ try { -+ Runnable runnable = takeRunnable(); -+ if (runnable != null) { -+ runnable.run(); -+ } -+ } catch (InterruptedException e) { -+ Thread.currentThread().interrupt(); -+ } catch (Exception e) { -+ PufferfishLogger.LOGGER.log(Level.SEVERE, e, () -> "Failed to execute async job for thread " + thread.getName()); -+ } -+ } -+ } -+ -+ private Runnable takeRunnable() throws InterruptedException { -+ mutex.lock(); -+ try { -+ while (jobs.isEmpty() && !killswitch) { -+ cond.await(); -+ } -+ -+ if (jobs.isEmpty()) return null; // We've set killswitch -+ -+ return jobs.remove(); -+ } finally { -+ mutex.unlock(); -+ } -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fdcb62d12164024a5f354d60cc863821a18d1b2a ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncPlayerAreaMap.java -@@ -0,0 +1,31 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import com.destroystokyo.paper.util.misc.PlayerAreaMap; -+import com.destroystokyo.paper.util.misc.PooledLinkedHashSets; -+import java.util.concurrent.ConcurrentHashMap; -+import net.minecraft.server.level.ServerPlayer; -+ -+public final class AsyncPlayerAreaMap extends PlayerAreaMap { -+ -+ public AsyncPlayerAreaMap() { -+ super(); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets) { -+ super(pooledHashSets); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, -+ final ChangeCallback removeCallback) { -+ this(pooledHashSets, addCallback, removeCallback, null); -+ } -+ -+ public AsyncPlayerAreaMap(final PooledLinkedHashSets pooledHashSets, final ChangeCallback addCallback, -+ final ChangeCallback removeCallback, final ChangeSourceCallback changeSourceCallback) { -+ super(pooledHashSets, addCallback, removeCallback, changeSourceCallback); -+ this.areaMap = new Long2ObjectOpenHashMapWrapper<>(new ConcurrentHashMap<>(1024, 0.7f)); -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c1929840254a3e6d721816f4a20415bea1742580 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java -@@ -0,0 +1,20 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import java.util.Iterator; -+import org.jetbrains.annotations.NotNull; -+ -+public class IterableWrapper implements Iterable { -+ -+ private final Iterator iterator; -+ -+ public IterableWrapper(Iterator iterator) { -+ this.iterator = iterator; -+ } -+ -+ @NotNull -+ @Override -+ public Iterator iterator() { -+ return iterator; -+ } -+ -+} -diff --git a/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..facd55463d44cb7e3d2ca6892982f5497b8dded1 ---- /dev/null -+++ b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java -@@ -0,0 +1,40 @@ -+package gg.pufferfish.pufferfish.util; -+ -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import java.util.Map; -+import org.jetbrains.annotations.Nullable; -+ -+public class Long2ObjectOpenHashMapWrapper extends Long2ObjectOpenHashMap { -+ -+ private final Map backingMap; -+ -+ public Long2ObjectOpenHashMapWrapper(Map map) { -+ backingMap = map; -+ } -+ -+ @Override -+ public V put(Long key, V value) { -+ return backingMap.put(key, value); -+ } -+ -+ @Override -+ public V get(Object key) { -+ return backingMap.get(key); -+ } -+ -+ @Override -+ public V remove(Object key) { -+ return backingMap.remove(key); -+ } -+ -+ @Nullable -+ @Override -+ public V putIfAbsent(Long key, V value) { -+ return backingMap.putIfAbsent(key, value); -+ } -+ -+ @Override -+ public int size() { -+ return backingMap.size(); -+ } -+} -diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index c69088a2ec374b2d236fec61e267f42afa2967b1..5dffe28f93925d4c45c9b5c4f7565b00b1aa8dd2 100644 ---- a/src/main/java/io/papermc/paper/util/MCUtil.java -+++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -215,7 +215,7 @@ public final class MCUtil { - } - - public static long getCoordinateKey(final Entity entity) { -- return ((long)(MCUtil.fastFloor(entity.getZ()) >> 4) << 32) | ((MCUtil.fastFloor(entity.getX()) >> 4) & 0xFFFFFFFFL); -+ return ((long)(entity.blockPosition.getZ() >> 4) << 32) | ((entity.blockPosition.getX() >> 4) & 0xFFFFFFFFL); // Pufferfish - eliminate double->long cast in hotpath - } - - public static long getCoordinateKey(final ChunkPos pair) { -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 435f5ee3388f5da93df938c43ea2578f7d586407..d71449697ff4ae17b16d9c3e3a8d0a8d994041fc 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -311,6 +311,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { - AtomicReference atomicreference = new AtomicReference(); -@@ -1693,7 +1695,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return "Pufferfish"; // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! - } - - public SystemReport fillSystemReport(SystemReport details) { -@@ -2272,6 +2274,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop passengers = parent.getPassengers(); -+ -+ for (int i = 0, size = passengers.size(); i < size; i++) { -+ Entity entity = passengers.get(i); -+ int range = entity.getType().clientTrackingRange() * 16; -+ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Paper -+ -+ if (range > highest) { // Paper - we need the lowest range thanks to the fact that our tracker doesn't account for passenger logic // Tuinity - not anymore! -+ highest = range; -+ } -+ -+ highest = getHighestRange(entity, highest); -+ } -+ -+ return highest; -+ } -+ - private int getEffectiveRange() { - int i = this.range; -+ // Pufferfish start - remove iterators and streams -+ /* - Iterator iterator = this.entity.getIndirectPassengers().iterator(); - - while (iterator.hasNext()) { -@@ -1606,6 +1626,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - i = j; - } - } -+ */ -+ i = getHighestRange(this.entity, i); -+ // Pufferfish end - - return this.scaledRange(i); - } -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index acbcdc8cb1523044b1657e03a141fae6389a3686..fe1a9c646b09d11e7fa2186afbeb70b680ad2b57 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -76,6 +76,9 @@ public class ServerChunkCache extends ChunkSource { - final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f); - - private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; -+ -+ public boolean firstRunSpawnCounts = true; // Pufferfish -+ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs - - private static int getChunkCacheKey(int x, int z) { - return x & 3 | ((z & 3) << 2); -@@ -553,6 +556,7 @@ public class ServerChunkCache extends ChunkSource { - ProfilerFiller gameprofilerfiller = this.level.getProfiler(); - - gameprofilerfiller.push("pollingChunks"); -+ this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random - int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); - boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit - -@@ -562,28 +566,35 @@ public class ServerChunkCache extends ChunkSource { - // Paper start - per player mob spawning - NaturalSpawner.SpawnState spawnercreature_d; // moved down - if ((this.spawnFriendlies || this.spawnEnemies) && this.chunkMap.playerMobDistanceMap != null) { // don't count mobs when animals and monsters are disabled -- // re-set mob counts -- for (ServerPlayer player : this.level.players) { -- // Paper start - per player mob spawning backoff -- for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { -- player.mobCounts[ii] = 0; -- -- int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? -- if (newBackoff < 0) { -- newBackoff = 0; -+ // Pufferfish start - moved down when async processing -+ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { -+ // re-set mob counts -+ for (ServerPlayer player : this.level.players) { -+ // Paper start - per player mob spawning backoff -+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { -+ player.mobCounts[ii] = 0; -+ -+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? -+ if (newBackoff < 0) { -+ newBackoff = 0; -+ } -+ player.mobBackoffCounts[ii] = newBackoff; - } -- player.mobBackoffCounts[ii] = newBackoff; -+ // Paper end - per player mob spawning backoff - } -- // Paper end - per player mob spawning backoff -+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); - } -- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, null, true); -+ // Pufferfish end - } else { -- spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); -+ // Pufferfish start - this is only implemented for per-player mob spawning so this makes everything work if this setting is disabled. -+ lastSpawnState = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, this.chunkMap.playerMobDistanceMap == null ? new LocalMobCapCalculator(this.chunkMap) : null, false); -+ _pufferfish_spawnCountsReady.set(true); -+ // Pufferfish end - } - // Paper end - this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings - -- this.lastSpawnState = spawnercreature_d; -+ //this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously - gameprofilerfiller.popPush("filteringLoadedChunks"); - // Paper - moved down - this.level.timings.chunkTicks.startTiming(); // Paper -@@ -622,8 +633,8 @@ public class ServerChunkCache extends ChunkSource { - - if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking - chunk1.incrementInhabitedTime(j); -- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration -- NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1); -+ if (flag2 && (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning || _pufferfish_spawnCountsReady.get()) && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration -+ NaturalSpawner.spawnForChunk(this.level, chunk1, lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1); // Pufferfish - } - - if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking -@@ -685,6 +696,40 @@ public class ServerChunkCache extends ChunkSource { - } - // Paper end - controlled flush for entity tracker packets - } -+ -+ // Pufferfish start - optimize mob spawning -+ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { -+ for (ServerPlayer player : this.level.players) { -+ // Paper start - per player mob spawning backoff -+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { -+ player.mobCounts[ii] = 0; -+ -+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? -+ if (newBackoff < 0) { -+ newBackoff = 0; -+ } -+ player.mobBackoffCounts[ii] = newBackoff; -+ } -+ // Paper end - per player mob spawning backoff -+ } -+ if (firstRunSpawnCounts) { -+ firstRunSpawnCounts = false; -+ _pufferfish_spawnCountsReady.set(true); -+ } -+ if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { -+ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { -+ int mapped = distanceManager.getNaturalSpawnChunkCount(); -+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = -+ level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); -+ gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = -+ new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); -+ lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); -+ objectiterator.finishedIterating(); -+ _pufferfish_spawnCountsReady.set(true); -+ }); -+ } -+ } -+ // Pufferfish end - } - - private void getFullChunk(long pos, Consumer chunkConsumer) { -diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 196280f54e397c69d32bd4d1f6ae666efdd93773..8ab959dd588b5154b63e133b2e937fa2d0ab8e52 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -180,7 +180,8 @@ public class ServerEntity { - long i1 = this.positionCodec.encodeZ(vec3d); - boolean flag6 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; - -- if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()&& !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync -+ if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround() && !(io.papermc.paper.configuration.GlobalConfiguration.get().collisions.sendFullPosForHardCollidingEntities && this.entity.hardCollides())) { // Paper - send full pos for hard colliding entities to prevent collision problems due to desync -+ if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Pufferfish - if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { - if (flag2) { - packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.onGround()); -@@ -194,6 +195,7 @@ public class ServerEntity { - flag4 = true; - flag5 = true; - } -+ } // Pufferfish - } else { - this.wasOnGround = this.entity.onGround(); - this.teleportDelay = 0; -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 18aac3da3c88f33b1a71a5920a8daa27e9723913..eac31c3fcc9161711328588ac852fcae1116d8ef 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -850,6 +850,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - org.spigotmc.ActivationRange.activateEntities(this); // Spigot - timings.entityTick.startTiming(); // Spigot - this.entityTickList.forEach((entity) -> { -+ entity.activatedPriorityReset = false; // Pufferfish - DAB - if (!entity.isRemoved()) { - if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed - entity.discard(); -@@ -869,7 +870,20 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - gameprofilerfiller.push("tick"); -- this.guardEntityTick(this::tickNonPassenger, entity); -+ // Pufferfish start - copied from this.guardEntityTick -+ try { -+ this.tickNonPassenger(entity); // Pufferfish - changed -+ MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick -+ } catch (Throwable throwable) { -+ if (throwable instanceof ThreadDeath) throw throwable; // Paper -+ // Paper start - Prevent tile entity and entity crashes -+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); -+ MinecraftServer.LOGGER.error(msg, throwable); -+ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); -+ entity.discard(); -+ // Paper end -+ } -+ // Pufferfish end - gameprofilerfiller.pop(); - } - } -@@ -934,9 +948,11 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - // Paper start - optimise random block ticking - private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); -- private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(this.random.nextLong()); -+ // private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); // Pufferfish - moved to super - // Paper end - -+ private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish -+ - public void tickChunk(LevelChunk chunk, int randomTickSpeed) { - ChunkPos chunkcoordintpair = chunk.getPos(); - boolean flag = this.isRaining(); -@@ -947,7 +963,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - gameprofilerfiller.push("thunder"); - final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change - -- if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - disable thunder -+ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Pufferfish - replace random with shouldDoLightning - blockposition.set(this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15))); // Paper - if (this.isRainingAt(blockposition)) { - DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition); -@@ -978,7 +994,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - int l; - int i1; - -- if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow -+ if (!this.paperConfig().environment.disableIceAndSnow && (this.currentIceAndSnowTick++ & 15) == 0) { // Paper - Disable ice and snow // Paper - optimise random ticking // Pufferfish - optimize further random ticking - // Paper start - optimise chunk ticking - this.getRandomBlockPosition(j, 0, k, 15, blockposition); - int normalY = chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, blockposition.getX() & 15, blockposition.getZ() & 15) + 1; -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 3c0651fa5a5db880202c9a3805a6455269c5f16d..776c7df81d2b71a5610fe90475f4e8044850beab 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1233,6 +1233,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - @Override - public void handleEditBook(ServerboundEditBookPacket packet) { -+ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableBooks && !this.player.getBukkitEntity().hasPermission("pufferfish.usebooks")) return; // Pufferfish - // Paper start - if (!this.cserver.isPrimaryThread()) { - List pageList = packet.getPages(); -@@ -2420,6 +2421,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - - private boolean updateChatOrder(Instant timestamp) { -+ if (gg.pufferfish.pufferfish.PufferfishConfig.disableOutOfOrderChat) return true; - Instant instant1; - - do { -diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 2ff578e4a953ffcf5176815ba8e3f06f73499989..878001928327d92423d5f7f6d5ce8772d6fa477f 100644 ---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -204,6 +204,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - - @Override - public void onDisconnect(Component reason) { -+ if (gg.pufferfish.pufferfish.PufferfishConfig.suppressNullIdDisconnections && this.gameProfile != null && this.gameProfile.getId() == null && "Disconnected".equals(reason.getString())) return; // Pufferfish - ServerLoginPacketListenerImpl.LOGGER.info("{} lost connection: {}", this.getUserName(), reason.getString()); - } - -diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java -index 241fec02e6869c638d3a160819b32173a081467b..6a8f9e8f5bf108674c47018def28906e2d0a729c 100644 ---- a/src/main/java/net/minecraft/world/CompoundContainer.java -+++ b/src/main/java/net/minecraft/world/CompoundContainer.java -@@ -1,5 +1,6 @@ - package net.minecraft.world; - -+import net.minecraft.core.Direction; // Pufferfish - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.ItemStack; - -@@ -64,6 +65,23 @@ public class CompoundContainer implements Container { - this.container2 = second; - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null); -+ } -+ // Pufferfish end -+ - @Override - public int getContainerSize() { - return this.container1.getContainerSize() + this.container2.getContainerSize(); -diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java -index 04b1531572e8fff1e46fe1c94e7fc863841e0f66..47ddc42f2b63d9d3fae5ae6ea93d418352d76c94 100644 ---- a/src/main/java/net/minecraft/world/Container.java -+++ b/src/main/java/net/minecraft/world/Container.java -@@ -3,6 +3,8 @@ package net.minecraft.world; - import java.util.Set; - import java.util.function.Predicate; - import net.minecraft.core.BlockPos; -+ -+import net.minecraft.core.Direction; // Pufferfish - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.Item; - import net.minecraft.world.item.ItemStack; -@@ -13,6 +15,63 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity; - // CraftBukkit end - - public interface Container extends Clearable { -+ // Pufferfish start - allow the inventory to override and optimize these frequent calls -+ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } -+ return false; -+ } -+ -+ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ -+ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) { -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ // Pufferfish end - - int LARGE_MAX_STACK_SIZE = 64; - int DEFAULT_DISTANCE_LIMIT = 8; -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e8485fb900b25e911a858678a833852731cb2ace..36795968f4c296f680f79cc5a795391ae13c64e4 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -306,7 +306,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public double yo; - public double zo; - private Vec3 position; -- private BlockPos blockPosition; -+ public BlockPos blockPosition; // Pufferfish - private->public - private ChunkPos chunkPosition; - private Vec3 deltaMovement; - private float yRot; -@@ -434,6 +434,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return this.originWorld; - } - // Paper end -+ // Pufferfish start -+ public boolean activatedPriorityReset = false; // DAB -+ public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // golf score -+ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed -+ // Pufferfish end -+ - public float getBukkitYaw() { - return this.yRot; - } -@@ -508,17 +514,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.isLegacyTrackingEntity = isLegacyTrackingEntity; - } - -+ private org.spigotmc.TrackingRange.TrackingRangeType getFurthestEntity(Entity entity, net.minecraft.server.level.ChunkMap chunkMap, org.spigotmc.TrackingRange.TrackingRangeType type, int range) { -+ List passengers = entity.getPassengers(); -+ for (int i = 0, size = passengers.size(); i < size; i++) { -+ Entity passenger = passengers.get(i); -+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; -+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -+ if (passengerRange > range) { -+ type = passengerType; -+ range = passengerRange; -+ } -+ -+ type = this.getFurthestEntity(passenger, chunkMap, type, range); -+ } -+ -+ return type; -+ } -+ - public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet getPlayersInTrackRange() { - // determine highest range of passengers - if (this.passengers.isEmpty()) { - return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()] - .getObjectsInRange(MCUtil.getCoordinateKey(this)); - } -- Iterable passengers = this.getIndirectPassengers(); -+ //Iterable passengers = this.getIndirectPassengers(); // Pufferfish - net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap; - org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType; - int range = chunkMap.getEntityTrackerRange(type.ordinal()); - -+ // Pufferfish start - use getFurthestEntity to skip getIndirectPassengers -+ /* - for (Entity passenger : passengers) { - org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType; - int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal()); -@@ -527,6 +552,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - range = passengerRange; - } - } -+ */ -+ type = this.getFurthestEntity(this, chunkMap, type, range); -+ // Pufferfish end - - return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this)); - } -@@ -798,6 +826,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public void tick() { -+ // Pufferfish start - entity TTL -+ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { -+ discard(); -+ return; -+ } -+ // Pufferfish end - entity TTL - this.baseTick(); - } - -@@ -4283,16 +4317,18 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { -- if (this.touchingUnloadedChunk()) { -+ if (false && this.touchingUnloadedChunk()) { // Pufferfish - cost of a lookup here is the same cost as below, so skip - return false; - } else { - AABB axisalignedbb = this.getBoundingBox().deflate(0.001D); -- int i = Mth.floor(axisalignedbb.minX); -- int j = Mth.ceil(axisalignedbb.maxX); -- int k = Mth.floor(axisalignedbb.minY); -- int l = Mth.ceil(axisalignedbb.maxY); -- int i1 = Mth.floor(axisalignedbb.minZ); -- int j1 = Mth.ceil(axisalignedbb.maxZ); -+ // Pufferfish start - rename -+ int minBlockX = Mth.floor(axisalignedbb.minX); -+ int maxBlockX = Mth.ceil(axisalignedbb.maxX); -+ int minBlockY = Mth.floor(axisalignedbb.minY); -+ int maxBlockY = Mth.ceil(axisalignedbb.maxY); -+ int minBlockZ = Mth.floor(axisalignedbb.minZ); -+ int maxBlockZ = Mth.ceil(axisalignedbb.maxZ); -+ // Pufferfish end - double d1 = 0.0D; - boolean flag = this.isPushedByFluid(); - boolean flag1 = false; -@@ -4300,14 +4336,61 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - int k1 = 0; - BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); - -- for (int l1 = i; l1 < j; ++l1) { -- for (int i2 = k; i2 < l; ++i2) { -- for (int j2 = i1; j2 < j1; ++j2) { -- blockposition_mutableblockposition.set(l1, i2, j2); -- FluidState fluid = this.level().getFluidState(blockposition_mutableblockposition); -+ // Pufferfish start - based off CollisionUtil.getCollisionsForBlocksOrWorldBorder -+ final int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level()); -+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level()); -+ final int minBlock = minSection << 4; -+ final int maxBlock = (maxSection << 4) | 15; -+ -+ // special cases: -+ if (minBlockY > maxBlock || maxBlockY < minBlock) { -+ // no point in checking -+ return false; -+ } -+ -+ int minYIterate = Math.max(minBlock, minBlockY); -+ int maxYIterate = Math.min(maxBlock, maxBlockY); -+ -+ int minChunkX = minBlockX >> 4; -+ int maxChunkX = maxBlockX >> 4; -+ -+ int minChunkZ = minBlockZ >> 4; -+ int maxChunkZ = maxBlockZ >> 4; -+ -+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { -+ int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk -+ int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 16; // coordinate in chunk -+ -+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { -+ int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk -+ int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 16; // coordinate in chunk -+ -+ net.minecraft.world.level.chunk.ChunkAccess chunk = this.level().getChunkIfLoadedImmediately(currChunkX, currChunkZ); -+ if (chunk == null) { -+ return false; // if we're touching an unloaded chunk then it's false -+ } -+ -+ net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections(); -+ -+ for (int currY = minYIterate; currY < maxYIterate; ++currY) { -+ net.minecraft.world.level.chunk.LevelChunkSection section = sections[(currY >> 4) - minSection]; -+ -+ if (section == null || section.hasOnlyAir() || section.fluidStateCount == 0) { // if no fluids, nothing in this section -+ // empty -+ // skip to next section -+ currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one -+ continue; -+ } -+ -+ net.minecraft.world.level.chunk.PalettedContainer blocks = section.states; -+ -+ for (int currZ = minZ; currZ < maxZ; ++currZ) { -+ for (int currX = minX; currX < maxX; ++currX) { -+ FluidState fluid = blocks.get(currX & 15, currY & 15, currZ & 15).getFluidState(); - - if (fluid.is(tag)) { -- double d2 = (double) ((float) i2 + fluid.getHeight(this.level(), blockposition_mutableblockposition)); -+ blockposition_mutableblockposition.set((currChunkX << 4) + currX, currY, (currChunkZ << 4) + currZ); -+ double d2 = (double) ((float) currY + fluid.getHeight(this.level(), blockposition_mutableblockposition)); - - if (d2 >= axisalignedbb.minY) { - flag1 = true; -@@ -4329,9 +4412,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // CraftBukkit end - } - } -+ } -+ } - } - } - } -+ // Pufferfish end - - if (vec3d.length() > 0.0D) { - if (k1 > 0) { -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 9afc81ccb237c3655d64cdbe8a0db9a4d7791043..aa5cec6d56d7a8e80861aa4c9b4a74ca3e64be8c 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -300,6 +300,8 @@ public class EntityType implements FeatureElement, EntityTypeT - private final boolean canSpawnFarFromPlayer; - private final int clientTrackingRange; - private final int updateInterval; -+ public boolean dabEnabled = false; // Pufferfish -+ public int ttl = -1; // Pufferfish - @Nullable - private String descriptionId; - @Nullable -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index e11d7283662834047b2ff81a2fd25a4263792deb..e9a31314424d9db911cd9806741222397c3072d7 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -142,7 +142,6 @@ import org.bukkit.event.entity.EntityTeleportEvent; - import org.bukkit.event.player.PlayerItemConsumeEvent; - // CraftBukkit end - --import co.aikar.timings.MinecraftTimings; // Paper - - public abstract class LivingEntity extends Entity implements Attackable { - -@@ -414,7 +413,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - boolean flag = this instanceof net.minecraft.world.entity.player.Player; - - if (!this.level().isClientSide) { -- if (this.isInWall()) { -+ if ((!gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F))) && this.isInWall()) { // Pufferfish - optimize suffocation - this.hurt(this.damageSources().inWall(), 1.0F); - } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { - double d0 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); -@@ -1369,6 +1368,15 @@ public abstract class LivingEntity extends Entity implements Attackable { - return this.getHealth() <= 0.0F; - } - -+ // Pufferfish start - optimize suffocation -+ public boolean couldPossiblyBeHurt(float amount) { -+ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && amount <= this.lastHurt) { -+ return false; -+ } -+ return true; -+ } -+ // Pufferfish end -+ - @Override - public boolean hurt(DamageSource source, float amount) { - if (this.isInvulnerableTo(source)) { -@@ -1965,6 +1973,20 @@ public abstract class LivingEntity extends Entity implements Attackable { - return this.lastClimbablePos; - } - -+ -+ // Pufferfish start -+ private boolean cachedOnClimable = false; -+ private BlockPos lastClimbingPosition = null; -+ -+ public boolean onClimableCached() { -+ if (!this.blockPosition().equals(this.lastClimbingPosition)) { -+ this.cachedOnClimable = this.onClimbable(); -+ this.lastClimbingPosition = this.blockPosition(); -+ } -+ return this.cachedOnClimable; -+ } -+ // Pufferfish end -+ - public boolean onClimbable() { - if (this.isSpectator()) { - return false; -@@ -3651,7 +3673,10 @@ public abstract class LivingEntity extends Entity implements Attackable { - Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ()); - - // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists -- return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; -+ // Pufferfish start -+ //return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; -+ return vec3d1.distanceToSqr(vec3d) > 128D * 128D ? false : this.level().rayTraceDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ // Pufferfish end - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index e2a25c29ec74147b3e66aa0b3deb85a8f6ee53a5..f6eb032897c6d5d16ab5c8c287e49e189c24571c 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -218,14 +218,16 @@ public abstract class Mob extends LivingEntity implements Targeting { - return this.lookControl; - } - -+ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking - // Paper start - @Override - public void inactiveTick() { - super.inactiveTick(); -- if (this.goalSelector.inactiveTick()) { -+ boolean isThrottled = gg.pufferfish.pufferfish.PufferfishConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking -+ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking - this.goalSelector.tick(); - } -- if (this.targetSelector.inactiveTick()) { -+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority - this.targetSelector.tick(); - } - } -@@ -910,16 +912,20 @@ public abstract class Mob extends LivingEntity implements Targeting { - - if (i % 2 != 0 && this.tickCount > 1) { - this.level().getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tickRunningGoals(false); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goalSelector"); -+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tickRunningGoals(false); - this.level().getProfiler().pop(); - } else { - this.level().getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tick(); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goalSelector"); -+ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tick(); - this.level().getProfiler().pop(); - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index dd1102d5291ef6f18e82400a6d8a0a376cc071e9..e283eb57c25f7de222f9d09dca851169f5f6e488 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -23,9 +23,11 @@ public class AttributeMap { - private final Map attributes = Maps.newHashMap(); - private final Set dirtyAttributes = Sets.newHashSet(); - private final AttributeSupplier supplier; -+ private final java.util.function.Function createInstance; // Pufferfish - - public AttributeMap(AttributeSupplier defaultAttributes) { - this.supplier = defaultAttributes; -+ this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish - } - - private void onAttributeModified(AttributeInstance instance) { -@@ -45,11 +47,10 @@ public class AttributeMap { - }).collect(Collectors.toList()); - } - -+ - @Nullable - public AttributeInstance getInstance(Attribute attribute) { -- return this.attributes.computeIfAbsent(attribute, (attributex) -> { -- return this.supplier.createInstance(this::onAttributeModified, attributex); -- }); -+ return this.attributes.computeIfAbsent(attribute, this.createInstance); // Pufferfish - cache lambda, as for some reason java allocates it anyways - } - - @Nullable -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -index d4c91e0a0c64fcb7f1145de3f30134cb1f1f8ee6..fe502445a77afe7e3807afae48d7bf03f370e290 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -@@ -47,6 +47,7 @@ public class AcquirePoi { - return false; - } else { - mutableLong.setValue(time + 20L + (long)world.getRandom().nextInt(20)); -+ if (entity.getNavigation().isStuck()) mutableLong.add(200); // Pufferfish - wait an additional 10s to check again if they're stuck - PoiManager poiManager = world.getPoiManager(); - long2ObjectMap.long2ObjectEntrySet().removeIf((entry) -> { - return !entry.getValue().isStillValid(time); -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -index 646d9a121d908a2fc3e4e302484dd5cd1bfc6804..e546ecdccde352502e26a8668eaaafe048d6e282 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java -@@ -37,7 +37,11 @@ public class VillagerPanicTrigger extends Behavior { - - @Override - protected void tick(ServerLevel world, Villager entity, long time) { -- if (time % 100L == 0L) { -+ // Pufferfish start -+ if (entity.nextGolemPanic < 0) entity.nextGolemPanic = time + 100; -+ if (--entity.nextGolemPanic < time) { -+ entity.nextGolemPanic = -1; -+ // Pufferfish end - entity.spawnGolemIfNeeded(world, time, 3); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -index b738ee2d3801fadfd09313f05ae24593e56b0ec6..1635818fc4b1788c0d397085239df6dd75b210ab 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -53,9 +53,12 @@ public class GoalSelector { - } - - // Paper start -- public boolean inactiveTick() { -+ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start -+ if (inactive && !gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled) tickRate = 4; // reset to Paper's -+ tickRate = Math.min(tickRate, this.newGoalRate); - this.curRate++; -- return this.curRate % this.newGoalRate == 0; -+ return this.curRate % tickRate == 0; -+ // Pufferfish end - } - public boolean hasTasks() { - for (WrappedGoal task : this.availableGoals) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 34f319ad09276c6f68dde449c79351de0d7d86f5..a719af0b512d9ef243d0d54f3b744b1b1a5f2772 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -@@ -119,6 +119,7 @@ public abstract class MoveToBlockGoal extends Goal { - for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { - for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { - mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); -+ if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Pufferfish - if this block isn't loaded, continue - if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { - this.blockPos = mutableBlockPos; - setTargetPosition(mutableBlockPos.immutable()); // Paper -diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -index 58422f00c7d64dbd1cf6d7211c9838875cbe7778..d25307ae8bbdf10ae067ec70fc2cb957b852a0eb 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -@@ -75,9 +75,18 @@ public class TargetingConditions { - } - - if (this.range > 0.0D) { -- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper -+ // Pufferfish start - check range before getting visibility -+ // d = invisibility percent, e = follow range adjusted for invisibility, f = distance - double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ()); -+ double followRangeRaw = this.useFollowRange ? this.getFollowRange(baseEntity) : this.range; -+ -+ if (f > followRangeRaw * followRangeRaw) { // the actual follow range will always be this value or smaller, so if the distance is larger then it never will return true after getting invis -+ return false; -+ } -+ -+ double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -+ double e = Math.max((followRangeRaw) * d, 2.0D); // Paper -+ // Pufferfish end - if (f > e * e) { - return false; - } -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index 5beaa849a250ea005733250ad3edfa8382224667..2c91fe46355c9a201507de5577f693ed4f5fb974 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -237,13 +237,22 @@ public class Bat extends AmbientCreature { - } - } - -+ // Pufferfish start - only check for spooky season once an hour -+ private static boolean isSpookySeason = false; -+ private static final int ONE_HOUR = 20 * 60 * 60; -+ private static int lastSpookyCheck = -ONE_HOUR; - private static boolean isHalloween() { -+ if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { - LocalDate localdate = LocalDate.now(); - int i = localdate.get(ChronoField.DAY_OF_MONTH); - int j = localdate.get(ChronoField.MONTH_OF_YEAR); - -- return j == 10 && i >= 20 || j == 11 && i <= 3; -+ isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3; -+ lastSpookyCheck = net.minecraft.server.MinecraftServer.currentTick; -+ } -+ return isSpookySeason; - } -+ // Pufferfish end - - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 339c70f101d026a100a801e66cf514b3329a89d2..1a0eee3b766a5ce5623c32ed9c023a0f80db1d1a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -222,9 +222,11 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - return 0.4F; - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("allayBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("allayActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index 2682a49cd3948e0f80e2d7e58abcd3e6d8f7ac4e..42e22a4b9cb6841de04862cc81454da3232aa65a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -285,9 +285,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder { - return true; - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("frogBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("frogActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 4aeab90e778629c355189dfe79c39c4b21f5f5ac..6ed4ac06c76b8d0d6e8db778cade15dbd1e3e5f5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -@@ -77,9 +77,11 @@ public class Tadpole extends AbstractFish { - return SoundEvents.TADPOLE_FLOP; - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("tadpoleBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("tadpoleActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 247aca0b612f5079a0596350e8311c385df8ab1c..7f21d1d400c8a5615ed1a787dcb0680338f8c28d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -189,9 +189,11 @@ public class Goat extends Animal { - return (Brain) super.getBrain(); // CraftBukkit - decompile error - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("goatBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goatActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index 52196431a6538872755344859a0454a0e50c3b6e..80fc7918cb294b0d88a293bd6a920441cb55c3ad 100644 ---- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -270,10 +270,16 @@ public class ItemEntity extends Entity implements TraceableEntity { - if (entityitem.isMergable()) { - // Paper Start - Fix items merging through walls - if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) { -+ // Pufferfish start - skip the allocations -+ /* - net.minecraft.world.level.ClipContext rayTrace = new net.minecraft.world.level.ClipContext(this.position(), entityitem.position(), - net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, this); - net.minecraft.world.phys.BlockHitResult rayTraceResult = this.level().clip(rayTrace); - if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; -+ */ -+ if (level().rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == -+ net.minecraft.world.phys.HitResult.Type.BLOCK) continue; -+ // Pufferfish end - } - // Paper End - this.tryToMerge(entityitem); -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index b62457313a1e30aad0c5313d608667b5d3811455..410f10ad93935d1c078447a4596023f367a8e9b7 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -326,11 +326,17 @@ public class EnderMan extends Monster implements NeutralMob { - private boolean teleport(double x, double y, double z) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z); - -- while (blockposition_mutableblockposition.getY() > this.level().getMinBuildHeight() && !this.level().getBlockState(blockposition_mutableblockposition).blocksMotion()) { -+ // Pufferfish start - single chunk lookup -+ net.minecraft.world.level.chunk.LevelChunk chunk = this.level().getChunkIfLoaded(blockposition_mutableblockposition); -+ if (chunk == null) { -+ return false; -+ } -+ // Pufferfish end -+ while (blockposition_mutableblockposition.getY() > this.level().getMinBuildHeight() && !chunk.getBlockState(blockposition_mutableblockposition).blocksMotion()) { // Pufferfish - blockposition_mutableblockposition.move(Direction.DOWN); - } - -- BlockState iblockdata = this.level().getBlockState(blockposition_mutableblockposition); -+ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Pufferfish - boolean flag = iblockdata.blocksMotion(); - boolean flag1 = iblockdata.getFluidState().is(FluidTags.WATER); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index e8f6c34ea789136d63c0aa88aec90203ef6282b5..d6d61b91096d28eea1e5af69ea1c07890820ee7f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -126,9 +126,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return (Brain) super.getBrain(); // Paper - decompile fix - } - -+ private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("hoglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - HoglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index 27d9145693a772cd1b5d171da303c934101f3be8..e235cc9d1b3ce59ab662ef3cf3ce0267ca78536d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -305,9 +305,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - return !this.cannotHunt; - } - -+ private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("piglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - PiglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 97b763431bc5015448ee7a26a340635a932c950b..71db8bd6885377d55cfc571fccc21df6d8a57b54 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -271,11 +271,13 @@ public class Warden extends Monster implements VibrationSystem { - - } - -+ private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { - ServerLevel worldserver = (ServerLevel) this.level(); - - worldserver.getProfiler().push("wardenBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick(worldserver, this); - this.level().getProfiler().pop(); - super.customServerAiStep(); -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index e30d5ae3e2900f43d7cafde71b8196f26e872841..c4ddf2661bca728d504918171295e10e307b18b4 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -141,6 +141,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return holder.is(PoiTypes.MEETING); - }); - -+ public long nextGolemPanic = -1; // Pufferfish -+ - public Villager(EntityType entityType, Level world) { - this(entityType, world, VillagerType.PLAINS); - } -@@ -244,6 +246,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - // Spigot End - -+ private int behaviorTick = 0; // Pufferfish - @Override - @Deprecated // Paper - protected void customServerAiStep() { -@@ -253,7 +256,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - protected void customServerAiStep(final boolean inactive) { - // Paper end - this.level().getProfiler().push("villagerBrain"); -- if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ // Pufferfish start -+ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { -+ this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ } -+ // Pufferfish end - this.level().getProfiler().pop(); - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; -diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java -index 96d664c28738d6090f7067761c2978dd1aa0fd0e..b1c24a02b87aca7b180a6efbce177f2300db49c1 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Inventory.java -+++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java -@@ -687,6 +687,8 @@ public class Inventory implements Container, Nameable { - } - - public boolean contains(ItemStack stack) { -+ // Pufferfish start - don't allocate iterators -+ /* - Iterator iterator = this.compartments.iterator(); - - while (iterator.hasNext()) { -@@ -701,6 +703,18 @@ public class Inventory implements Container, Nameable { - } - } - } -+ */ -+ for (int i = 0; i < this.compartments.size(); i++) { -+ List list = this.compartments.get(i); -+ for (int j = 0; j < list.size(); j++) { -+ ItemStack itemstack1 = list.get(j); -+ -+ if (!itemstack1.isEmpty() && ItemStack.isSameItemSameTags(itemstack1, stack)) { -+ return true; -+ } -+ } -+ } -+ // Pufferfish end - - return false; - } -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index 1b7cf6d06bdf36f146656727511a461f2520762e..459aee61b519a40d9136546c0d9356562f5757c8 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -44,6 +44,36 @@ public abstract class Projectile extends Entity implements TraceableEntity { - super(type, world); - } - -+ // Pufferfish start -+ private static int loadedThisTick = 0; -+ private static int loadedTick; -+ -+ private int loadedLifetime = 0; -+ @Override -+ public void setPos(double x, double y, double z) { -+ int currentTick = net.minecraft.server.MinecraftServer.currentTick; -+ if (loadedTick != currentTick) { -+ loadedTick = currentTick; -+ loadedThisTick = 0; -+ } -+ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4; -+ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4; -+ if (previousX != newX || previousZ != newZ) { -+ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level().getChunkSource()).getChunkAtIfLoadedMainThread(newX, newZ) != null; -+ if (!isLoaded) { -+ if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { -+ if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { -+ this.discard(); -+ } -+ return; -+ } -+ Projectile.loadedThisTick++; -+ } -+ } -+ super.setPos(x, y, z); -+ } -+ // Pufferfish end -+ - public void setOwner(@Nullable Entity entity) { - if (entity != null) { - this.ownerUUID = entity.getUUID(); -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -index 272095d7a09ab41227d741172735f66fd2798ce1..47692d6db44b58bb724c87128279bd0d3e62a398 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -@@ -27,7 +27,10 @@ import org.bukkit.inventory.InventoryHolder; - - public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity { - -+ // Pufferfish start - private NonNullList itemStacks; -+ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized; -+ // Pufferfish end - @Nullable - public ResourceLocation lootTable; - public long lootTableSeed; -@@ -89,12 +92,18 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - - protected AbstractMinecartContainer(EntityType type, Level world) { - super(type, world); -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - } - - protected AbstractMinecartContainer(EntityType type, double x, double y, double z, Level world) { - super(type, world, x, y, z); -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - } - - @Override -@@ -156,6 +165,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - protected void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); - this.lootableData.loadNbt(nbt); // Paper -+ // Pufferfish start -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.itemStacks = this.itemStacksOptimized.nonNullList; -+ // Pufferfish end - this.readChestVehicleSaveData(nbt); - } - -diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -index 5a19875cbc603acea95193d969d2e1dc1e0bfd78..3688e9f8c6c6d1239095e3a87060ccca90386d0c 100644 ---- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java -+++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -@@ -55,7 +55,7 @@ public class EndCrystalItem extends Item { - world.gameEvent((Entity) context.getPlayer(), GameEvent.ENTITY_PLACE, blockposition1); - EndDragonFight enderdragonbattle = ((ServerLevel) world).getDragonFight(); - -- if (enderdragonbattle != null) { -+ if (enderdragonbattle != null && gg.pufferfish.pufferfish.PufferfishConfig.allowEndCrystalRespawn) { // Pufferfish - enderdragonbattle.tryRespawn(aboveBlockPosition); // Paper - pass placed end crystal position to pre-check proximity to portal - } - } -diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -index 2e60bdc44c33d434bfd9ca5bf8f75de799c6768c..565318c2afaa1661ed9963453a6354dff499f47a 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -@@ -27,8 +27,13 @@ public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBoo - final CraftingBookCategory category; - final ItemStack result; - final NonNullList ingredients; -+ private final boolean isBukkit; // Pufferfish - -+ // Pufferfish start - public ShapelessRecipe(ResourceLocation id, String group, CraftingBookCategory category, ItemStack output, NonNullList input) { -+ this(id, group, category, output, input, false); -+ } -+ public ShapelessRecipe(ResourceLocation id, String group, CraftingBookCategory category, ItemStack output, NonNullList input, boolean isBukkit) { this.isBukkit = isBukkit; // Pufferfish end - this.id = id; - this.group = group; - this.category = category; -@@ -83,6 +88,28 @@ public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBoo - } - - public boolean matches(CraftingContainer inventory, Level world) { -+ // Pufferfish start -+ if (!this.isBukkit) { -+ java.util.List ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0])); -+ -+ inventory: for (int index = 0; index < inventory.getContainerSize(); index++) { -+ ItemStack itemStack = inventory.getItem(index); -+ -+ if (!itemStack.isEmpty()) { -+ for (int i = 0; i < ingredients.size(); i++) { -+ if (ingredients.get(i).test(itemStack)) { -+ ingredients.remove(i); -+ continue inventory; -+ } -+ } -+ return false; -+ } -+ } -+ -+ return ingredients.isEmpty(); -+ } -+ // Pufferfish end -+ - StackedContents autorecipestackmanager = new StackedContents(); - autorecipestackmanager.initialize(this); // Paper - better exact choice recipes - int i = 0; -diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java -index 0e8746759752b692668886370181aa5db1fd0bb0..58e5ce2afabf480f5dfd9adf43f8fc12666861c6 100644 ---- a/src/main/java/net/minecraft/world/level/BlockGetter.java -+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java -@@ -68,6 +68,16 @@ public interface BlockGetter extends LevelHeightAccessor { - }); - } - -+ // Pufferfish start - broken down variant of below rayTraceBlock, used by World#rayTraceDirect -+ default net.minecraft.world.phys.BlockHitResult.Type rayTraceBlockDirect(Vec3 vec3d, Vec3 vec3d1, BlockPos blockposition, BlockState iblockdata, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { -+ if (iblockdata.isAir()) return null; // Tuinity - optimise air cases -+ VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(iblockdata, this, blockposition, voxelshapecoll); -+ net.minecraft.world.phys.BlockHitResult movingobjectpositionblock = this.clipWithInteractionOverride(vec3d, vec3d1, blockposition, voxelshape, iblockdata); -+ -+ return movingobjectpositionblock == null ? null : movingobjectpositionblock.getType(); -+ } -+ // Pufferfish end -+ - // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace - default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { - // Paper start - Prevent raytrace from loading chunks -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index f39ab10c5b0b8d86b579a5b683491204c51db70b..d8d4a1ca2eb062af8b2de4ab44503983587cdd77 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -273,6 +273,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - -+ protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter -+ -+ // Pufferfish start - ensure these get inlined -+ private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection; -+ @Override public final int getMaxBuildHeight() { return this.maxBuildHeight; } -+ @Override public final int getMinSection() { return this.minSection; } -+ @Override public final int getMaxSection() { return this.maxSection; } -+ @Override public final int getMinBuildHeight() { return this.minBuildHeight; } -+ @Override public final int getHeight() { return this.height; } -+ // Pufferfish end -+ - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper -@@ -295,6 +306,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - }); - final DimensionType dimensionmanager = (DimensionType) holder.value(); - -+ // Pufferfish start -+ this.minBuildHeight = dimensionmanager.minY(); -+ this.minSection = SectionPos.blockToSectionCoord(this.minBuildHeight); -+ this.height = dimensionmanager.height(); -+ this.maxBuildHeight = this.minBuildHeight + this.height; -+ this.maxSection = SectionPos.blockToSectionCoord(this.maxBuildHeight - 1) + 1; -+ // Pufferfish end - this.dimension = resourcekey; - this.isClientSide = flag; - if (dimensionmanager.coordinateScale() != 1.0D) { -@@ -412,6 +430,91 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - return null; - } - -+ // Pufferfish start - broken down method of raytracing for EntityLiving#hasLineOfSight, replaces IBlockAccess#rayTrace(RayTrace) -+ public net.minecraft.world.phys.BlockHitResult.Type rayTraceDirect(net.minecraft.world.phys.Vec3 vec3d, net.minecraft.world.phys.Vec3 vec3d1, net.minecraft.world.phys.shapes.CollisionContext voxelshapecoll) { -+ // most of this code comes from IBlockAccess#a(RayTrace, BiFunction, Function), but removes the needless functions -+ if (vec3d.equals(vec3d1)) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ double endX = Mth.lerp(-1.0E-7D, vec3d1.x, vec3d.x); -+ double endY = Mth.lerp(-1.0E-7D, vec3d1.y, vec3d.y); -+ double endZ = Mth.lerp(-1.0E-7D, vec3d1.z, vec3d.z); -+ -+ double startX = Mth.lerp(-1.0E-7D, vec3d.x, vec3d1.x); -+ double startY = Mth.lerp(-1.0E-7D, vec3d.y, vec3d1.y); -+ double startZ = Mth.lerp(-1.0E-7D, vec3d.z, vec3d1.z); -+ -+ int currentX = Mth.floor(startX); -+ int currentY = Mth.floor(startY); -+ int currentZ = Mth.floor(startZ); -+ -+ BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ); -+ -+ LevelChunk chunk = this.getChunkIfLoaded(currentBlock); -+ if (chunk == null) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); -+ -+ if (initialCheck != null) { -+ return initialCheck; -+ } -+ -+ double diffX = endX - startX; -+ double diffY = endY - startY; -+ double diffZ = endZ - startZ; -+ -+ int xDirection = Mth.sign(diffX); -+ int yDirection = Mth.sign(diffY); -+ int zDirection = Mth.sign(diffZ); -+ -+ double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX; -+ double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY; -+ double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ; -+ -+ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX)); -+ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY)); -+ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ)); -+ -+ net.minecraft.world.phys.BlockHitResult.Type result; -+ -+ do { -+ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ -+ if (normalizedXDirection < normalizedYDirection) { -+ if (normalizedXDirection < normalizedZDirection) { -+ currentX += xDirection; -+ normalizedXDirection += normalizedX; -+ } else { -+ currentZ += zDirection; -+ normalizedZDirection += normalizedZ; -+ } -+ } else if (normalizedYDirection < normalizedZDirection) { -+ currentY += yDirection; -+ normalizedYDirection += normalizedY; -+ } else { -+ currentZ += zDirection; -+ normalizedZDirection += normalizedZ; -+ } -+ -+ currentBlock.set(currentX, currentY, currentZ); -+ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) { -+ chunk = this.getChunkIfLoaded(currentBlock); -+ if (chunk == null) { -+ return net.minecraft.world.phys.BlockHitResult.Type.MISS; -+ } -+ } -+ result = this.rayTraceBlockDirect(vec3d, vec3d1, currentBlock, chunk.getBlockState(currentBlock), voxelshapecoll); -+ } while (result == null); -+ -+ return result; -+ } -+ // Pufferfish end -+ - public boolean isInWorldBounds(BlockPos pos) { - return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check - } -@@ -919,13 +1022,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - try { - tickConsumer.accept(entity); - MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick -- } catch (Throwable throwable) { -+ } catch (Throwable throwable) { // Pufferfish - diff on change ServerLevel.tick - if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent tile entity and entity crashes - final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); - MinecraftServer.LOGGER.error(msg, throwable); - getCraftServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable))); -- entity.discard(); -+ entity.discard(); // Pufferfish - diff on change ServerLevel.tick - // Paper end - } - } -@@ -1380,6 +1483,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - - public ProfilerFiller getProfiler() { -+ if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish - return (ProfilerFiller) this.profiler.get(); - } - -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index e85ddf92b4f6f044e2b5834a172f37d78e702ef3..49de1fca183b2c6a0a5399025abfc0e47f314315 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -431,12 +431,12 @@ public final class NaturalSpawner { - } - } - -- private static BlockPos getRandomPosWithin(Level world, LevelChunk chunk) { -+ private static BlockPos getRandomPosWithin(ServerLevel world, LevelChunk chunk) { // Pufferfish - accept serverlevel - ChunkPos chunkcoordintpair = chunk.getPos(); -- int i = chunkcoordintpair.getMinBlockX() + world.random.nextInt(16); -- int j = chunkcoordintpair.getMinBlockZ() + world.random.nextInt(16); -+ int i = chunkcoordintpair.getMinBlockX() + world.getThreadUnsafeRandom().nextInt(16); // Pufferfish - use thread unsafe random -+ int j = chunkcoordintpair.getMinBlockZ() + world.getThreadUnsafeRandom().nextInt(16); // Pufferfish - int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1; -- int l = Mth.randomBetweenInclusive(world.random, world.getMinBuildHeight(), k); -+ int l = Mth.randomBetweenInclusive(world.getThreadUnsafeRandom(), world.getMinBuildHeight(), k); // Pufferfish - - return new BlockPos(i, l, j); - } -diff --git a/src/main/java/net/minecraft/world/level/biome/Biome.java b/src/main/java/net/minecraft/world/level/biome/Biome.java -index 65012a12e1430956ef55ced56773e6354ac26444..ed439b7e94646141c93a7dd3704d1cdeb5c27e16 100644 ---- a/src/main/java/net/minecraft/world/level/biome/Biome.java -+++ b/src/main/java/net/minecraft/world/level/biome/Biome.java -@@ -66,14 +66,20 @@ public final class Biome { - private final BiomeGenerationSettings generationSettings; - private final MobSpawnSettings mobSettings; - private final BiomeSpecialEffects specialEffects; -- private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { -+ // Pufferfish start - use our cache -+ private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { - return Util.make(() -> { -+ /* - Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { - protected void rehash(int i) { - } - }; - long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN); - return long2FloatLinkedOpenHashMap; -+ -+ */ -+ return new gg.airplane.structs.Long2FloatAgingCache(TEMPERATURE_CACHE_SIZE); -+ // Pufferfish end - }); - }); - -@@ -118,17 +124,15 @@ public final class Biome { - @Deprecated - public float getTemperature(BlockPos blockPos) { - long l = blockPos.asLong(); -- Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get(); -- float f = long2FloatLinkedOpenHashMap.get(l); -+ // Pufferfish start -+ gg.airplane.structs.Long2FloatAgingCache cache = this.temperatureCache.get(); -+ float f = cache.getValue(l); - if (!Float.isNaN(f)) { - return f; - } else { - float g = this.getHeightAdjustedTemperature(blockPos); -- if (long2FloatLinkedOpenHashMap.size() == 1024) { -- long2FloatLinkedOpenHashMap.removeFirstFloat(); -- } -- -- long2FloatLinkedOpenHashMap.put(l, g); -+ cache.putValue(l, g); -+ // Pufferfish end - return g; - } - } -diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -index a71414397bd45ee7bcacfeef0041d80dfa25f114..d66806565770cb03a21794f99e5c4b0f3040b26a 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -@@ -31,7 +31,10 @@ import org.bukkit.entity.HumanEntity; - public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity { - - private static final int EVENT_SET_OPEN_COUNT = 1; -+ // Pufferfish start - private NonNullList items; -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; -+ // Pufferfish end - public final ContainerOpenersCounter openersCounter; - private final ChestLidController chestLidController; - -@@ -65,9 +68,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - } - // CraftBukkit end - -+ private final boolean isNative = getClass().equals(ChestBlockEntity.class); // Pufferfish - protected ChestBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); -- this.items = NonNullList.withSize(27, ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - this.openersCounter = new ContainerOpenersCounter() { - @Override - protected void onOpen(Level world, BlockPos pos, BlockState state) { -@@ -98,6 +105,23 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - this.chestLidController = new ChestLidController(); - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ // Pufferfish end -+ - public ChestBlockEntity(BlockPos pos, BlockState state) { - this(BlockEntityType.CHEST, pos, state); - } -@@ -115,7 +139,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -187,7 +214,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ // Pufferfish start -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index b11f51762ca289d99eaa49e66e31e58595bcea4e..f03608a133338b0f5522a07239e06fd2245db1e5 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -@@ -47,7 +47,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - public static final int MOVE_ITEM_SPEED = 8; - public static final int HOPPER_CONTAINER_SIZE = 5; -+ // Pufferfish start - private NonNullList items; -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; // Pufferfish -+ // Pufferfish end - private int cooldownTime; - private long tickedGameTime; - -@@ -83,14 +86,37 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - public HopperBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.HOPPER, pos, state); -- this.items = NonNullList.withSize(5, ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - this.cooldownTime = -1; - } - -+ // Pufferfish start -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return !this.optimizedItems.hasFullStacks(); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ // Pufferfish end -+ - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ // Pufferfish start -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -162,7 +188,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - flag = HopperBlockEntity.ejectItems(world, pos, state, (Container) blockEntity, blockEntity); // CraftBukkit - } - -- if (!blockEntity.inventoryFull()) { -+ if (!blockEntity.optimizedItems.hasFullStacks() || !blockEntity.inventoryFull()) { // Pufferfish - use bitset first - flag |= booleansupplier.getAsBoolean(); - } - -@@ -452,11 +478,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - } - - private static boolean isFullContainer(Container inventory, Direction direction) { -- return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams -+ // Pufferfish start - use bitsets -+ //return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams -+ return inventory.isCompletelyFull(direction); -+ // Pufferfish end - } - - private static boolean isEmptyContainer(Container inv, Direction facing) { -- return allMatch(inv, facing, IS_EMPTY_TEST); -+ // Paper start -+ // Pufferfish start - use bitsets -+ //return allMatch(inv, facing, IS_EMPTY_TEST); -+ return inv.isCompletelyEmpty(facing); -+ // Pufferfish end - } - - public static boolean suckInItems(Level world, Hopper hopper) { -@@ -637,7 +670,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, side)) { - boolean flag = false; -- boolean flag1 = to.isEmpty(); -+ boolean flag1 = to.isCompletelyEmpty(side); // Pufferfish - - if (itemstack1.isEmpty()) { - // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem -@@ -832,7 +865,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ // Pufferfish start -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; -+ // Pufferfish end - } - - public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index 081691f9710ff1115e4308f79ed49fbc38941193..765ee7f78532a363813286ef7db2a7e48605cb06 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -96,12 +96,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc - public boolean isEmpty() { - this.unpackLootTable((Player)null); - // Paper start -- for (final ItemStack itemStack : this.getItems()) { -- if (!itemStack.isEmpty()) { -- return false; -- } -- } -- return true; -+ return this.isCompletelyEmpty(null); // Pufferfish - use super - // Paper end - } - -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 4ff0d2fc9fd76e92e64abd69f2c9e299aa08ac32..3eeb1f0eac76efe9b7c24f6d5787018c7842d07a 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -85,6 +85,18 @@ public class LevelChunk extends ChunkAccess { - private final LevelChunkTicks blockTicks; - private final LevelChunkTicks fluidTicks; - -+ // Pufferfish start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively -+ private int lightningTick; -+ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline -+ public final boolean shouldDoLightning(net.minecraft.util.RandomSource random) { -+ if (this.lightningTick-- <= 0) { -+ this.lightningTick = random.nextInt(this.level.spigotConfig.thunderChance) << 1; -+ return true; -+ } -+ return false; -+ } -+ // Pufferfish end -+ - public LevelChunk(Level world, ChunkPos pos) { - this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); - } -@@ -112,6 +124,8 @@ public class LevelChunk extends ChunkAccess { - this.postLoad = entityLoader; - this.blockTicks = blockTickScheduler; - this.fluidTicks = fluidTickScheduler; -+ -+ this.lightningTick = this.level.getThreadUnsafeRandom().nextInt(100000) << 1; // Pufferfish - initialize lightning tick - } - - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -index d4477b0dda6a1ef7bd8323c0d11b636bd071d18e..5d3dd6a61366ae2c185b62bc9198440ef6227927 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -25,6 +25,7 @@ public class LevelChunkSection { - public final PalettedContainer states; - // CraftBukkit start - read/write - private PalettedContainer> biomes; -+ public short fluidStateCount; // Pufferfish - public final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper - - public LevelChunkSection(PalettedContainer datapaletteblock, PalettedContainer> palettedcontainerro) { -@@ -190,6 +191,7 @@ public class LevelChunkSection { - - if (!fluid.isEmpty()) { - --this.tickingFluidCount; -+ --this.fluidStateCount; // Pufferfish - } - - if (!state.isAir()) { -@@ -204,6 +206,7 @@ public class LevelChunkSection { - - if (!fluid1.isEmpty()) { - ++this.tickingFluidCount; -+ ++this.fluidStateCount; // Pufferfish - } - - this.updateKnownBlockInfo(x | (z << 4) | (y << 8), iblockdata1, state); // Paper -@@ -249,6 +252,7 @@ public class LevelChunkSection { - if (fluid.isRandomlyTicking()) { - this.tickingFluidCount = (short) (this.tickingFluidCount + 1); - } -+ this.fluidStateCount++; // Pufferfish - } - - }); -diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -index 4cdfc433df67afcd455422e9baf56f167dd712ae..57fcf3910f45ce371ac2e237b277b1034caaac4e 100644 ---- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -@@ -8,7 +8,7 @@ import javax.annotation.Nullable; - import net.minecraft.world.entity.Entity; - - public class EntityTickList { -- private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? -+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public - - private void ensureActiveIsNotIterated() { - // Paper - replace with better logic, do not delay removals -diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 5502ad143fd2575f1346334b5b4fe7846628f54e..7d56693102ee558fe784e3a9b9fdcff4b7ad57b9 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -44,6 +44,8 @@ public abstract class FlowingFluid extends Fluid { - public static final BooleanProperty FALLING = BlockStateProperties.FALLING; - public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_FLOWING; - private static final int CACHE_SIZE = 200; -+ // Pufferfish start - use our own cache -+ /* - private static final ThreadLocal> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> { - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap(200) { - protected void rehash(int i) {} -@@ -52,6 +54,14 @@ public abstract class FlowingFluid extends Fluid { - object2bytelinkedopenhashmap.defaultReturnValue((byte) 127); - return object2bytelinkedopenhashmap; - }); -+ */ -+ -+ private static final ThreadLocal> localFluidDirectionCache = ThreadLocal.withInitial(() -> { -+ // Pufferfish todo - mess with this number for performance -+ // with 2048 it seems very infrequent on a small world that it has to remove old entries -+ return new gg.airplane.structs.FluidDirectionCache<>(2048); -+ }); -+ // Pufferfish end - private final Map shapes = Maps.newIdentityHashMap(); - - public FlowingFluid() {} -@@ -240,6 +250,8 @@ public abstract class FlowingFluid extends Fluid { - } - - private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { -+ // Pufferfish start - modify to use our cache -+ /* - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; - - if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -@@ -247,9 +259,16 @@ public abstract class FlowingFluid extends Fluid { - } else { - object2bytelinkedopenhashmap = null; - } -+ */ -+ gg.airplane.structs.FluidDirectionCache cache = null; -+ -+ if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { -+ cache = localFluidDirectionCache.get(); -+ } - - Block.BlockStatePairKey block_a; - -+ /* - if (object2bytelinkedopenhashmap != null) { - block_a = new Block.BlockStatePairKey(state, fromState, face); - byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a); -@@ -260,11 +279,22 @@ public abstract class FlowingFluid extends Fluid { - } else { - block_a = null; - } -+ */ -+ if (cache != null) { -+ block_a = new Block.BlockStatePairKey(state, fromState, face); -+ Boolean flag = cache.getValue(block_a); -+ if (flag != null) { -+ return flag; -+ } -+ } else { -+ block_a = null; -+ } - - VoxelShape voxelshape = state.getCollisionShape(world, pos); - VoxelShape voxelshape1 = fromState.getCollisionShape(world, fromPos); - boolean flag = !Shapes.mergedFaceOccludes(voxelshape, voxelshape1, face); - -+ /* - if (object2bytelinkedopenhashmap != null) { - if (object2bytelinkedopenhashmap.size() == 200) { - object2bytelinkedopenhashmap.removeLastByte(); -@@ -272,6 +302,11 @@ public abstract class FlowingFluid extends Fluid { - - object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); - } -+ */ -+ if (cache != null) { -+ cache.putValue(block_a, flag); -+ } -+ // Pufferfish end - - return flag; - } -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java b/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java -index e43d07ccdd36f0c9f5b8e9c74cf0d87e17eec66a..8e441f7c2b2d911a0c0111aaa231fc6adae08730 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/LootParams.java -@@ -21,8 +21,10 @@ public class LootParams { - - public LootParams(ServerLevel world, Map, Object> parameters, Map dynamicDrops, float luck) { - this.level = world; -- this.params = parameters; -- this.dynamicDrops = dynamicDrops; -+ // Pufferfish start - use unmodifiable maps instead of immutable ones to skip the copy -+ this.params = java.util.Collections.unmodifiableMap(parameters); -+ this.dynamicDrops = java.util.Collections.unmodifiableMap(dynamicDrops); -+ // Pufferfish end - this.luck = luck; - } - -diff --git a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -index ebe65474a4a05ff1637d7f37ebcfe690af59def5..42142c512b12e5b269c19f1e821c50e7496a5f25 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/EntityCollisionContext.java -@@ -19,47 +19,66 @@ public class EntityCollisionContext implements CollisionContext { - return defaultValue; - } - }; -- private final boolean descending; -- private final double entityBottom; -- private final ItemStack heldItem; -- private final Predicate canStandOnFluid; -+ // Pufferfish start - remove these and pray no plugin uses them -+ // private final boolean descending; -+ // private final double entityBottom; -+ // private final ItemStack heldItem; -+ // private final Predicate canStandOnFluid; -+ // Pufferfish end - @Nullable - private final Entity entity; - - protected EntityCollisionContext(boolean descending, double minY, ItemStack heldItem, Predicate walkOnFluidPredicate, @Nullable Entity entity) { -- this.descending = descending; -- this.entityBottom = minY; -- this.heldItem = heldItem; -- this.canStandOnFluid = walkOnFluidPredicate; -+ // Pufferfish start - remove these -+ // this.descending = descending; -+ // this.entityBottom = minY; -+ // this.heldItem = heldItem; -+ // this.canStandOnFluid = walkOnFluidPredicate; -+ // Pufferfish end - this.entity = entity; - } - - /** @deprecated */ - @Deprecated - protected EntityCollisionContext(Entity entity) { -- this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluidState) -> { -- return false; -- }, entity); -+ // Pufferfish start - remove this -+ // this(entity.isDescending(), entity.getY(), entity instanceof LivingEntity ? ((LivingEntity)entity).getMainHandItem() : ItemStack.EMPTY, entity instanceof LivingEntity ? ((LivingEntity)entity)::canStandOnFluid : (fluidState) -> { -+ // return false; -+ // }, entity); -+ // Pufferfish end -+ this.entity = entity; - } - - @Override - public boolean isHoldingItem(Item item) { -- return this.heldItem.is(item); -+ // Pufferfish start -+ Entity entity = this.entity; -+ if (entity instanceof LivingEntity livingEntity) { -+ return livingEntity.getMainHandItem().is(item); -+ } -+ return ItemStack.EMPTY.is(item); -+ // Pufferfish end - } - - @Override - public boolean canStandOnFluid(FluidState stateAbove, FluidState state) { -- return this.canStandOnFluid.test(state) && !stateAbove.getType().isSame(state.getType()); -+ // Pufferfish start -+ Entity entity = this.entity; -+ if (entity instanceof LivingEntity livingEntity) { -+ return livingEntity.canStandOnFluid(state) && !stateAbove.getType().isSame(state.getType()); -+ } -+ return false; -+ // Pufferfish end - } - - @Override - public boolean isDescending() { -- return this.descending; -+ return this.entity != null && this.entity.isDescending(); // Pufferfish - } - - @Override - public boolean isAbove(VoxelShape shape, BlockPos pos, boolean defaultValue) { -- return this.entityBottom > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; -+ return (this.entity == null ? -Double.MAX_VALUE : entity.getY()) > (double)pos.getY() + shape.max(Direction.Axis.Y) - (double)1.0E-5F; // Pufferfish - } - - @Nullable -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index ec4b73321205b472f19fa5bd4ad95893020d1340..74c46cea456f4a736325892bb7b4d0f1b35b62cd 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -265,7 +265,7 @@ import javax.annotation.Nullable; // Paper - import javax.annotation.Nonnull; // Paper - - public final class CraftServer implements Server { -- private final String serverName = "Paper"; // Paper -+ private final String serverName = "Pufferfish"; // Paper // Pufferfish - private final String serverVersion; - private final String bukkitVersion = Versioning.getBukkitVersion(); - private final Logger logger = Logger.getLogger("Minecraft"); -@@ -1140,6 +1140,11 @@ public final class CraftServer implements Server { - plugin.getPluginMeta().getDisplayName(), - "This plugin is not properly shutting down its async tasks when it is being shut down. This task may throw errors during the final shutdown logs and might not complete before process dies." - )); -+ getLogger().log(Level.SEVERE, String.format("%s Stacktrace", worker.getThread().getName())); -+ StackTraceElement[] stackTrace = worker.getThread().getStackTrace(); -+ for (StackTraceElement element : stackTrace) { -+ getLogger().log(Level.SEVERE, " " + element.toString()); -+ } - } - } - // Paper end -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -index f7ea77dd82d978ad307f99c743efacfb34478b3d..009ab06182359862b8f543030ec4fe4e2572c93c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java -@@ -44,6 +44,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe - data.set(i, toNMS(ingred.get(i), true)); - } - -- MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data)); -+ MinecraftServer.getServer().getRecipeManager().addRecipe(new net.minecraft.world.item.crafting.ShapelessRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data, true)); - } - } -diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 719e7103f7dfdc30f1cefd24a3fa572fa0ac8b1e..2b4581f92543c11f31bcc1417e90d7f90b2aea20 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -470,7 +470,7 @@ public final class CraftMagicNumbers implements UnsafeValues { - - @Override - public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { -- return new com.destroystokyo.paper.PaperVersionFetcher(); -+ return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 774556a62eb240da42e84db4502e2ed43495be17..80553face9c70c2a3d897681e7761df85b22d464 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -@@ -11,7 +11,7 @@ public final class Versioning { - public static String getBukkitVersion() { - String result = "Unknown-Version"; - -- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); -+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish - Properties properties = new Properties(); - - if (stream != null) { -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index eda7f0bb42f7269676d5d2193e1155912ede9920..68557964e27fa1e5ba218178f9bcc0b28e3a78d9 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -38,6 +38,10 @@ import co.aikar.timings.MinecraftTimings; - import net.minecraft.world.entity.schedule.Activity; - import net.minecraft.world.level.Level; - import net.minecraft.world.phys.AABB; -+// Pufferfish start -+import net.minecraft.world.phys.Vec3; -+import java.util.List; -+// Pufferfish end - - public class ActivationRange - { -@@ -217,6 +221,25 @@ public class ActivationRange - for (int i = 0; i < entities.size(); i++) { - Entity entity = entities.get(i); - ActivationRange.activateEntity(entity); -+ -+ // Pufferfish start -+ if (gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled && entity.getType().dabEnabled) { -+ if (!entity.activatedPriorityReset) { -+ entity.activatedPriorityReset = true; -+ entity.activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; -+ } -+ Vec3 playerVec = player.position(); -+ Vec3 entityVec = entity.position(); -+ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; -+ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); -+ entity.activatedPriority = squaredDistance > gg.pufferfish.pufferfish.PufferfishConfig.startDistanceSquared ? -+ Math.max(1, Math.min(squaredDistance >> gg.pufferfish.pufferfish.PufferfishConfig.activationDistanceMod, entity.activatedPriority)) : -+ 1; -+ } else { -+ entity.activatedPriority = 1; -+ } -+ // Pufferfish end -+ - } - // Paper end - } -@@ -233,12 +256,12 @@ public class ActivationRange - if ( MinecraftServer.currentTick > entity.activatedTick ) - { - if ( entity.defaultActivationState ) -- { -+ { // Pufferfish - diff on change - entity.activatedTick = MinecraftServer.currentTick; - return; - } - if ( entity.activationType.boundingBox.intersects( entity.getBoundingBox() ) ) -- { -+ { // Pufferfish - diff on change - entity.activatedTick = MinecraftServer.currentTick; - } - } -@@ -292,7 +315,7 @@ public class ActivationRange - if ( entity instanceof LivingEntity ) - { - LivingEntity living = (LivingEntity) entity; -- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper -+ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 ) // Paper // Pufferfish - use cached - { - return 1; // Paper - } diff --git a/patches/server/0002-Fix-pufferfish-issues.patch b/patches/server/0002-Fix-pufferfish-issues.patch deleted file mode 100644 index b72835a19..000000000 --- a/patches/server/0002-Fix-pufferfish-issues.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 12 Jun 2022 09:18:57 -0500 -Subject: [PATCH] Fix pufferfish issues - - -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -index 0dd3374468e05f7a312ba5856b9cf8a4787dfa59..960c0555a001fe63de78d77d5ea47d08a520d029 100644 ---- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -224,7 +224,7 @@ public class PufferfishConfig { - public static int activationDistanceMod; - - private static void dynamicActivationOfBrains() throws IOException { -- dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); -+ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", false); // Purpur - startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, - "This value determines how far away an entity has to be", - "from the player to start being effected by DEAR."); -@@ -268,7 +268,7 @@ public class PufferfishConfig { - - public static boolean throttleInactiveGoalSelectorTick; - private static void inactiveGoalSelectorThrottle() { -- throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, -+ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", false, // Purpur - "Throttles the AI goal selector in entity inactive ticks.", - "This can improve performance by a few percent, but has minor gameplay implications."); - } -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index eac31c3fcc9161711328588ac852fcae1116d8ef..dbb59969cf55eda997588f4c3ef7dc899ea619bb 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -948,7 +948,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - // Paper start - optimise random block ticking - private final BlockPos.MutableBlockPos chunkTickMutablePosition = new BlockPos.MutableBlockPos(); -- // private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(); // Pufferfish - moved to super -+ private final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(this.random.nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - moved to super // Purpur - dont break ABI - // Paper end - - private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.randomTickRandom.nextInt(16); } // Pufferfish -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 30d8ec75203adc677e5fb91d9538baf52a0684ad..5eca99fbd23ff0d35607bd185b011d6f9a30d0f0 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -273,7 +273,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public abstract ResourceKey getTypeKey(); - -- protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter -+ //protected final io.papermc.paper.util.math.ThreadUnsafeRandom randomTickRandom = new io.papermc.paper.util.math.ThreadUnsafeRandom(java.util.concurrent.ThreadLocalRandom.current().nextLong()); public net.minecraft.util.RandomSource getThreadUnsafeRandom() { return this.randomTickRandom; } // Pufferfish - move thread unsafe random initialization // Pufferfish - getter // Purpur - dont break ABI - - // Pufferfish start - ensure these get inlined - private final int minBuildHeight, minSection, height, maxBuildHeight, maxSection; -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 3eeb1f0eac76efe9b7c24f6d5787018c7842d07a..dbb0593a6feb60216379bde6720ca16f3ca827ae 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -125,7 +125,7 @@ public class LevelChunk extends ChunkAccess { - this.blockTicks = blockTickScheduler; - this.fluidTicks = fluidTickScheduler; - -- this.lightningTick = this.level.getThreadUnsafeRandom().nextInt(100000) << 1; // Pufferfish - initialize lightning tick -+ this.lightningTick = java.util.concurrent.ThreadLocalRandom.current().nextInt(100000) << 1; // Pufferfish - initialize lightning tick // Purpur - any random will do - } - - // CraftBukkit start diff --git a/patches/server/0004-Purpur-config-files.patch b/patches/server/0004-Purpur-config-files.patch deleted file mode 100644 index 9d66f3718..000000000 --- a/patches/server/0004-Purpur-config-files.patch +++ /dev/null @@ -1,529 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 9 May 2019 18:09:43 -0500 -Subject: [PATCH] Purpur config files - - -diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index 692c962193cf9fcc6801fc93f3220bdc673d527b..8cde30544e14f8fc2dac32966ae3c21f8cf3a551 100644 ---- a/src/main/java/com/destroystokyo/paper/Metrics.java -+++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -593,7 +593,7 @@ public class Metrics { - boolean logFailedRequests = config.getBoolean("logFailedRequests", false); - // Only start Metrics, if it's enabled in the config - if (config.getBoolean("enabled", true)) { -- Metrics metrics = new Metrics("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish -+ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish // Purpur - - metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { - String minecraftVersion = Bukkit.getVersion(); -@@ -602,16 +602,8 @@ public class Metrics { - })); - - metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); -- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); -- final String paperVersion; -- final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); -- if (implVersion != null) { -- final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); -- paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish -- } else { -- paperVersion = "unknown"; -- } -- metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish -+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur -+ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur - - metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { - Map> map = new HashMap<>(); -diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java -index 0de2eae2d448ac9e269a4edf48406d5ea8af8059..f03fd95412883a3a5bbe2b91c603874bf147e6cb 100644 ---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java -+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java -@@ -336,6 +336,30 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy - } - } - -+ // Purpur start -+ public void sendSuccess(@Nullable String message) { -+ sendSuccess(message, false); -+ } -+ -+ public void sendSuccess(@Nullable String message, boolean broadcastToOps) { -+ if (message == null) { -+ return; -+ } -+ sendSuccess(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), broadcastToOps); -+ } -+ -+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message) { -+ sendSuccess(message, false); -+ } -+ -+ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message, boolean broadcastToOps) { -+ if (message == null) { -+ return; -+ } -+ sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps); -+ } -+ // Purpur end -+ - public void sendSuccess(Supplier feedbackSupplier, boolean broadcastToOps) { - boolean flag1 = this.source.acceptsSuccess() && !this.silent; - boolean flag2 = broadcastToOps && this.source.shouldInformAdmins() && !this.silent; -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 28af96ce4f9f1a83316b6fd9e1625c71b5874c3f..cf7c7b3cd4081e1be059647dca237ffd72c374df 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -218,6 +218,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - thread.start(); // Paper - start console thread after MinecraftServer.console & PaperConfig are initialized - io.papermc.paper.command.PaperCommands.registerCommands(this); - com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); -+ // Purpur start -+ try { -+ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings")); -+ } catch (Exception e) { -+ DedicatedServer.LOGGER.error("Unable to load server configuration", e); -+ return false; -+ } -+ org.purpurmc.purpur.PurpurConfig.registerCommands(); -+ // Purpur end - com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now - io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider - // Paper end -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index f5a3203c60e555c496626c842d72e4cdc6cd9f6e..dc71cf3c66a0a4390177428688e6f4ee39a981b6 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -176,6 +176,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - // Paper end - - public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray -+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - public final co.aikar.timings.WorldTimingsHandler timings; // Paper - public static BlockPos lastPhysicsProblem; // Spigot - private org.spigotmc.TickLimiter entityLimiter; -@@ -287,6 +288,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper -+ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur - this.generator = gen; - this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 4a5804e14b82229fc4b4bf44725d79f55d0d4c80..a78cf70e754ec35366fc269cca8e2deda14635b0 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1057,6 +1057,7 @@ public final class CraftServer implements Server { - - org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot - this.console.paperConfigurations.reloadConfigs(this.console); -+ org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur - for (ServerLevel world : this.console.getAllLevels()) { - // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty - world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -1072,6 +1073,7 @@ public final class CraftServer implements Server { - } - } - world.spigotConfig.init(); // Spigot -+ world.purpurConfig.init(); // Purpur - } - - Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper -@@ -1087,6 +1089,7 @@ public final class CraftServer implements Server { - this.reloadData(); - org.spigotmc.SpigotConfig.registerCommands(); // Spigot - io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper -+ org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); - this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); - -@@ -2937,6 +2940,18 @@ public final class CraftServer implements Server { - return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); - } - -+ // Purpur start -+ @Override -+ public YamlConfiguration getPurpurConfig() { -+ return org.purpurmc.purpur.PurpurConfig.config; -+ } -+ -+ @Override -+ public java.util.Properties getServerProperties() { -+ return getProperties().properties; -+ } -+ // Purpur end -+ - @Override - public void restart() { - org.spigotmc.RestartCommand.restart(); -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 776b7fc26fe96b69be260bbd36efae147d988640..b1ac20e88ba0db9b6591df745ca8bc4bc3f9e4ae 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -173,6 +173,14 @@ public class Main { - .describedAs("Jar file"); - // Paper end - -+ // Purpur Start -+ acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("purpur.yml")) -+ .describedAs("Yml file"); -+ // Purpur end -+ - // Paper start - acceptsAll(asList("server-name"), "Name of the server") - .withRequiredArg() -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..61d193d8ddd87817bf2c560037d42366cff1eca9 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -0,0 +1,175 @@ -+package org.purpurmc.purpur; -+ -+import com.google.common.base.Throwables; -+import com.google.common.collect.ImmutableMap; -+import com.mojang.datafixers.util.Pair; -+import net.kyori.adventure.bossbar.BossBar; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.minecraft.core.registries.BuiltInRegistries; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.effect.MobEffect; -+import net.minecraft.world.effect.MobEffectInstance; -+import net.minecraft.world.entity.EntityDimensions; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.food.FoodProperties; -+import net.minecraft.world.food.Foods; -+import net.minecraft.world.item.enchantment.Enchantment; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.block.Blocks; -+import org.bukkit.Bukkit; -+import org.bukkit.command.Command; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.InvalidConfigurationException; -+import org.bukkit.configuration.file.YamlConfiguration; -+import org.purpurmc.purpur.command.PurpurCommand; -+import org.purpurmc.purpur.task.TPSBarTask; -+ -+import java.io.File; -+import java.io.IOException; -+import java.lang.reflect.InvocationTargetException; -+import java.lang.reflect.Method; -+import java.lang.reflect.Modifier; -+import java.util.ArrayList; -+import java.util.Collections; -+import java.util.HashMap; -+import java.util.HashSet; -+import java.util.List; -+import java.util.Map; -+import java.util.Set; -+import java.util.logging.Level; -+ -+@SuppressWarnings("unused") -+public class PurpurConfig { -+ private static final String HEADER = "This is the main configuration file for Purpur.\n" -+ + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" -+ + "with caution, and make sure you know what each option does before configuring.\n" -+ + "\n" -+ + "If you need help with the configuration or have any questions related to Purpur,\n" -+ + "join us in our Discord guild.\n" -+ + "\n" -+ + "Website: https://purpurmc.org \n" -+ + "Docs: https://purpurmc.org/docs \n"; -+ private static File CONFIG_FILE; -+ public static YamlConfiguration config; -+ -+ private static Map commands; -+ -+ public static int version; -+ static boolean verbose; -+ -+ public static void init(File configFile) { -+ CONFIG_FILE = configFile; -+ config = new YamlConfiguration(); -+ try { -+ config.load(CONFIG_FILE); -+ } catch (IOException ignore) { -+ } catch (InvalidConfigurationException ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex); -+ throw Throwables.propagate(ex); -+ } -+ config.options().header(HEADER); -+ config.options().copyDefaults(true); -+ verbose = getBoolean("verbose", false); -+ -+ commands = new HashMap<>(); -+ commands.put("purpur", new PurpurCommand("purpur")); -+ -+ version = getInt("config-version", 33); -+ set("config-version", 33); -+ -+ readConfig(PurpurConfig.class, null); -+ -+ Blocks.rebuildCache(); -+ } -+ -+ protected static void log(String s) { -+ if (verbose) { -+ log(Level.INFO, s); -+ } -+ } -+ -+ protected static void log(Level level, String s) { -+ Bukkit.getLogger().log(level, s); -+ } -+ -+ public static void registerCommands() { -+ for (Map.Entry entry : commands.entrySet()) { -+ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue()); -+ } -+ } -+ -+ static void readConfig(Class clazz, Object instance) { -+ for (Method method : clazz.getDeclaredMethods()) { -+ if (Modifier.isPrivate(method.getModifiers())) { -+ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { -+ try { -+ method.setAccessible(true); -+ method.invoke(instance); -+ } catch (InvocationTargetException ex) { -+ throw Throwables.propagate(ex.getCause()); -+ } catch (Exception ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); -+ } -+ } -+ } -+ } -+ -+ try { -+ config.save(CONFIG_FILE); -+ } catch (IOException ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); -+ } -+ } -+ -+ private static void set(String path, Object val) { -+ config.addDefault(path, val); -+ config.set(path, val); -+ } -+ -+ private static String getString(String path, String def) { -+ config.addDefault(path, def); -+ return config.getString(path, config.getString(path)); -+ } -+ -+ private static boolean getBoolean(String path, boolean def) { -+ config.addDefault(path, def); -+ return config.getBoolean(path, config.getBoolean(path)); -+ } -+ -+ private static double getDouble(String path, double def) { -+ config.addDefault(path, def); -+ return config.getDouble(path, config.getDouble(path)); -+ } -+ -+ private static int getInt(String path, int def) { -+ config.addDefault(path, def); -+ return config.getInt(path, config.getInt(path)); -+ } -+ -+ private static List getList(String path, T def) { -+ config.addDefault(path, def); -+ return config.getList(path, config.getList(path)); -+ } -+ -+ static Map getMap(String path, Map def) { -+ if (def != null && config.getConfigurationSection(path) == null) { -+ config.addDefault(path, def); -+ return def; -+ } -+ return toMap(config.getConfigurationSection(path)); -+ } -+ -+ private static Map toMap(ConfigurationSection section) { -+ ImmutableMap.Builder builder = ImmutableMap.builder(); -+ if (section != null) { -+ for (String key : section.getKeys(false)) { -+ Object obj = section.get(key); -+ if (obj != null) { -+ builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj); -+ } -+ } -+ } -+ return builder.build(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5f0732c2b8f85185b6dfc1db3119c22e8be7f5da ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -0,0 +1,92 @@ -+package org.purpurmc.purpur; -+ -+import net.minecraft.core.registries.BuiltInRegistries; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.util.Mth; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.item.DyeColor; -+import net.minecraft.world.item.Item; -+import net.minecraft.world.item.Items; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.block.Blocks; -+import net.minecraft.world.level.block.state.properties.Tilt; -+import org.purpurmc.purpur.tool.Flattenable; -+import org.purpurmc.purpur.tool.Strippable; -+import org.purpurmc.purpur.tool.Tillable; -+import org.purpurmc.purpur.tool.Waxable; -+import org.purpurmc.purpur.tool.Weatherable; -+import org.apache.commons.lang.BooleanUtils; -+import org.bukkit.ChatColor; -+import org.bukkit.World; -+import org.bukkit.configuration.ConfigurationSection; -+import java.util.ArrayList; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.function.Predicate; -+import java.util.logging.Level; -+import static org.purpurmc.purpur.PurpurConfig.log; -+ -+@SuppressWarnings("unused") -+public class PurpurWorldConfig { -+ -+ private final String worldName; -+ private final World.Environment environment; -+ -+ public PurpurWorldConfig(String worldName, World.Environment environment) { -+ this.worldName = worldName; -+ this.environment = environment; -+ init(); -+ } -+ -+ public void init() { -+ log("-------- World Settings For [" + worldName + "] --------"); -+ PurpurConfig.readConfig(PurpurWorldConfig.class, this); -+ } -+ -+ private void set(String path, Object val) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, val); -+ PurpurConfig.config.set("world-settings.default." + path, val); -+ if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) { -+ PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val); -+ PurpurConfig.config.set("world-settings." + worldName + "." + path, val); -+ } -+ } -+ -+ private ConfigurationSection getConfigurationSection(String path) { -+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path); -+ return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path); -+ } -+ -+ private String getString(String path, String def) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, def); -+ return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path)); -+ } -+ -+ private boolean getBoolean(String path, boolean def) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, def); -+ return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path)); -+ } -+ -+ private double getDouble(String path, double def) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, def); -+ return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path)); -+ } -+ -+ private int getInt(String path, int def) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, def); -+ return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path)); -+ } -+ -+ private List getList(String path, T def) { -+ PurpurConfig.config.addDefault("world-settings.default." + path, def); -+ return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path)); -+ } -+ -+ private Map getMap(String path, Map def) { -+ final Map fallback = PurpurConfig.getMap("world-settings.default." + path, def); -+ final Map value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null); -+ return value.isEmpty() ? fallback : value; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..afdf04f8b22ad0b7c0b41675e44687b49c2f86d6 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java -@@ -0,0 +1,65 @@ -+package org.purpurmc.purpur.command; -+ -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import org.purpurmc.purpur.PurpurConfig; -+import org.bukkit.ChatColor; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+ -+import java.io.File; -+import java.util.Collections; -+import java.util.List; -+import java.util.stream.Collectors; -+import java.util.stream.Stream; -+ -+public class PurpurCommand extends Command { -+ public PurpurCommand(String name) { -+ super(name); -+ this.description = "Purpur related commands"; -+ this.usageMessage = "/purpur [reload | version]"; -+ this.setPermission("bukkit.command.purpur"); -+ } -+ -+ @Override -+ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { -+ if (args.length == 1) { -+ return Stream.of("reload", "version") -+ .filter(arg -> arg.startsWith(args[0].toLowerCase())) -+ .collect(Collectors.toList()); -+ } -+ return Collections.emptyList(); -+ } -+ -+ @Override -+ public boolean execute(CommandSender sender, String commandLabel, String[] args) { -+ if (!testPermission(sender)) return true; -+ -+ if (args.length != 1) { -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); -+ return false; -+ } -+ -+ if (args[0].equalsIgnoreCase("reload")) { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); -+ -+ MinecraftServer console = MinecraftServer.getServer(); -+ PurpurConfig.init((File) console.options.valueOf("purpur-settings")); -+ for (ServerLevel level : console.getAllLevels()) { -+ level.purpurConfig.init(); -+ } -+ console.server.reloadCount++; -+ -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete."); -+ } else if (args[0].equalsIgnoreCase("version")) { -+ Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -+ if (verCmd != null) { -+ return verCmd.execute(sender, commandLabel, new String[0]); -+ } -+ } -+ -+ return true; -+ } -+} diff --git a/patches/server/0005-Purpur-client-support.patch b/patches/server/0005-Purpur-client-support.patch deleted file mode 100644 index 5fe9d13fd..000000000 --- a/patches/server/0005-Purpur-client-support.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 30 Jul 2021 14:31:25 -0500 -Subject: [PATCH] Purpur client support - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 271de9d39dd765e4b4153670daa5cf6a4df22456..5787e59a7ce1aedbe655928fa2e283823a450343 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -278,6 +278,7 @@ public class ServerPlayer extends Player { - public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet cachedSingleHashSet; // Paper - public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event -+ public boolean purpurClient = false; // Purpur - - private final java.util.concurrent.atomic.AtomicReference viewDistances = new java.util.concurrent.atomic.AtomicReference<>(new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.ViewDistances(-1, -1, -1)); - public io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 776c7df81d2b71a5610fe90475f4e8044850beab..63eac8b250b8bae40de432a0ffd4792122cbae74 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -3595,6 +3595,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private static final ResourceLocation CUSTOM_UNREGISTER = new ResourceLocation("unregister"); - - private static final ResourceLocation MINECRAFT_BRAND = new ResourceLocation("brand"); // Paper - Brand support -+ private static final ResourceLocation PURPUR_CLIENT = new ResourceLocation("purpur", "client"); // Purpur - - @Override - public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { -@@ -3619,6 +3620,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); - this.disconnect("Invalid payload UNREGISTER!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause - } -+ // Purpur start -+ } else if (packet.identifier.equals(PURPUR_CLIENT)) { -+ try { -+ player.purpurClient = true; -+ } catch (Exception ignore) { -+ } -+ // Purpur end - } else { - try { - byte[] data = new byte[packet.data.readableBytes()]; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 4e6fea7cf11b1e29ae7c7098a6f5d06bb5f93cc2..7f7375c0a3e680ef60fb9fc6157b72bb0812d1c3 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3267,4 +3267,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - return this.spigot; - } - // Spigot end -+ -+ // Purpur start -+ @Override -+ public boolean usesPurpurClient() { -+ return getHandle().purpurClient; -+ } -+ // Purpur end - } diff --git a/patches/server/0006-Fix-decompile-errors.patch b/patches/server/0006-Fix-decompile-errors.patch deleted file mode 100644 index 9f9c3c120..000000000 --- a/patches/server/0006-Fix-decompile-errors.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 12 Jun 2022 06:20:21 -0500 -Subject: [PATCH] Fix decompile errors - - -diff --git a/src/main/java/net/minecraft/world/entity/decoration/Painting.java b/src/main/java/net/minecraft/world/entity/decoration/Painting.java -index 0f4ef103afcbabc04880c8fc3547b861341c15fc..d5784a19cec98eb199a51acd9e1f4de8c6bf7265 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/Painting.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/Painting.java -@@ -121,7 +121,7 @@ public class Painting extends HangingEntity implements VariantHolder holder = loadVariant(nbt).orElseGet(Painting::getDefaultVariant); -+ Holder holder = loadVariant(nbt).orElseGet(() -> (Holder.Reference) getDefaultVariant()); // Purpur - decompile error TODO: still needed? - this.setVariant(holder); - this.direction = Direction.from2DDataValue(nbt.getByte("facing")); - super.readAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index c1abeb62f63d2f8fb891efec8f76c6736b8f7f75..7815af9b64ead32d6f7bcad6f86b2d21ec4aec22 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -182,7 +182,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - - @Override - public Brain getBrain() { -- return super.getBrain(); -+ return (Brain) super.getBrain(); // Purpur - decompile error - } - - protected void updateActivity() { -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index d02ee11066fc4f07ccb110b09b86d895ff90d4f2..e1be4a77fae0b9120781f460079269b85c993930 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -70,7 +70,7 @@ public class PiglinBrute extends AbstractPiglin { - - @Override - public Brain getBrain() { -- return super.getBrain(); -+ return (Brain) super.getBrain(); // Purpur - decompile error - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/Blocks.java b/src/main/java/net/minecraft/world/level/block/Blocks.java -index 877035b6c6593a28f475b9c5bcd7727e3fcdb802..0453397c157c8c7968947445f41bc46b68b111e8 100644 ---- a/src/main/java/net/minecraft/world/level/block/Blocks.java -+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java -@@ -1159,7 +1159,7 @@ public class Blocks { - } - - private static Boolean ocelotOrParrot(BlockState state, BlockGetter world, BlockPos pos, EntityType type) { -- return (boolean)type == EntityType.OCELOT || type == EntityType.PARROT; -+ return type == EntityType.OCELOT || type == EntityType.PARROT; // Purpur - decompile error - } - - private static BedBlock bed(DyeColor color) { diff --git a/patches/server/0007-Component-related-conveniences.patch b/patches/server/0007-Component-related-conveniences.patch deleted file mode 100644 index a2f21c219..000000000 --- a/patches/server/0007-Component-related-conveniences.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 29 Jun 2021 21:37:40 -0500 -Subject: [PATCH] Component related conveniences - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0c3da427151d9a2ce1ca69acff283a153bd758f7..944866d42dbb4732f669c8c697934fdf0c212978 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1780,6 +1780,26 @@ public class ServerPlayer extends Player { - this.lastSentExp = -1; // CraftBukkit - Added to reset - } - -+ // Purpur start -+ public void sendActionBarMessage(@Nullable String message) { -+ if (message != null && !message.isEmpty()) { -+ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); -+ } -+ } -+ -+ public void sendActionBarMessage(@Nullable net.kyori.adventure.text.Component message) { -+ if (message != null) { -+ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); -+ } -+ } -+ -+ public void sendActionBarMessage(@Nullable Component message) { -+ if (message != null) { -+ displayClientMessage(message, true); -+ } -+ } -+ // Purpur end -+ - @Override - public void displayClientMessage(Component message, boolean overlay) { - this.sendSystemMessage(message, overlay); -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index f097ec5b4e3ad6b1a7c464a8cff4f8b2568fcf4f..78636bf26ac8efaffd8a8f4b5bbc703d6b670b7c 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1045,6 +1045,20 @@ public abstract class PlayerList { - } - // CraftBukkit end - -+ // Purpur Start -+ public void broadcastMiniMessage(@Nullable String message, boolean overlay) { -+ if (message != null && !message.isEmpty()) { -+ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay); -+ } -+ } -+ -+ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) { -+ if (message != null) { -+ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay); -+ } -+ } -+ // Purpur end -+ - public void broadcastAll(Packet packet, ResourceKey dimension) { - Iterator iterator = this.players.iterator(); - -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index 25a5a3b949a0eb632611355e74ccd4865be108ca..14fcfd7c1d3a62833978e163f4e0d6f9b2203042 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -126,6 +126,15 @@ public class DamageSource { - } - } - -+ // Purpur start -+ public Component getLocalizedDeathMessage(String str, LivingEntity entity) { -+ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName()); -+ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name); -+ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template); -+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component); -+ } -+ // Purpur end -+ - public String getMsgId() { - return this.type().msgId(); - } -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 36795968f4c296f680f79cc5a795391ae13c64e4..6951b47abb5d36cdd9fe200152a93155c31aedaf 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4047,6 +4047,20 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return SlotAccess.NULL; - } - -+ // Purpur Start -+ public void sendMiniMessage(@Nullable String message) { -+ if (message != null && !message.isEmpty()) { -+ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); -+ } -+ } -+ -+ public void sendMessage(@Nullable net.kyori.adventure.text.Component message) { -+ if (message != null) { -+ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); -+ } -+ } -+ // Purpur end -+ - @Override - public void sendSystemMessage(Component message) {} - diff --git a/patches/server/0008-Ridables.patch b/patches/server/0008-Ridables.patch deleted file mode 100644 index 9b58442a4..000000000 --- a/patches/server/0008-Ridables.patch +++ /dev/null @@ -1,6696 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 5 Jul 2020 22:19:49 -0500 -Subject: [PATCH] Ridables - - -diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java -index 83cab746d1d6fe25c043c8aee28c39412b90c127..ec6b58dae525c81bbb1c0e2d96fbded6f00a45b5 100644 ---- a/src/main/java/net/minecraft/core/BlockPos.java -+++ b/src/main/java/net/minecraft/core/BlockPos.java -@@ -48,6 +48,12 @@ public class BlockPos extends Vec3i { - private static final int X_OFFSET = 38; - // Paper end - -+ // Purpur start -+ public BlockPos(net.minecraft.world.entity.Entity entity) { -+ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()); -+ } -+ // Purpur end -+ - public BlockPos(int x, int y, int z) { - super(x, y, z); - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 94f95539b22f1673022d25fd5558b85ea3fa80cb..81eca1ab647ee7cfd5ab6ffa2538a451964774ed 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1540,6 +1540,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper -+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - - this.profiler.push(() -> { - return worldserver + " " + worldserver.dimension().location(); -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index dbb59969cf55eda997588f4c3ef7dc899ea619bb..4c3b5a26a6b04afff3a707929ced3c62b5256a67 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -223,6 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - public boolean hasPhysicsEvent = true; // Paper - public boolean hasEntityMoveEvent = false; // Paper - private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) -+ public boolean hasRidableMoveEvent = false; // Purpur - public static Throwable getAddToWorldStackTrace(Entity entity) { - final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date()); - io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr); -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index bf88c86deaf677a10f2f8b67fe48a199e625fea9..3aff5f410220c2f3dee7acfa4339b9b5d0a5e7dd 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -752,6 +752,15 @@ public class ServerPlayer extends Player { - this.trackStartFallingPosition(); - this.trackEnteredOrExitedLavaOnVehicle(); - this.advancements.flushDirty(this); -+ -+ // Purpur start -+ if (this.level().purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level().getGameTime() % 100 == 0) { // 5 seconds -+ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION); -+ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds -+ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds -+ } -+ } -+ // Purpur end - } - - public void doTick() { -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 63eac8b250b8bae40de432a0ffd4792122cbae74..e936fa6e638a7ff4b087ecb4247c467be6fe9c57 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2870,6 +2870,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); - -+ player.processClick(enumhand); // Purpur -+ - // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a - if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) { - entity.getEntityData().resendPossiblyDesyncedEntity(player); // Paper - The entire mob gets deleted, so resend it. -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6951b47abb5d36cdd9fe200152a93155c31aedaf..14624fc143c9553930f181f7db93caecf24c0495 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -376,7 +376,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - private final Set tags; - private final double[] pistonDeltas; - private long pistonDeltasGameTime; -- private EntityDimensions dimensions; -+ protected EntityDimensions dimensions; // Purpur - private -> protected - private float eyeHeight; - public boolean isInPowderSnow; - public boolean wasInPowderSnow; -@@ -2942,6 +2942,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.passengers = ImmutableList.copyOf(list); - } - -+ // Purpur start -+ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) { -+ onMount(player); -+ this.rider = player; -+ } -+ // Purpur end -+ - this.gameEvent(GameEvent.ENTITY_MOUNT, passenger); - } - } -@@ -2982,6 +2989,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return false; - } - // Spigot end -+ -+ // Purpur start -+ if (this.rider != null && this.passengers.get(0) == this.rider) { -+ onDismount(this.rider); -+ this.rider = null; -+ } -+ // Purpur end -+ - if (this.passengers.size() == 1 && this.passengers.get(0) == entity) { - this.passengers = ImmutableList.of(); - } else { -@@ -4883,4 +4898,45 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return ((net.minecraft.server.level.ServerChunkCache) level.getChunkSource()).isPositionTicking(this); - } - // Paper end -+ -+ // Purpur start -+ @Nullable -+ private Player rider = null; -+ -+ @Nullable -+ public Player getRider() { -+ return rider; -+ } -+ -+ public boolean isRidable() { -+ return false; -+ } -+ -+ public boolean isControllable() { -+ return true; -+ } -+ -+ public void onMount(Player rider) { -+ if (this instanceof Mob) { -+ ((Mob) this).setTarget(null, null, false); -+ ((Mob) this).getNavigation().stop(); -+ } -+ rider.setJumping(false); // fixes jump on mount -+ } -+ -+ public void onDismount(Player player) { -+ } -+ -+ public boolean onSpacebar() { -+ return false; -+ } -+ -+ public boolean onClick(InteractionHand hand) { -+ return false; -+ } -+ -+ public boolean processClick(InteractionHand hand) { -+ return false; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index 759713f7c646aaf1a918c87a2834a1d405385dad..c6a06e07f0b4bb29b5f4c70dfa53ff6db2e4e6ea 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -23,6 +23,19 @@ public class GlowSquid extends Squid { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.glowSquidRidable; -+ } -+ -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.glowSquidControllable; -+ } -+ // Purpur end -+ - @Override - protected ParticleOptions getInkParticle() { - return ParticleTypes.GLOW_SQUID_INK; -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index e9a31314424d9db911cd9806741222397c3072d7..3c2892b9b2ade76d9d4b081ad5bce4991df11dda 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -218,9 +218,9 @@ public abstract class LivingEntity extends Entity implements Attackable { - protected int deathScore; - public float lastHurt; - public boolean jumping; -- public float xxa; -- public float yya; -- public float zza; -+ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER -+ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER -+ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER - protected int lerpSteps; - protected double lerpX; - protected double lerpY; -@@ -286,7 +286,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.effectsDirty = true; - this.useItem = ItemStack.EMPTY; - this.lastClimbablePos = Optional.empty(); -- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type)); -+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit - // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor - this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue()); -@@ -337,6 +337,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - public static AttributeSupplier.Builder createLivingAttributes() { - return AttributeSupplier.builder().add(Attributes.MAX_HEALTH).add(Attributes.KNOCKBACK_RESISTANCE).add(Attributes.MOVEMENT_SPEED).add(Attributes.ARMOR).add(Attributes.ARMOR_TOUGHNESS); - } -+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur - - @Override - protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { -@@ -2686,7 +2687,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - protected long lastJumpTime = 0L; // Paper -- protected void jumpFromGround() { -+ public void jumpFromGround() { // Purpur - protected -> public - Vec3 vec3d = this.getDeltaMovement(); - // Paper start - long time = System.nanoTime(); -@@ -3458,8 +3459,10 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.pushEntities(); - this.level().getProfiler().pop(); - // Paper start -- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) { -- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { -+ // Purpur start -+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { -+ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) { -+ // Purpur end - Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO); - Location to = new Location (this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); - io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone()); -@@ -3469,6 +3472,21 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); - } - } -+ // Purpur start -+ if (getRider() != null) { -+ getRider().resetLastActionTime(); -+ if (((ServerLevel) level()).hasRidableMoveEvent && this instanceof Mob) { -+ Location from = new Location(level().getWorld(), xo, yo, zo, this.yRotO, this.xRotO); -+ Location to = new Location(level().getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot()); -+ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (Player) getRider().getBukkitEntity(), from, to.clone()); -+ if (!event.callEvent()) { -+ absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch()); -+ } else if (!to.equals(event.getTo())) { -+ absMoveTo(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch()); -+ } -+ } -+ } -+ // Purpur end - } - // Paper end - if (!this.level().isClientSide && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) { -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index f6eb032897c6d5d16ab5c8c287e49e189c24571c..1f4c41521c9d6fd781f96c7d9552c8e55bbf347b 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -145,8 +145,8 @@ public abstract class Mob extends LivingEntity implements Targeting { - this.restrictRadius = -1.0F; - this.goalSelector = new GoalSelector(world.getProfilerSupplier()); - this.targetSelector = new GoalSelector(world.getProfilerSupplier()); -- this.lookControl = new LookControl(this); -- this.moveControl = new MoveControl(this); -+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur -+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur - this.jumpControl = new JumpControl(this); - this.bodyRotationControl = this.createBodyControl(); - this.navigation = this.createNavigation(world); -@@ -1381,7 +1381,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - protected void onOffspringSpawnedFromEgg(Player player, Mob child) {} - - protected InteractionResult mobInteract(Player player, InteractionHand hand) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - - public boolean isWithinRestriction() { -@@ -1759,4 +1759,56 @@ public abstract class Mob extends LivingEntity implements Targeting { - - return itemmonsteregg == null ? null : new ItemStack(itemmonsteregg); - } -+ -+ // Purpur start -+ public double getMaxY() { -+ return level().getHeight(); -+ } -+ -+ public InteractionResult tryRide(Player player, InteractionHand hand) { -+ return tryRide(player, hand, InteractionResult.PASS); -+ } -+ -+ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) { -+ if (!isRidable()) { -+ return result; -+ } -+ if (hand != InteractionHand.MAIN_HAND) { -+ return InteractionResult.PASS; -+ } -+ if (player.isShiftKeyDown()) { -+ return InteractionResult.PASS; -+ } -+ if (!player.getItemInHand(hand).isEmpty()) { -+ return InteractionResult.PASS; -+ } -+ if (!passengers.isEmpty() || player.isPassenger()) { -+ return InteractionResult.PASS; -+ } -+ if (this instanceof TamableAnimal tamable) { -+ if (tamable.isTame() && !tamable.isOwnedBy(player)) { -+ return InteractionResult.PASS; -+ } -+ if (!tamable.isTame() && !level().purpurConfig.untamedTamablesAreRidable) { -+ return InteractionResult.PASS; -+ } -+ } -+ if (this instanceof AgeableMob ageable) { -+ if (ageable.isBaby() && !level().purpurConfig.babiesAreRidable) { -+ return InteractionResult.PASS; -+ } -+ } -+ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) { -+ player.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob); -+ return InteractionResult.PASS; -+ } -+ player.setYRot(this.getYRot()); -+ player.setXRot(this.getXRot()); -+ if (player.startRiding(this)) { -+ return InteractionResult.SUCCESS; -+ } else { -+ return InteractionResult.PASS; -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index e283eb57c25f7de222f9d09dca851169f5f6e488..210a0bee1227e4671909dd553ab22027cfc868fb 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -24,14 +24,21 @@ public class AttributeMap { - private final Set dirtyAttributes = Sets.newHashSet(); - private final AttributeSupplier supplier; - private final java.util.function.Function createInstance; // Pufferfish -+ private final net.minecraft.world.entity.LivingEntity entity; // Purpur - - public AttributeMap(AttributeSupplier defaultAttributes) { -+ // Purpur start -+ this(defaultAttributes, null); -+ } -+ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) { -+ this.entity = entity; -+ // Purpur end - this.supplier = defaultAttributes; - this.createInstance = attribute -> this.supplier.createInstance(this::onAttributeModified, attribute); // Pufferfish - } - - private void onAttributeModified(AttributeInstance instance) { -- if (instance.getAttribute().isClientSyncable()) { -+ if (instance.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute()))) { // Purpur - this.dirtyAttributes.add(instance); - } - -@@ -43,7 +50,7 @@ public class AttributeMap { - - public Collection getSyncableAttributes() { - return this.attributes.values().stream().filter((attribute) -> { -- return attribute.getAttribute().isClientSyncable(); -+ return attribute.getAttribute().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute())); // Purpur - }).collect(Collectors.toList()); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java -index 8a720f9ae81d7ea856e28cb27a66adcf04bcb0eb..e0b70d9732a2b7d96999b7e4a497ffa1d8cf86a7 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java -@@ -80,7 +80,88 @@ import org.slf4j.Logger; - - public class DefaultAttributes { - private static final Logger LOGGER = LogUtils.getLogger(); -- private static final Map, AttributeSupplier> SUPPLIERS = ImmutableMap., AttributeSupplier>builder().put(EntityType.ALLAY, Allay.createAttributes().build()).put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build()).put(EntityType.AXOLOTL, Axolotl.createAttributes().build()).put(EntityType.BAT, Bat.createAttributes().build()).put(EntityType.BEE, Bee.createAttributes().build()).put(EntityType.BLAZE, Blaze.createAttributes().build()).put(EntityType.CAT, Cat.createAttributes().build()).put(EntityType.CAMEL, Camel.createAttributes().build()).put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build()).put(EntityType.CHICKEN, Chicken.createAttributes().build()).put(EntityType.COD, AbstractFish.createAttributes().build()).put(EntityType.COW, Cow.createAttributes().build()).put(EntityType.CREEPER, Creeper.createAttributes().build()).put(EntityType.DOLPHIN, Dolphin.createAttributes().build()).put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.DROWNED, Zombie.createAttributes().build()).put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build()).put(EntityType.ENDERMAN, EnderMan.createAttributes().build()).put(EntityType.ENDERMITE, Endermite.createAttributes().build()).put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build()).put(EntityType.EVOKER, Evoker.createAttributes().build()).put(EntityType.FOX, Fox.createAttributes().build()).put(EntityType.FROG, Frog.createAttributes().build()).put(EntityType.GHAST, Ghast.createAttributes().build()).put(EntityType.GIANT, Giant.createAttributes().build()).put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build()).put(EntityType.GOAT, Goat.createAttributes().build()).put(EntityType.GUARDIAN, Guardian.createAttributes().build()).put(EntityType.HOGLIN, Hoglin.createAttributes().build()).put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build()).put(EntityType.HUSK, Zombie.createAttributes().build()).put(EntityType.ILLUSIONER, Illusioner.createAttributes().build()).put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build()).put(EntityType.LLAMA, Llama.createAttributes().build()).put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build()).put(EntityType.MOOSHROOM, Cow.createAttributes().build()).put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build()).put(EntityType.OCELOT, Ocelot.createAttributes().build()).put(EntityType.PANDA, Panda.createAttributes().build()).put(EntityType.PARROT, Parrot.createAttributes().build()).put(EntityType.PHANTOM, Monster.createMonsterAttributes().build()).put(EntityType.PIG, Pig.createAttributes().build()).put(EntityType.PIGLIN, Piglin.createAttributes().build()).put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build()).put(EntityType.PILLAGER, Pillager.createAttributes().build()).put(EntityType.PLAYER, Player.createAttributes().build()).put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build()).put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build()).put(EntityType.RABBIT, Rabbit.createAttributes().build()).put(EntityType.RAVAGER, Ravager.createAttributes().build()).put(EntityType.SALMON, AbstractFish.createAttributes().build()).put(EntityType.SHEEP, Sheep.createAttributes().build()).put(EntityType.SHULKER, Shulker.createAttributes().build()).put(EntityType.SILVERFISH, Silverfish.createAttributes().build()).put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build()).put(EntityType.SLIME, Monster.createMonsterAttributes().build()).put(EntityType.SNIFFER, Sniffer.createAttributes().build()).put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build()).put(EntityType.SPIDER, Spider.createAttributes().build()).put(EntityType.SQUID, Squid.createAttributes().build()).put(EntityType.STRAY, AbstractSkeleton.createAttributes().build()).put(EntityType.STRIDER, Strider.createAttributes().build()).put(EntityType.TADPOLE, Tadpole.createAttributes().build()).put(EntityType.TRADER_LLAMA, Llama.createAttributes().build()).put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build()).put(EntityType.TURTLE, Turtle.createAttributes().build()).put(EntityType.VEX, Vex.createAttributes().build()).put(EntityType.VILLAGER, Villager.createAttributes().build()).put(EntityType.VINDICATOR, Vindicator.createAttributes().build()).put(EntityType.WARDEN, Warden.createAttributes().build()).put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build()).put(EntityType.WITCH, Witch.createAttributes().build()).put(EntityType.WITHER, WitherBoss.createAttributes().build()).put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build()).put(EntityType.WOLF, Wolf.createAttributes().build()).put(EntityType.ZOGLIN, Zoglin.createAttributes().build()).put(EntityType.ZOMBIE, Zombie.createAttributes().build()).put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build()).put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build()).put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build(); -+ private static final Map, AttributeSupplier> SUPPLIERS = ImmutableMap., AttributeSupplier>builder() -+ .put(EntityType.ALLAY, Allay.createAttributes().build()) -+ .put(EntityType.ARMOR_STAND, LivingEntity.createLivingAttributes().build()) -+ .put(EntityType.AXOLOTL, Axolotl.createAttributes().build()) -+ .put(EntityType.BAT, Bat.createAttributes().build()) -+ .put(EntityType.BEE, Bee.createAttributes().build()) -+ .put(EntityType.BLAZE, Blaze.createAttributes().build()) -+ .put(EntityType.CAT, Cat.createAttributes().build()) -+ .put(EntityType.CAMEL, Camel.createAttributes().build()) -+ .put(EntityType.CAVE_SPIDER, CaveSpider.createCaveSpider().build()) -+ .put(EntityType.CHICKEN, Chicken.createAttributes().build()) -+ .put(EntityType.COD, AbstractFish.createAttributes().build()) -+ .put(EntityType.COW, Cow.createAttributes().build()) -+ .put(EntityType.CREEPER, Creeper.createAttributes().build()) -+ .put(EntityType.DOLPHIN, Dolphin.createAttributes().build()) -+ .put(EntityType.DONKEY, AbstractChestedHorse.createBaseChestedHorseAttributes().build()) -+ .put(EntityType.DROWNED, Zombie.createAttributes().build()) -+ .put(EntityType.ELDER_GUARDIAN, ElderGuardian.createAttributes().build()) -+ .put(EntityType.ENDERMAN, EnderMan.createAttributes().build()) -+ .put(EntityType.ENDERMITE, Endermite.createAttributes().build()) -+ .put(EntityType.ENDER_DRAGON, EnderDragon.createAttributes().build()) -+ .put(EntityType.EVOKER, Evoker.createAttributes().build()) -+ .put(EntityType.FOX, Fox.createAttributes().build()) -+ .put(EntityType.FROG, Frog.createAttributes().build()) -+ .put(EntityType.GHAST, Ghast.createAttributes().build()) -+ .put(EntityType.GIANT, Giant.createAttributes().build()) -+ .put(EntityType.GLOW_SQUID, GlowSquid.createAttributes().build()) -+ .put(EntityType.GOAT, Goat.createAttributes().build()) -+ .put(EntityType.GUARDIAN, Guardian.createAttributes().build()) -+ .put(EntityType.HOGLIN, Hoglin.createAttributes().build()) -+ .put(EntityType.HORSE, AbstractHorse.createBaseHorseAttributes().build()) -+ .put(EntityType.HUSK, Zombie.createAttributes().build()) -+ .put(EntityType.ILLUSIONER, Illusioner.createAttributes().build()) -+ .put(EntityType.IRON_GOLEM, IronGolem.createAttributes().build()) -+ .put(EntityType.LLAMA, Llama.createAttributes().build()) -+ .put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build()) -+ .put(EntityType.MOOSHROOM, Cow.createAttributes().build()) -+ .put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build()) -+ .put(EntityType.OCELOT, Ocelot.createAttributes().build()) -+ .put(EntityType.PANDA, Panda.createAttributes().build()) -+ .put(EntityType.PARROT, Parrot.createAttributes().build()) -+ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur -+ .put(EntityType.PIG, Pig.createAttributes().build()) -+ .put(EntityType.PIGLIN, Piglin.createAttributes().build()) -+ .put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build()) -+ .put(EntityType.PILLAGER, Pillager.createAttributes().build()) -+ .put(EntityType.PLAYER, Player.createAttributes().build()) -+ .put(EntityType.POLAR_BEAR, PolarBear.createAttributes().build()) -+ .put(EntityType.PUFFERFISH, AbstractFish.createAttributes().build()) -+ .put(EntityType.RABBIT, Rabbit.createAttributes().build()) -+ .put(EntityType.RAVAGER, Ravager.createAttributes().build()) -+ .put(EntityType.SALMON, AbstractFish.createAttributes().build()) -+ .put(EntityType.SHEEP, Sheep.createAttributes().build()) -+ .put(EntityType.SHULKER, Shulker.createAttributes().build()) -+ .put(EntityType.SILVERFISH, Silverfish.createAttributes().build()) -+ .put(EntityType.SKELETON, AbstractSkeleton.createAttributes().build()) -+ .put(EntityType.SKELETON_HORSE, SkeletonHorse.createAttributes().build()) -+ .put(EntityType.SLIME, Monster.createMonsterAttributes().build()) -+ .put(EntityType.SNIFFER, Sniffer.createAttributes().build()) -+ .put(EntityType.SNOW_GOLEM, SnowGolem.createAttributes().build()) -+ .put(EntityType.SPIDER, Spider.createAttributes().build()) -+ .put(EntityType.SQUID, Squid.createAttributes().build()) -+ .put(EntityType.STRAY, AbstractSkeleton.createAttributes().build()) -+ .put(EntityType.STRIDER, Strider.createAttributes().build()) -+ .put(EntityType.TADPOLE, Tadpole.createAttributes().build()) -+ .put(EntityType.TRADER_LLAMA, Llama.createAttributes().build()) -+ .put(EntityType.TROPICAL_FISH, AbstractFish.createAttributes().build()) -+ .put(EntityType.TURTLE, Turtle.createAttributes().build()) -+ .put(EntityType.VEX, Vex.createAttributes().build()) -+ .put(EntityType.VILLAGER, Villager.createAttributes().build()) -+ .put(EntityType.VINDICATOR, Vindicator.createAttributes().build()) -+ .put(EntityType.WARDEN, Warden.createAttributes().build()) -+ .put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build()) -+ .put(EntityType.WITCH, Witch.createAttributes().build()) -+ .put(EntityType.WITHER, WitherBoss.createAttributes().build()) -+ .put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build()) -+ .put(EntityType.WOLF, Wolf.createAttributes().build()) -+ .put(EntityType.ZOGLIN, Zoglin.createAttributes().build()) -+ .put(EntityType.ZOMBIE, Zombie.createAttributes().build()) -+ .put(EntityType.ZOMBIE_HORSE, ZombieHorse.createAttributes().build()) -+ .put(EntityType.ZOMBIE_VILLAGER, Zombie.createAttributes().build()) -+ .put(EntityType.ZOMBIFIED_PIGLIN, ZombifiedPiglin.createAttributes().build()).build(); - - public static AttributeSupplier getSupplier(EntityType type) { - return SUPPLIERS.get(type); -diff --git a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java -index 03d77c6d4697d4934ca65e3329982f0efe089820..ce7e3e90993b76225dc820a21e04267e488f5f7b 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java -+++ b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java -@@ -29,6 +29,20 @@ public class MoveControl implements Control { - this.mob = entity; - } - -+ // Purpur start -+ public void setSpeedModifier(double speed) { -+ this.speedModifier = speed; -+ } -+ -+ public void setForward(float forward) { -+ this.strafeForwards = forward; -+ } -+ -+ public void setStrafe(float strafe) { -+ this.strafeRight = strafe; -+ } -+ // Purpur end -+ - public boolean hasWanted() { - return this.operation == MoveControl.Operation.MOVE_TO; - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java -index 7df56705a4a0de2dc4ff7ab133fc26612c219162..384bed4505b6cabb1ae151cd2c4eb5e56f24563f 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java -+++ b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java -@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control; - import net.minecraft.util.Mth; - import net.minecraft.world.entity.Mob; - --public class SmoothSwimmingLookControl extends LookControl { -+public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - private final int maxYRotFromCenter; - private static final int HEAD_TILT_X = 10; - private static final int HEAD_TILT_Y = 20; -@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (this.lookAtCooldown > 0) { - --this.lookAtCooldown; - this.getYRotD().ifPresent((yaw) -> { -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index 2c91fe46355c9a201507de5577f693ed4f5fb974..e184d2a89a89d4bf77a32a2d610175c5bbd38a03 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -18,6 +18,7 @@ import net.minecraft.world.entity.EntityDimensions; - import net.minecraft.world.entity.EntityType; - import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.MobSpawnType; -+import net.minecraft.world.entity.MoverType; - import net.minecraft.world.entity.Pose; - import net.minecraft.world.entity.ai.attributes.AttributeSupplier; - import net.minecraft.world.entity.ai.attributes.Attributes; -@@ -43,12 +44,59 @@ public class Bat extends AmbientCreature { - - public Bat(EntityType type, Level world) { - super(type, world); -+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur - if (!world.isClientSide) { - this.setResting(true); - } - - } - -+ // Purpur start -+ @Override -+ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED; } // Fixes log spam on clients -+ -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.batRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.batRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.batControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.batMaxY; -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ if (isResting()) { -+ setResting(false); -+ level().levelEvent(null, 1025, new BlockPos(this).above(), 0); -+ } -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(MoverType.SELF, mot.multiply(speed, 0.25, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ // Purpur end -+ - @Override - public boolean isFlapping() { - return !this.isResting() && this.tickCount % Bat.TICKS_PER_FLAP == 0; -@@ -98,7 +146,7 @@ public class Bat extends AmbientCreature { - protected void pushEntities() {} - - public static AttributeSupplier.Builder createAttributes() { -- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D); -+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - } - - public boolean isResting() { -@@ -130,6 +178,14 @@ public class Bat extends AmbientCreature { - - @Override - protected void customServerAiStep() { -+ // Purpur start -+ if (getRider() != null && this.isControllable()) { -+ Vec3 mot = getDeltaMovement(); -+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z()); -+ return; -+ } -+ // Purpur end -+ - super.customServerAiStep(); - BlockPos blockposition = this.blockPosition(); - BlockPos blockposition1 = blockposition.above(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java -index 2249fc6dd98afb8d52623b5864955fdd3b3fc042..2ccfaab0a02cf5ff9779e250fb79a75a9852e10d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java -@@ -94,7 +94,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - @Override - protected void registerGoals() { - super.registerGoals(); -- this.goalSelector.addGoal(0, new PanicGoal(this, 1.25D)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6D, 1.4D, EntitySelector.NO_SPECTATORS::test)); - this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this)); - } -@@ -107,7 +107,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - @Override - public void travel(Vec3 movementInput) { - if (this.isEffectiveAi() && this.isInWater()) { -- this.moveRelative(0.01F, movementInput); -+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur - this.move(MoverType.SELF, this.getDeltaMovement()); - this.setDeltaMovement(this.getDeltaMovement().scale(0.9D)); - if (this.getTarget() == null) { -@@ -166,7 +166,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - protected void playStepSound(BlockPos pos, BlockState state) { - } - -- static class FishMoveControl extends MoveControl { -+ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur - private final AbstractFish fish; - - FishMoveControl(AbstractFish owner) { -@@ -174,14 +174,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - this.fish = owner; - } - -+ // Purpur start - @Override -- public void tick() { -+ public void purpurTick(Player rider) { -+ super.purpurTick(rider); -+ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); -+ } -+ // Purpur end -+ -+ @Override -+ public void vanillaTick() { // Purpur - if (this.fish.isEyeInFluid(FluidTags.WATER)) { - this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); - } - - if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) { -- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f)); - double d = this.wantedX - this.fish.getX(); - double e = this.wantedY - this.fish.getY(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index 55026e1731e41b4e3e4c6a8fef5d96a32051a556..706ae64b894709601dccfb621d3c215f073e98e9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -43,6 +43,7 @@ import net.minecraft.world.entity.EntityType; - import net.minecraft.world.entity.LivingEntity; - import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.MobType; -+import net.minecraft.world.entity.MoverType; - import net.minecraft.world.entity.NeutralMob; - import net.minecraft.world.entity.PathfinderMob; - import net.minecraft.world.entity.Pose; -@@ -147,6 +148,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - public Bee(EntityType type, Level world) { - super(type, world); - this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60); -+ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur - // Paper start - apply gravity to bees when they get stuck in the void, fixes MC-167279 - class BeeFlyingMoveControl extends FlyingMoveControl { - public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) { -@@ -155,11 +157,24 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - - @Override - public void tick() { -+ // Purpur start -+ if (mob.getRider() != null && mob.isControllable()) { -+ flyingController.purpurTick(mob.getRider()); -+ return; -+ } -+ // Purpur end - if (this.mob.getY() <= Bee.this.level().getMinBuildHeight()) { - this.mob.setNoGravity(false); - } - super.tick(); - } -+ -+ // Purpur start -+ @Override -+ public boolean hasWanted() { -+ return mob.getRider() != null || !mob.isControllable() || super.hasWanted(); -+ } -+ // Purpur end - } - this.moveControl = new BeeFlyingMoveControl(this, 20, true); - // Paper end -@@ -171,6 +186,40 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.setPathfindingMalus(BlockPathTypes.FENCE, -1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.beeRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.beeRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.beeControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.beeMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(MoverType.SELF, mot.multiply(speed, speed, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -@@ -185,6 +234,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.399999976158142D, true)); - this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal()); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); -@@ -200,6 +250,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal()); - this.goalSelector.addGoal(8, new Bee.BeeWanderGoal()); - this.goalSelector.addGoal(9, new FloatGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new Bee.BeeHurtByOtherGoal(this)).setAlertOthers(new Class[0])); - this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this)); - this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); -@@ -881,16 +932,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeLookControl extends LookControl { -+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - - BeeLookControl(Mob entity) { - super(entity); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (!Bee.this.isAngry()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 90ce201bc7c47cef9bc59d7b535a7453854bac75..90ec526b135a6de6e7ba16fdd3a113ef92b42ef6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -97,6 +97,31 @@ public class Cat extends TamableAnimal implements VariantHolder { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.catRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.catRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.catControllable; -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ setInSittingPose(false); -+ setLying(false); -+ setRelaxStateOne(false); -+ } -+ // Purpur end -+ - public ResourceLocation getResourceLocation() { - return this.getVariant().texture(); - } -@@ -105,6 +130,7 @@ public class Cat extends TamableAnimal implements VariantHolder { - protected void registerGoals() { - this.temptGoal = new Cat.CatTemptGoal(this, 0.6D, Cat.TEMPT_INGREDIENT, true); - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 1.5D)); - this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); - this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this)); -@@ -117,6 +143,7 @@ public class Cat extends TamableAnimal implements VariantHolder { - this.goalSelector.addGoal(10, new BreedGoal(this, 0.8D)); - this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F)); - this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0F)); -+ this.targetSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, (Predicate) null)); - this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); - } -@@ -373,6 +400,7 @@ public class Cat extends TamableAnimal implements VariantHolder { - - @Override - public InteractionResult mobInteract(Player player, InteractionHand hand) { -+ if (getRider() != null) return InteractionResult.PASS; // Purpur - ItemStack itemstack = player.getItemInHand(hand); - Item item = itemstack.getItem(); - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index 84cefbc339ca368ac306d521012ce2f826a660b5..600b7b4e510871de0dee4051158d344734f04966 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -54,9 +54,27 @@ public class Chicken extends Animal { - this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.chickenRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.chickenRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.chickenControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, Chicken.FOOD_ITEMS, false)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index 824e5e4fe7619ae46061c3c978c9a044db8c84ab..e2a98b45e56a368de19bb65e304370a5998c7cb9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.codRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.codControllable; -+ } -+ // Purpur end -+ - @Override - public ItemStack getBucketItemStack() { - return new ItemStack(Items.COD_BUCKET); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 0263acf81330ada1bb8ae7d52a3b04e08d0c4dcb..11082cf272b72123d8917baa9a0d0e5367dca66b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -42,9 +42,27 @@ public class Cow extends Animal { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.cowRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.cowRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.cowControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT), false)); -@@ -85,6 +103,7 @@ public class Cow extends Animal { - - @Override - public InteractionResult mobInteract(Player player, InteractionHand hand) { -+ if (getRider() != null) return InteractionResult.PASS; // Purpur - ItemStack itemstack = player.getItemInHand(hand); - - if (itemstack.is(Items.BUCKET) && !this.isBaby()) { -@@ -92,7 +111,7 @@ public class Cow extends Animal { - PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) player.level(), player, this.blockPosition(), this.blockPosition(), null, itemstack, Items.MILK_BUCKET, hand); - - if (event.isCancelled()) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - // CraftBukkit end - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index 8448c5d778998390cf2b683f36e4e18ca7ffdc34..3dab78e2f39855f71bb7294521be71b3faa59b81 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -82,14 +82,82 @@ public class Dolphin extends WaterAnimal { - public static final Predicate ALLOWED_ITEMS = (entityitem) -> { - return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater(); - }; -+ private int spitCooldown; // Purpur - - public Dolphin(EntityType type, Level world) { - super(type, world); -- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); -+ // Purpur start -+ class DolphinMoveControl extends SmoothSwimmingMoveControl { -+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD; -+ private final Dolphin dolphin; -+ -+ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) { -+ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant); -+ this.dolphin = dolphin; -+ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin); -+ } -+ -+ @Override -+ public void tick() { -+ if (dolphin.getRider() != null && dolphin.isControllable()) { -+ purpurTick(dolphin.getRider()); -+ } else { -+ super.tick(); -+ } -+ } -+ -+ public void purpurTick(Player rider) { -+ if (dolphin.getAirSupply() < 150) { -+ // if drowning override player WASD controls to find air -+ super.tick(); -+ } else { -+ waterMoveControllerWASD.purpurTick(rider); -+ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); -+ } -+ } -+ }; -+ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true); -+ // Purpur end - this.lookControl = new SmoothSwimmingLookControl(this, 10); - this.setCanPickUpLoot(true); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.dolphinRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.dolphinControllable; -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (spitCooldown == 0 && getRider() != null) { -+ spitCooldown = level().purpurConfig.dolphinSpitCooldown; -+ -+ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity(); -+ if (!player.hasPermission("allow.special.dolphin")) { -+ return false; -+ } -+ -+ org.bukkit.Location loc = player.getEyeLocation(); -+ loc.setPitch(loc.getPitch() - 10); -+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector()); -+ -+ org.purpurmc.purpur.entity.DolphinSpit spit = new org.purpurmc.purpur.entity.DolphinSpit(level(), this); -+ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level().purpurConfig.dolphinSpitSpeed, 5.0F); -+ -+ level().addFreshEntity(spit); -+ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F); -+ return true; -+ } -+ return false; -+ } -+ // Purpur end -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -@@ -164,6 +232,7 @@ public class Dolphin extends WaterAnimal { - protected void registerGoals() { - this.goalSelector.addGoal(0, new BreathAirGoal(this)); - this.goalSelector.addGoal(0, new TryFindWaterGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this)); - this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D)); - this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10)); -@@ -174,6 +243,7 @@ public class Dolphin extends WaterAnimal { - this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal()); - this.goalSelector.addGoal(8, new FollowBoatGoal(this)); - this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers()); - } - -@@ -225,7 +295,7 @@ public class Dolphin extends WaterAnimal { - - @Override - protected boolean canRide(Entity entity) { -- return true; -+ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs; - } - - @Override -@@ -260,6 +330,11 @@ public class Dolphin extends WaterAnimal { - @Override - public void tick() { - super.tick(); -+ // Purpur start -+ if (spitCooldown > 0) { -+ spitCooldown--; -+ } -+ // Purpur end - if (this.isNoAi()) { - this.setAirSupply(this.getMaxAirSupply()); - } else { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index 62604ed2e82afd3603ccac5d9c6e285c431542b1..e0a21a0e8ff29f5b634fcb312881a1a93c6c3a44 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -141,6 +141,44 @@ public class Fox extends Animal implements VariantHolder { - this.setCanPickUpLoot(true); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.foxRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.foxRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.foxControllable; -+ } -+ -+ @Override -+ public float getJumpPower() { -+ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower(); -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ setCanPickUpLoot(false); -+ clearStates(); -+ setIsPouncing(false); -+ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND)); -+ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); -+ } -+ -+ @Override -+ public void onDismount(Player rider) { -+ super.onDismount(rider); -+ setCanPickUpLoot(true); -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -@@ -160,6 +198,7 @@ public class Fox extends Animal implements VariantHolder { - return entityliving instanceof AbstractSchoolingFish; - }); - this.goalSelector.addGoal(0, new Fox.FoxFloatGoal()); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level())); - this.goalSelector.addGoal(1, new Fox.FaceplantGoal()); - this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2D)); -@@ -186,6 +225,7 @@ public class Fox extends Animal implements VariantHolder { - this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal()); - this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F)); - this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal()); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving) -> { - return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID()); - })); -@@ -758,16 +798,16 @@ public class Fox extends Animal implements VariantHolder { - return new Vec3(0.0D, (double) (0.55F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F)); - } - -- public class FoxLookControl extends LookControl { -+ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - - public FoxLookControl() { - super(Fox.this); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (!Fox.this.isSleeping()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - } - - } -@@ -778,16 +818,16 @@ public class Fox extends Animal implements VariantHolder { - } - } - -- private class FoxMoveControl extends MoveControl { -+ private class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - - public FoxMoveControl() { - super(Fox.this); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (Fox.this.canMove()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index f383928fc5b331ddf128bdcb6a23010d8fe088d3..6815d7350a82c6d32f60aa6116466ebd06a920f9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -66,8 +66,27 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - this.setMaxUpStep(1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.ironGolemRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ironGolemRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.ironGolemControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true)); - this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F)); - this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false)); -@@ -75,6 +94,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - this.goalSelector.addGoal(5, new OfferFlowerGoal(this)); - this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this)); - this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0])); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); -@@ -270,13 +290,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - ItemStack itemstack = player.getItemInHand(hand); - - if (!itemstack.is(Items.IRON_INGOT)) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } else { - float f = this.getHealth(); - - this.heal(25.0F); - if (this.getHealth() == f) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } else { - float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F; - -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index a04374f91f2fbb31219d86b6ae63bcf8fdf7318c..7b0e3b46e3d318ee856d53cfc5532f3432447438 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -63,6 +63,23 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder> optional = this.getEffectFromItemStack(itemstack); - - if (!optional.isPresent()) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - - Pair pair = (Pair) optional.get(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -index 924ea172cab5178e5754bfe09cc7b83c1a66faa6..c25549d516072bdb362f78efce3da730e08f3ccc 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -@@ -68,6 +68,23 @@ public class Ocelot extends Animal { - this.reassessTrustingGoals(); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.ocelotRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ocelotRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.ocelotControllable; -+ } -+ // Purpur end -+ - public boolean isTrusting() { - return (Boolean) this.entityData.get(Ocelot.DATA_TRUSTING); - } -@@ -99,12 +116,14 @@ public class Ocelot extends Animal { - protected void registerGoals() { - this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6D, Ocelot.TEMPT_INGREDIENT, true); - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(3, this.temptGoal); - this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F)); - this.goalSelector.addGoal(8, new OcelotAttackGoal(this)); - this.goalSelector.addGoal(9, new BreedGoal(this, 0.8D)); - this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8D, 1.0000001E-5F)); - this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false)); - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR)); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java -index f43c4fed59c4c75540008284ddb197d9a6b5487b..cc12b93b5c95202c4e3417e3a35e5f5c361b9767 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Panda.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java -@@ -111,6 +111,32 @@ public class Panda extends Animal { - - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.pandaRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pandaRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.pandaControllable; -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ setForwardMot(0.0F); -+ sit(false); -+ eat(false); -+ setOnBack(false); -+ } -+ // Purpur end -+ - @Override - public boolean canTakeItem(ItemStack stack) { - EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(stack); -@@ -272,6 +298,7 @@ public class Panda extends Animal { - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0D)); - this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true)); -@@ -287,6 +314,7 @@ public class Panda extends Animal { - this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this)); - this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25D)); - this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0D)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0])); - } - -@@ -633,7 +661,7 @@ public class Panda extends Animal { - ItemStack itemstack = player.getItemInHand(hand); - - if (this.isScared()) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } else if (this.isOnBack()) { - this.setOnBack(false); - return InteractionResult.sidedSuccess(this.level().isClientSide); -@@ -650,7 +678,7 @@ public class Panda extends Animal { - this.setInLove(player); - } else { - if (this.level().isClientSide || this.isSitting() || this.isInWater()) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - - this.tryToSit(); -@@ -669,7 +697,7 @@ public class Panda extends Animal { - - return InteractionResult.SUCCESS; - } else { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - } - -@@ -709,7 +737,7 @@ public class Panda extends Animal { - return !this.isOnBack() && !this.isScared() && !this.isEating() && !this.isRolling() && !this.isSitting(); - } - -- private static class PandaMoveControl extends MoveControl { -+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - - private final Panda panda; - -@@ -719,9 +747,9 @@ public class Panda extends Animal { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (this.panda.canPerformAction()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - } - } - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -index e1874aaa0065a6e8e6ae043713c22f08b58b5e21..dc6bc1ecb11a8d1776be488de4df7243d196f330 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -@@ -129,12 +129,68 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder type, Level world) { - super(type, world); -- this.moveControl = new FlyingMoveControl(this, 10, false); -+ // Purpur start -+ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); -+ class ParrotMoveControl extends FlyingMoveControl { -+ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) { -+ super(entity, maxPitchChange, noGravity); -+ } -+ -+ @Override -+ public void tick() { -+ if (mob.getRider() != null && mob.isControllable()) { -+ flyingController.purpurTick(mob.getRider()); -+ } else { -+ super.tick(); -+ } -+ } -+ -+ @Override -+ public boolean hasWanted() { -+ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted(); -+ } -+ } -+ this.moveControl = new ParrotMoveControl(this, 10, false); -+ // Purpur end - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F); - this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, -1.0F); - this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.parrotRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.parrotRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.parrotControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.parrotMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ // Purpur end -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -@@ -153,8 +209,10 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder type, Level world) { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.polarBearRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.polarBearRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.polarBearControllable; -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (!isStanding()) { -+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) { -+ setStanding(true); -+ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F); -+ } -+ } -+ return false; -+ } -+ // Purpur end -+ - @Nullable - @Override - public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) { -@@ -80,12 +109,14 @@ public class PolarBear extends Animal implements NeutralMob { - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal()); - this.goalSelector.addGoal(1, new PolarBear.PolarBearPanicGoal()); - this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D)); - this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal()); - this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal()); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); -@@ -202,6 +233,11 @@ public class PolarBear extends Animal implements NeutralMob { - this.updatePersistentAnger((ServerLevel)this.level(), true); - } - -+ // Purpur start -+ if (isStanding() && --standTimer <= 0) { -+ setStanding(false); -+ } -+ // Purpur end - } - - @Override -@@ -231,6 +267,7 @@ public class PolarBear extends Animal implements NeutralMob { - - public void setStanding(boolean warning) { - this.entityData.set(DATA_STANDING_ID, warning); -+ standTimer = warning ? 20 : -1; // Purpur - } - - public float getStandingAnimationScale(float tickDelta) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java -index a197337f2e09f53cf382022569c8836745d78769..95383e246a8e1b311e4f26a66372966b6bc51de5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java -@@ -45,6 +45,18 @@ public class Pufferfish extends AbstractFish { - this.refreshDimensions(); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.pufferfishRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.pufferfishControllable; -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -index 082351bd5f98d8738334b9164375f63fdc890455..8e747771fe023dfa4cf61e5166d017368ed746b0 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -86,6 +86,7 @@ public class Rabbit extends Animal implements VariantHolder { - private boolean wasOnGround; - private int jumpDelayTicks; - public int moreCarrotTicks; -+ private boolean actualJump; // Purpur - - public Rabbit(EntityType type, Level world) { - super(type, world); -@@ -93,9 +94,55 @@ public class Rabbit extends Animal implements VariantHolder { - this.moveControl = new Rabbit.RabbitMoveControl(this); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.rabbitRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.rabbitRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.rabbitControllable; -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (onGround) { -+ actualJump = true; -+ jumpFromGround(); -+ actualJump = false; -+ } -+ return true; -+ } -+ -+ private void handleJumping() { -+ if (onGround) { -+ RabbitJumpControl jumpController = (RabbitJumpControl) jumpControl; -+ if (!wasOnGround) { -+ setJumping(false); -+ jumpController.setCanJump(false); -+ } -+ if (!jumpController.wantJump()) { -+ if (moveControl.hasWanted()) { -+ startJumping(); -+ } -+ } else if (!jumpController.canJump()) { -+ jumpController.setCanJump(true); -+ } -+ } -+ wasOnGround = onGround; -+ } -+ // Purpur end -+ - @Override - public void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); - this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 0.8D)); -@@ -110,6 +157,14 @@ public class Rabbit extends Animal implements VariantHolder { - - @Override - protected float getJumpPower() { -+ // Purpur start -+ if (getRider() != null && this.isControllable()) { -+ if (getForwardMot() < 0) { -+ setSpeed(getForwardMot() * 2F); -+ } -+ return actualJump ? 0.5F : 0.3F; -+ } -+ // Purpur end - float f = 0.3F; - - if (this.horizontalCollision || this.moveControl.hasWanted() && this.moveControl.getWantedY() > this.getY() + 0.5D) { -@@ -134,7 +189,7 @@ public class Rabbit extends Animal implements VariantHolder { - } - - @Override -- protected void jumpFromGround() { -+ public void jumpFromGround() { // Purpur - protected -> public - super.jumpFromGround(); - double d0 = this.moveControl.getSpeedModifier(); - -@@ -184,6 +239,13 @@ public class Rabbit extends Animal implements VariantHolder { - - @Override - public void customServerAiStep() { -+ // Purpur start -+ if (getRider() != null && this.isControllable()) { -+ handleJumping(); -+ return; -+ } -+ // Purpur end -+ - if (this.jumpDelayTicks > 0) { - --this.jumpDelayTicks; - } -@@ -468,7 +530,7 @@ public class Rabbit extends Animal implements VariantHolder { - } - } - -- private static class RabbitMoveControl extends MoveControl { -+ private static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - - private final Rabbit rabbit; - private double nextJumpSpeed; -@@ -479,14 +541,14 @@ public class Rabbit extends Animal implements VariantHolder { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (this.rabbit.onGround() && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl) this.rabbit.jumpControl).wantJump()) { - this.rabbit.setSpeedModifier(0.0D); - } else if (this.hasWanted()) { - this.rabbit.setSpeedModifier(this.nextJumpSpeed); - } - -- super.tick(); -+ super.vanillaTick(); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 0af79daa357f53a8871e293b57e16c099e5d3f64..87c442fb198cad8671ad1419e589a5a67c4fdca8 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -13,6 +13,18 @@ public class Salmon extends AbstractSchoolingFish { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.salmonRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.salmonControllable; -+ } -+ // Purpur end -+ - @Override - public int getMaxSchoolSize() { - return 5; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index c0e89262c596fbdd0bb3c3f76baccb17a1bb5fcd..0e2f10fa133281477ab96ba743266ade4d5f2119 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -117,10 +117,28 @@ public class Sheep extends Animal implements Shearable { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.sheepRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.sheepRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.sheepControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.eatBlockGoal = new EatBlockGoal(this); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 1.25D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.1D, Ingredient.of(Items.WHEAT), false)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index 8adcfc8f6772a32b5915e4a07100e8eb735f907a..04927f5f06d6dc14ad01319dd22583632c2c511b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -54,12 +54,31 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.snowGolemRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snowGolemRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.snowGolemControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25D, 20, 10.0F)); - this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F)); - this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entityliving) -> { - return entityliving instanceof Enemy; - })); -@@ -107,6 +126,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - return; - } - -+ if (getRider() != null && this.isControllable() && !level().purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden - BlockState iblockdata = Blocks.SNOW.defaultBlockState(); - - for (int i = 0; i < 4; ++i) { -@@ -154,7 +174,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - if (itemstack.is(Items.SHEARS) && this.readyForShearing()) { - // CraftBukkit start - if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - // CraftBukkit end - this.shear(SoundSource.PLAYERS); -@@ -167,7 +187,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - - return InteractionResult.sidedSuccess(this.level().isClientSide); - } else { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index f60c4cd0543fd5d50fa7e2c1a9e8381227adb540..7fa03dbc442942b70f374096e346d54089d8f0a0 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -48,9 +48,32 @@ public class Squid extends WaterAnimal { - this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.squidRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.squidControllable; -+ } -+ -+ protected void rotateVectorAroundY(org.bukkit.util.Vector vector, double degrees) { -+ double rad = Math.toRadians(degrees); -+ double cos = Math.cos(rad); -+ double sine = Math.sin(rad); -+ double x = vector.getX(); -+ double z = vector.getZ(); -+ vector.setX(cos * x - sine * z); -+ vector.setZ(sine * x + cos * z); -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Squid.SquidFleeGoal()); - } - -@@ -298,6 +321,37 @@ public class Squid extends WaterAnimal { - - @Override - public void tick() { -+ // Purpur start -+ Player rider = squid.getRider(); -+ if (rider != null && squid.isControllable()) { -+ if (rider.jumping) { -+ squid.onSpacebar(); -+ } -+ float forward = rider.getForwardMot(); -+ float strafe = rider.getStrafeMot(); -+ float speed = (float) squid.getAttributeValue(Attributes.MOVEMENT_SPEED) * 10F; -+ if (forward < 0.0F) { -+ speed *= -0.5; -+ } -+ org.bukkit.util.Vector dir = rider.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(speed / 20.0F); -+ if (strafe != 0.0F) { -+ if (forward == 0.0F) { -+ dir.setY(0); -+ rotateVectorAroundY(dir, strafe > 0.0F ? -90 : 90); -+ } else if (forward < 0.0F) { -+ rotateVectorAroundY(dir, strafe > 0.0F ? 45 : -45); -+ } else { -+ rotateVectorAroundY(dir, strafe > 0.0F ? -45 : 45); -+ } -+ } -+ if (forward != 0.0F || strafe != 0.0F) { -+ squid.setMovementVector((float) dir.getX(), (float) dir.getY(), (float) dir.getZ()); -+ } else { -+ squid.setMovementVector(0.0F, 0.0F, 0.0F); -+ } -+ return; -+ } -+ // Purpur end - int i = this.squid.getNoActionTime(); - if (i > 100) { - this.squid.setMovementVector(0.0F, 0.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -index b05b560b7570e97bc234b75f26233909fcf575b3..71234b258157579d3a47064e7e299bb7fb90908f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -@@ -42,6 +42,18 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.tropicalFishRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.tropicalFishControllable; -+ } -+ // Purpur end -+ - public static String getPredefinedName(int variant) { - return "entity.minecraft.tropical_fish.predefined." + variant; - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index d3c15d029d5f003cba3c89f7ea1f3ed4f943f2bd..46b71439efdab39c28d29684913bec2a1ba6491a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -83,6 +83,23 @@ public class Turtle extends Animal { - this.setMaxUpStep(1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.turtleRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.turtleRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.turtleControllable; -+ } -+ // Purpur end -+ - public void setHomePos(BlockPos pos) { - this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... - } -@@ -185,6 +202,7 @@ public class Turtle extends Animal { - - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2D)); - this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0D)); - this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0D)); -@@ -342,13 +360,15 @@ public class Turtle extends Animal { - org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit - } - -- private static class TurtleMoveControl extends MoveControl { -+ private static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - - private final Turtle turtle; -+ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur - - TurtleMoveControl(Turtle turtle) { - super(turtle); - this.turtle = turtle; -+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur - } - - private void updateSpeed() { -@@ -367,8 +387,18 @@ public class Turtle extends Animal { - - } - -+ // Purpur start -+ public void purpurTick(Player rider) { -+ if (turtle.isInWater()) { -+ waterController.purpurTick(rider); -+ } else { -+ super.purpurTick(rider); -+ } -+ } -+ // Purpur end -+ - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - this.updateSpeed(); - if (this.operation == MoveControl.Operation.MOVE_TO && !this.turtle.getNavigation().isDone()) { - double d0 = this.wantedX - this.turtle.getX(); -@@ -384,7 +414,7 @@ public class Turtle extends Animal { - - this.turtle.setYRot(this.rotlerp(this.turtle.getYRot(), f, 90.0F)); - this.turtle.yBodyRot = this.turtle.getYRot(); -- float f1 = (float) (this.speedModifier * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float f1 = (float) (this.getSpeedModifier() * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED)); - - this.turtle.setSpeed(Mth.lerp(0.125F, this.turtle.getSpeed(), f1)); - this.turtle.setDeltaMovement(this.turtle.getDeltaMovement().add(0.0D, (double) this.turtle.getSpeed() * d1 * 0.1D, 0.0D)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index 57ceec70bb150afaa66962090b142048d5b50c2f..40d647d6b3132f8273d43a1addcb8c8116c7ec19 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -103,9 +103,32 @@ public class Wolf extends TamableAnimal implements NeutralMob { - this.setPathfindingMalus(BlockPathTypes.DANGER_POWDER_SNOW, -1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.wolfRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wolfRidableInWater; -+ } -+ -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ setInSittingPose(false); -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.wolfControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Wolf.WolfPanicGoal(1.5D)); - this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); - this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D)); -@@ -117,6 +140,7 @@ public class Wolf extends TamableAnimal implements NeutralMob { - this.goalSelector.addGoal(9, new BegGoal(this, 8.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(10, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this)); - this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); - this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); -diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 1a0eee3b766a5ce5623c32ed9c023a0f80db1d1a..6c2129b93dbeaeb1e8e8db58fce8670ef8ce5716 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -101,10 +101,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - private float spinningAnimationTicks; - private float spinningAnimationTicks0; - public boolean forceDancing = false; // CraftBukkit -+ private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur - - public Allay(EntityType type, Level world) { - super(type, world); -- this.moveControl = new FlyingMoveControl(this, 20, true); -+ // Purpur start -+ this.purpurController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.1F, 0.5F); -+ this.moveControl = new FlyingMoveControl(this, 20, true) { -+ @Override -+ public void tick() { -+ if (mob.getRider() != null && mob.isControllable()) { -+ purpurController.purpurTick(mob.getRider()); -+ } else { -+ super.tick(); -+ } -+ } -+ }; -+ // Purpur end - this.setCanPickUpLoot(this.canPickUpLoot()); - this.vibrationUser = new Allay.VibrationUser(); - this.vibrationData = new VibrationSystem.Data(); -@@ -118,6 +131,28 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - } - // CraftBukkit end - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.allayRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.allayRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.allayControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ } -+ // Purpur end -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES); -@@ -226,7 +261,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("allayBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("allayActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index 42e22a4b9cb6841de04862cc81454da3232aa65a..b76bead9c107889e9b2f11bdc24ad7da811f97ed 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -98,6 +98,23 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { - return this.modelRotationValues; -@@ -289,7 +306,7 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder { - public final AnimationState croakAnimationState = new AnimationState(); - public final AnimationState tongueAnimationState = new AnimationState(); - public final AnimationState swimIdleAnimationState = new AnimationState(); -+ private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur -+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - - public Frog(EntityType type, Level world) { - super(type, world); - this.lookControl = new Frog.FrogLookControl(this); - this.setPathfindingMalus(BlockPathTypes.WATER, 4.0F); - this.setPathfindingMalus(BlockPathTypes.TRAPDOOR, -1.0F); -- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); -+ // Purpur start -+ this.purpurLandController = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.2F); -+ this.purpurWaterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F); -+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) { -+ @Override -+ public void tick() { -+ net.minecraft.world.entity.player.Player rider = mob.getRider(); -+ if (rider != null && mob.isControllable()) { -+ if (mob.isInWater()) { -+ purpurWaterController.purpurTick(rider); -+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, -0.005D, 0.0D)); -+ } else { -+ purpurLandController.purpurTick(rider); -+ } -+ } else { -+ super.tick(); -+ } -+ } -+ }; -+ // Purpur end - this.setMaxUpStep(1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.frogRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.frogRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.frogControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ } -+ -+ @Override -+ public float getJumpPower() { -+ return (getRider() != null && isControllable()) ? level().purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower(); -+ } -+ // Purpur end -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -@@ -166,7 +215,7 @@ public class Frog extends Animal implements VariantHolder { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("frogBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("frogActivityUpdate"); -@@ -347,7 +396,7 @@ public class Frog extends Animal implements VariantHolder { - return world.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos); - } - -- class FrogLookControl extends LookControl { -+ class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - FrogLookControl(Mob entity) { - super(entity); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 6ed4ac06c76b8d0d6e8db778cade15dbd1e3e5f5..af8438ae8c805d3276ef2d82eb39b08880fcc8a1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -@@ -45,13 +45,50 @@ public class Tadpole extends AbstractFish { - protected static final ImmutableList>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS); - protected static final ImmutableList> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING); - public boolean ageLocked; // Paper -+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur - - public Tadpole(EntityType type, Level world) { - super(type, world); -- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); -+ // Purpur start -+ this.purpurController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F); -+ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) { -+ @Override -+ public void tick() { -+ Player rider = mob.getRider(); -+ if (rider != null && mob.isControllable()) { -+ purpurController.purpurTick(rider); -+ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.002D, 0.0D)); -+ } else { -+ super.tick(); -+ } -+ } -+ }; -+ // Purpur end - this.lookControl = new SmoothSwimmingLookControl(this, 10); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.tadpoleRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.tadpoleRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.tadpoleControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ } -+ // Purpur end -+ - @Override - protected PathNavigation createNavigation(Level world) { - return new WaterBoundPathNavigation(this, world); -@@ -81,7 +118,7 @@ public class Tadpole extends AbstractFish { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("tadpoleBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("tadpoleActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 7f21d1d400c8a5615ed1a787dcb0680338f8c28d..ec956339b02fc9da53563d67c0eeaa91a3c3a8ee 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -90,6 +90,23 @@ public class Goat extends Animal { - return InstrumentItem.create(Items.GOAT_HORN, (Holder) holderset.getRandomElement(randomsource).get()); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.goatRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.goatRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.goatControllable; -+ } -+ // Purpur end -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -@@ -193,7 +210,7 @@ public class Goat extends Animal { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("goatBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Uncomment when pufferfish patch - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goatActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -index 49d7109b6ca63c8073db777549a65b2fcb70967e..4b656f4e1660978e3dd284093241003cabff4f23 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -@@ -147,12 +147,22 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - - protected AbstractHorse(EntityType type, Level world) { - super(type, world); -+ this.moveControl = new net.minecraft.world.entity.ai.control.MoveControl(this); // Purpur - use vanilla controller -+ this.lookControl = new net.minecraft.world.entity.ai.control.LookControl(this); // Purpur - use vanilla controller - this.setMaxUpStep(1.0F); - this.createInventory(); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return false; // vanilla handles -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 1.2D)); - this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D, AbstractHorse.class)); -@@ -163,6 +173,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - if (this.canPerformRearing()) { - this.goalSelector.addGoal(9, new RandomStandGoal(this)); - } -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - - this.addBehaviourGoals(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -index e0dfee0e0ce091d5ae0ec740e939c2c50915c104..6a5fb18523545ccc788b09ab4fbee1b14a64bc63 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -@@ -15,6 +15,13 @@ public class Donkey extends AbstractChestedHorse { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater; -+ } -+ // Purpur end -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.DONKEY_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -index 5f5dc651d570989ec1294c31a14dcfede466b80a..3e50581033e88e8eddcbd85bfa890cbe0b88a7e6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -@@ -40,6 +40,13 @@ public class Horse extends AbstractHorse implements VariantHolder { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.horseRidableInWater; -+ } -+ // Purpur end -+ - @Override - protected void randomizeAttributes(RandomSource random) { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index d28089c37707f323f73e60cb0ebed4e3cdf8c0fd..42d5b435728aa497dc34f0ac984e8637fe96343d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -76,7 +76,51 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, Level world) { - super(type, world); -+ // Purpur start -+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this) { -+ @Override -+ public void tick() { -+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) { -+ purpurTick(entity.getRider()); -+ } else { -+ vanillaTick(); -+ } -+ } -+ }; -+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { -+ @Override -+ public void tick() { -+ if (entity.getRider() != null && entity.isControllable() && isSaddled()) { -+ purpurTick(entity.getRider()); -+ } else { -+ vanillaTick(); -+ } -+ } -+ }; -+ // Purpur end -+ } -+ -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.llamaRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.llamaRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.llamaControllable; -+ } -+ -+ @Override -+ public boolean isSaddled() { -+ return super.isSaddled() || (isTamed() && getSwag() != null); - } -+ // Purpur end - - public boolean isTraderLlama() { - return false; -@@ -127,6 +171,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder public - super.jumpFromGround(); - double d0 = this.moveControl.getSpeedModifier(); - -diff --git a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java -index a6f3fba3b02b0b4d2a4e9e5205301c6f52d0188a..4eebfc27ded55e4d764d04f35d3e9c9e0791c89f 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java -+++ b/src/main/java/net/minecraft/world/entity/boss/EnderDragonPart.java -@@ -24,6 +24,13 @@ public class EnderDragonPart extends Entity { - this.name = name; - } - -+ // Purpur start -+ @Override -+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { -+ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS; -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - } -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index a24ae93efcdb2da5782d342c7697a1bb253400c7..c5100d3d8c36bf0c07f2028c5d3ec4bbe6e92256 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -105,6 +105,7 @@ public class EnderDragon extends Mob implements Enemy { - @Nullable - private BlockPos podium; - // Paper end -+ private boolean hadRider; // Purpur - - public EnderDragon(EntityType entitytypes, Level world) { - super(EntityType.ENDER_DRAGON, world); -@@ -126,7 +127,37 @@ public class EnderDragon extends Mob implements Enemy { - this.noPhysics = true; - this.noCulling = true; - this.phaseManager = new EnderDragonPhaseManager(this); -- this.explosionSource = new Explosion(world, this, null, null, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, Explosion.BlockInteraction.DESTROY); // CraftBukkit -+ -+ // Purpur start -+ this.moveControl = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this) { -+ @Override -+ public void vanillaTick() { -+ // dragon doesn't use the controller. do nothing -+ } -+ }; -+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { -+ @Override -+ public void vanillaTick() { -+ // dragon doesn't use the controller. do nothing -+ } -+ -+ @Override -+ public void purpurTick(Player rider) { -+ setYawPitch(rider.getYRot() - 180F, rider.xRotO * 0.5F); -+ } -+ }; -+ // Purpur end -+ } -+ -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.enderDragonRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.enderDragonRidableInWater; - } - - public void setDragonFight(EndDragonFight fight) { -@@ -141,6 +172,17 @@ public class EnderDragon extends Mob implements Enemy { - return this.fightOrigin; - } - -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.enderDragonControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.enderDragonMaxY; -+ } -+ // Purpur end -+ - public static AttributeSupplier.Builder createAttributes() { - return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0D); - } -@@ -202,6 +244,37 @@ public class EnderDragon extends Mob implements Enemy { - - @Override - public void aiStep() { -+ // Purpur start -+ boolean hasRider = getRider() != null && this.isControllable(); -+ if (hasRider) { -+ if (!hadRider) { -+ hadRider = true; -+ noPhysics = false; -+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(4.0F, 2.0F); -+ } -+ -+ // dragon doesn't use controllers, so must tick manually -+ moveControl.tick(); -+ lookControl.tick(); -+ -+ moveRelative((float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F, new Vec3(-getStrafeMot(), getVerticalMot(), -getForwardMot())); -+ Vec3 mot = getDeltaMovement(); -+ setDeltaMovement(mot); -+ move(MoverType.PLAYER, mot); -+ -+ mot = mot.multiply(0.9F, 0.9F, 0.9F); -+ setDeltaMovement(mot); -+ -+ // control wing flap speed on client -+ phaseManager.setPhase(mot.x() * mot.x() + mot.z() * mot.z() < 0.005F ? EnderDragonPhase.HOVERING : EnderDragonPhase.HOLDING_PATTERN); -+ } else if (hadRider) { -+ hadRider = false; -+ noPhysics = true; -+ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(16.0F, 8.0F); -+ phaseManager.setPhase(EnderDragonPhase.HOLDING_PATTERN); // HoldingPattern -+ } -+ // Purpur end -+ - this.processFlappingMovement(); - if (this.level().isClientSide) { - this.setHealth(this.getHealth()); -@@ -228,6 +301,8 @@ public class EnderDragon extends Mob implements Enemy { - float f; - - if (this.isDeadOrDying()) { -+ if (hasRider) ejectPassengers(); // Purpur -+ - float f1 = (this.random.nextFloat() - 0.5F) * 8.0F; - - f = (this.random.nextFloat() - 0.5F) * 4.0F; -@@ -240,9 +315,9 @@ public class EnderDragon extends Mob implements Enemy { - - f = 0.2F / ((float) vec3d.horizontalDistance() * 10.0F + 1.0F); - f *= (float) Math.pow(2.0D, vec3d.y); -- if (this.phaseManager.getCurrentPhase().isSitting()) { -+ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur - this.flapTime += 0.1F; -- } else if (this.inWall) { -+ } else if (!hasRider && this.inWall) { // Purpur - this.flapTime += f * 0.5F; - } else { - this.flapTime += f; -@@ -287,7 +362,7 @@ public class EnderDragon extends Mob implements Enemy { - } - - this.phaseManager.getCurrentPhase().doClientTick(); -- } else { -+ } else if (!hasRider) { // Purpur - DragonPhaseInstance idragoncontroller = this.phaseManager.getCurrentPhase(); - - idragoncontroller.doServerTick(); -@@ -356,7 +431,7 @@ public class EnderDragon extends Mob implements Enemy { - this.tickPart(this.body, (double) (f11 * 0.5F), 0.0D, (double) (-f12 * 0.5F)); - this.tickPart(this.wing1, (double) (f12 * 4.5F), 2.0D, (double) (f11 * 4.5F)); - this.tickPart(this.wing2, (double) (f12 * -4.5F), 2.0D, (double) (f11 * -4.5F)); -- if (!this.level().isClientSide && this.hurtTime == 0) { -+ if (!hasRider && !this.level().isClientSide && this.hurtTime == 0) { // Purpur - this.knockBack(this.level().getEntities((Entity) this, this.wing1.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR)); - this.knockBack(this.level().getEntities((Entity) this, this.wing2.getBoundingBox().inflate(4.0D, 2.0D, 4.0D).move(0.0D, -2.0D, 0.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR)); - this.hurt(this.level().getEntities((Entity) this, this.head.getBoundingBox().inflate(1.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR)); -@@ -400,7 +475,7 @@ public class EnderDragon extends Mob implements Enemy { - } - - if (!this.level().isClientSide) { -- this.inWall = this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox()); -+ this.inWall = !hasRider && this.checkWalls(this.head.getBoundingBox()) | this.checkWalls(this.neck.getBoundingBox()) | this.checkWalls(this.body.getBoundingBox()); // Purpur - if (this.dragonFight != null) { - this.dragonFight.updateDragon(this); - } -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 1e07febcf7a3dfb281728cc5e3e4f15dd776d7e0..6c86411658ef0bf64cb8cf4f213112b65f2d1d90 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -84,16 +84,30 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - return entityliving.getMobType() != MobType.UNDEAD && entityliving.attackable(); - }; - private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR); -+ private int shootCooldown = 0; // Purpur - // Paper start - private boolean canPortal = false; - - public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; } - // Paper end -+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur - - public WitherBoss(EntityType type, Level world) { - super(type, world); - this.bossEvent = (ServerBossEvent) (new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS)).setDarkenScreen(true); -- this.moveControl = new FlyingMoveControl(this, 10, false); -+ // Purpur start -+ this.purpurController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.1F); -+ this.moveControl = new FlyingMoveControl(this, 10, false) { -+ @Override -+ public void tick() { -+ if (mob.getRider() != null && mob.isControllable()) { -+ purpurController.purpurTick(mob.getRider()); -+ } else { -+ super.tick(); -+ } -+ } -+ }; -+ // Purpur end - this.setHealth(this.getMaxHealth()); - this.xpReward = 50; - } -@@ -108,13 +122,113 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - return navigationflying; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.witherRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.witherControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.witherMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 5F; -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.5, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ this.entityData.set(DATA_TARGETS.get(0), 0); -+ this.entityData.set(DATA_TARGETS.get(1), 0); -+ this.entityData.set(DATA_TARGETS.get(2), 0); -+ getNavigation().stop(); -+ shootCooldown = 20; -+ } -+ -+ @Override -+ public boolean onClick(net.minecraft.world.InteractionHand hand) { -+ return shoot(getRider(), hand == net.minecraft.world.InteractionHand.MAIN_HAND ? new int[]{1} : new int[]{2}); -+ } -+ -+ public boolean shoot(@Nullable Player rider, int[] heads) { -+ if (shootCooldown > 0) { -+ return false; -+ } -+ -+ shootCooldown = 20; -+ if (rider == null) { -+ return false; -+ } -+ -+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = rider.getBukkitEntity(); -+ if (!player.hasPermission("allow.special.wither")) { -+ return false; -+ } -+ -+ net.minecraft.world.phys.HitResult rayTrace = getRayTrace(120, net.minecraft.world.level.ClipContext.Fluid.NONE); -+ if (rayTrace == null) { -+ return false; -+ } -+ -+ Vec3 loc; -+ if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) { -+ BlockPos pos = ((net.minecraft.world.phys.BlockHitResult) rayTrace).getBlockPos(); -+ loc = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D); -+ } else if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.ENTITY) { -+ Entity target = ((net.minecraft.world.phys.EntityHitResult) rayTrace).getEntity(); -+ loc = new Vec3(target.getX(), target.getY() + (target.getEyeHeight() / 2), target.getZ()); -+ } else { -+ org.bukkit.block.Block block = player.getTargetBlock(null, 120); -+ loc = new Vec3(block.getX() + 0.5D, block.getY() + 0.5D, block.getZ() + 0.5D); -+ } -+ -+ for (int head : heads) { -+ shoot(head, loc.x(), loc.y(), loc.z(), rider); -+ } -+ -+ return true; // handled -+ } -+ -+ public void shoot(int head, double x, double y, double z, Player rider) { -+ level().levelEvent(null, 1024, blockPosition(), 0); -+ double headX = getHeadX(head); -+ double headY = getHeadY(head); -+ double headZ = getHeadZ(head); -+ WitherSkull skull = new WitherSkull(level(), this, x - headX, y - headY, z - headZ); -+ skull.setPosRaw(headX, headY, headZ); -+ level().addFreshEntity(skull); -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal()); - this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 40, 20.0F)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0])); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, WitherBoss.LIVING_ENTITY_SELECTOR)); - } -@@ -256,6 +370,16 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - - @Override - protected void customServerAiStep() { -+ // Purpur start -+ if (getRider() != null && this.isControllable()) { -+ Vec3 mot = getDeltaMovement(); -+ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z()); -+ } -+ if (shootCooldown > 0) { -+ shootCooldown--; -+ } -+ // Purpur end -+ - int i; - - if (this.getInvulnerableTicks() > 0) { -@@ -576,11 +700,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - } - - public int getAlternativeTarget(int headIndex) { -- return (Integer) this.entityData.get((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex)); -+ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(WitherBoss.DATA_TARGETS.get(headIndex)); // Purpur - } - - public void setAlternativeTarget(int headIndex, int id) { -- this.entityData.set((EntityDataAccessor) WitherBoss.DATA_TARGETS.get(headIndex), id); -+ if (getRider() == null || !this.isControllable()) this.entityData.set(WitherBoss.DATA_TARGETS.get(headIndex), id); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -index 9ca1e9d95e62929c0015d5ca2c2f9c70e421842e..93443238b77c04f6a9da3c25427d200ccf4699f8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -70,12 +70,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(2, new RestrictSunGoal(this)); - this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0D, 1.2D)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0])); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Blaze.java b/src/main/java/net/minecraft/world/entity/monster/Blaze.java -index 17aa7676ab624440651850bbe5689f8a6c9dbeed..63049b0b4a4ae09e5925129dc363e43dbae23272 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java -@@ -32,6 +32,7 @@ public class Blaze extends Monster { - - public Blaze(EntityType type, Level world) { - super(type, world); -+ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur - this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); - this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F); - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F); -@@ -39,19 +40,55 @@ public class Blaze extends Monster { - this.xpReward = 10; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.blazeRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.blazeRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.blazeControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.blazeMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED); -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this)); - this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0D)); - this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F)); - this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers()); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); - } - - public static AttributeSupplier.Builder createAttributes() { -- return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D); -+ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, (double)0.23F).add(Attributes.FOLLOW_RANGE, 48.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - } - - @Override -@@ -106,6 +143,14 @@ public class Blaze extends Monster { - - @Override - protected void customServerAiStep() { -+ // Purpur start -+ if (getRider() != null && this.isControllable()) { -+ Vec3 mot = getDeltaMovement(); -+ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z()); -+ return; -+ } -+ // Purpur end -+ - --this.nextHeightOffsetChangeTick; - if (this.nextHeightOffsetChangeTick <= 0) { - this.nextHeightOffsetChangeTick = 100; -diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -index da48bd8e15463be8170262ae90a8e95575959bf2..34abd8093015d7cdaf2faca2396d3950ca0a361b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -@@ -28,6 +28,23 @@ public class CaveSpider extends Spider { - return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.caveSpiderRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.caveSpiderRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.caveSpiderControllable; -+ } -+ // Purpur end -+ - @Override - public boolean doHurtTarget(Entity target) { - if (super.doHurtTarget(target)) { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index fd1b5a1beea7594fa65decfdcccfa15781fc005b..66b2fdcda53fdfd278994dc16be3e12041be6e99 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -59,21 +59,98 @@ public class Creeper extends Monster implements PowerableMob { - public int maxSwell = 30; - public int explosionRadius = 3; - private int droppedSkulls; -+ // Purpur start -+ private int spacebarCharge = 0; -+ private int prevSpacebarCharge = 0; -+ private int powerToggleDelay = 0; -+ // Purpur end - - public Creeper(EntityType type, Level world) { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.creeperRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creeperRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.creeperControllable; -+ } -+ -+ @Override -+ protected void customServerAiStep() { -+ if (powerToggleDelay > 0) { -+ powerToggleDelay--; -+ } -+ if (getRider() != null && this.isControllable()) { -+ if (getRider().getForwardMot() != 0 || getRider().getStrafeMot() != 0) { -+ spacebarCharge = 0; -+ setIgnited(false); -+ setSwellDir(-1); -+ } -+ if (spacebarCharge == prevSpacebarCharge) { -+ spacebarCharge = 0; -+ } -+ prevSpacebarCharge = spacebarCharge; -+ } -+ super.customServerAiStep(); -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ setIgnited(false); -+ setSwellDir(-1); -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (powerToggleDelay > 0) { -+ return true; // just toggled power, do not jump or ignite -+ } -+ spacebarCharge++; -+ if (spacebarCharge > maxSwell - 2) { -+ spacebarCharge = 0; -+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.powered.creeper")) { -+ powerToggleDelay = 20; -+ setPowered(!isPowered()); -+ setIgnited(false); -+ setSwellDir(-1); -+ return true; -+ } -+ } -+ if (!isIgnited()) { -+ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0 && -+ getRider().getBukkitEntity().hasPermission("allow.special.creeper")) { -+ setIgnited(true); -+ setSwellDir(1); -+ return true; -+ } -+ } -+ return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); - this.goalSelector.addGoal(2, new SwellGoal(this)); -+ this.goalSelector.addGoal(3, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0D, 1.2D)); - this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0D, 1.2D)); - this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true)); - this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0])); - } -@@ -324,6 +401,7 @@ public class Creeper extends Monster implements PowerableMob { - com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited); - if (event.callEvent()) { - this.entityData.set(Creeper.DATA_IS_IGNITED, event.isIgnited()); -+ if (!event.isIgnited()) setSwellDir(-1); // Purpur - } - } - // Paper end -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 991d728db2a3b64316fc2102cf3aee470327a62e..c64c88ddd929d49fcfc6ebc2d0030bc86c8ac337 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -68,6 +68,23 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.groundNavigation = new GroundPathNavigation(this, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.drownedRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.drownedRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.drownedControllable; -+ } -+ // Purpur end -+ - @Override - protected void addBehaviourGoals() { - this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0D)); -@@ -259,8 +276,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.searchingForLand = targetingUnderwater; - } - -- private static class DrownedMoveControl extends MoveControl { -- -+ private static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - private final Drowned drowned; - - public DrownedMoveControl(Drowned drowned) { -@@ -269,7 +285,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - LivingEntity entityliving = this.drowned.getTarget(); - - if (this.drowned.wantsToSwim() && this.drowned.isInWater()) { -@@ -292,7 +308,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - - this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F)); - this.drowned.yBodyRot = this.drowned.getYRot(); -- float f1 = (float) (this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float f1 = (float) (this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1); - - this.drowned.setSpeed(f2); -@@ -302,7 +318,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0D, -0.008D, 0.0D)); - } - -- super.tick(); -+ super.vanillaTick(); // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -index 8f481e11815d7162dd62a2b850b3d2af6d904519..16486ece9fc415d875ff94d9b806b0b5884ebc11 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -@@ -33,6 +33,18 @@ public class ElderGuardian extends Guardian { - - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.elderGuardianRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.elderGuardianControllable; -+ } -+ // Purpur end -+ - public static AttributeSupplier.Builder createAttributes() { - return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 410f10ad93935d1c078447a4596023f367a8e9b7..dc359bb3a45a0b648933894e18d4ee6b51e3c7bc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -96,9 +96,27 @@ public class EnderMan extends Monster implements NeutralMob { - this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.endermanRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermanRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.endermanControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this)); - this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false)); - this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F)); -@@ -106,6 +124,7 @@ public class EnderMan extends Monster implements NeutralMob { - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); - this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this)); - this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt)); - this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0])); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false)); -@@ -287,7 +306,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - protected void customServerAiStep() { -- if (this.level().isDay() && this.tickCount >= this.targetChangeTime + 600) { -+ if ((getRider() == null || !this.isControllable()) && this.level().isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - no random teleporting - float f = this.getLightLevelDependentMagicValue(); - - if (f > 0.5F && this.level().canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper -@@ -408,6 +427,7 @@ public class EnderMan extends Monster implements NeutralMob { - public boolean hurt(DamageSource source, float amount) { - if (this.isInvulnerableTo(source)) { - return false; -+ } else if (getRider() != null && this.isControllable()) { return super.hurt(source, amount); // Purpur - no teleporting on damage - } else { - boolean flag = source.getDirectEntity() instanceof ThrownPotion; - boolean flag1; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -index 29b8771ec85f07fcd9d9ba041c9a25a212009b16..638665391f7552d054965f7e449b96ec6bceb03d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -37,14 +37,33 @@ public class Endermite extends Monster { - this.xpReward = 3; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.endermiteRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermiteRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.endermiteControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); - this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false)); - this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this)).setAlertOthers()); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -index a6dde4e31790eaecff27135a4036627192c85702..13e0fc756c6f572d0d688ae788e784eda6ce6d98 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -48,10 +48,28 @@ public class Evoker extends SpellcasterIllager { - this.xpReward = 10; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.evokerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.evokerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.evokerControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal()); - this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6D, 1.0D)); - this.goalSelector.addGoal(4, new Evoker.EvokerSummonSpellGoal()); -@@ -60,6 +78,7 @@ public class Evoker extends SpellcasterIllager { - this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D)); - this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers()); - this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300)); - this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -index 592b0dae251800552a0771ec46b4b8532b63075d..9f09ccd41320423179dd1cf81c2a7094b88a008b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -@@ -44,11 +44,47 @@ public class Ghast extends FlyingMob implements Enemy { - this.moveControl = new Ghast.GhastMoveControl(this); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.ghastRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ghastRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.ghastControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.ghastMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED); -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this)); - this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this)); - this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> { - return Math.abs(entityliving.getY() - this.getY()) <= 4.0D; - })); -@@ -103,7 +139,7 @@ public class Ghast extends FlyingMob implements Enemy { - } - - public static AttributeSupplier.Builder createAttributes() { -- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D); -+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - } - - @Override -@@ -160,7 +196,7 @@ public class Ghast extends FlyingMob implements Enemy { - return 2.6F; - } - -- private static class GhastMoveControl extends MoveControl { -+ private static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - - private final Ghast ghast; - private int floatDuration; -@@ -171,7 +207,7 @@ public class Ghast extends FlyingMob implements Enemy { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (this.operation == MoveControl.Operation.MOVE_TO) { - if (this.floatDuration-- <= 0) { - this.floatDuration += this.ghast.getRandom().nextInt(5) + 2; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java -index 41004c28edb748e12c4f868aa07b4672891197c1..33d9d90495efc8b80a8f948752becfb203ee8e63 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -14,6 +14,29 @@ public class Giant extends Monster { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.giantRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.giantRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.giantControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ } -+ // Purpur end -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return 10.440001F; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java -index 5e9b9e9f124ec6103b2e832ae94bc5b334a2c533..a893a8cb6f9aa79520d9a7814be459d48e3c6853 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java -@@ -69,15 +69,36 @@ public class Guardian extends Monster { - this.xpReward = 10; - this.setPathfindingMalus(BlockPathTypes.WATER, 0.0F); - this.moveControl = new Guardian.GuardianMoveControl(this); -+ // Purpur start -+ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { -+ @Override -+ public void setYawPitch(float yaw, float pitch) { -+ super.setYawPitch(yaw, pitch * 0.35F); -+ } -+ }; -+ // Purpur end - this.clientSideTailAnimation = this.random.nextFloat(); - this.clientSideTailAnimationO = this.clientSideTailAnimation; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.guardianRidable; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.guardianControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D); - - this.randomStrollGoal = new RandomStrollGoal(this, 1.0D, 80); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field - this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction); - this.goalSelector.addGoal(7, this.randomStrollGoal); -@@ -86,6 +107,7 @@ public class Guardian extends Monster { - this.goalSelector.addGoal(9, new RandomLookAroundGoal(this)); - this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); - pathfindergoalmovetowardsrestriction.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this))); - } - -@@ -351,7 +373,7 @@ public class Guardian extends Monster { - @Override - public void travel(Vec3 movementInput) { - if (this.isControlledByLocalInstance() && this.isInWater()) { -- this.moveRelative(0.1F, movementInput); -+ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, movementInput); // Purpur - this.move(MoverType.SELF, this.getDeltaMovement()); - this.setDeltaMovement(this.getDeltaMovement().scale(0.9D)); - if (!this.isMoving() && this.getTarget() == null) { -@@ -363,7 +385,7 @@ public class Guardian extends Monster { - - } - -- private static class GuardianMoveControl extends MoveControl { -+ private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur - - private final Guardian guardian; - -@@ -372,8 +394,17 @@ public class Guardian extends Monster { - this.guardian = guardian; - } - -+ // Purpur start - @Override -- public void tick() { -+ public void purpurTick(Player rider) { -+ super.purpurTick(rider); -+ guardian.setDeltaMovement(guardian.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); -+ guardian.setMoving(guardian.getForwardMot() > 0.0F); // control tail speed -+ } -+ // Purpur end -+ -+ @Override -+ public void vanillaTick() { // Purpur - if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) { - Vec3 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ()); - double d0 = vec3d.length(); -@@ -384,7 +415,7 @@ public class Guardian extends Monster { - - this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F)); - this.guardian.yBodyRot = this.guardian.getYRot(); -- float f1 = (float) (this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float f1 = (float) (this.getSpeedModifier() * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1); - - this.guardian.setSpeed(f2); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java -index efe8e6b895da1a81e5de42697fda58443cf15a59..5beed21038f88ebb4826b66013d0ae45a1f1ec34 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -22,6 +22,23 @@ public class Husk extends Zombie { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.huskRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.huskRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.huskControllable; -+ } -+ // Purpur end -+ - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -index 63fce7e3d9f59f36e29bc827a46396d73143bb8b..f83f09f7568c44a1165aac73f7127c5d1af0478f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -59,10 +59,28 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.illusionerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.illusionerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.illusionerControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal()); - this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal()); - this.goalSelector.addGoal(5, new Illusioner.IllusionerBlindnessSpellGoal()); -@@ -70,6 +88,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D)); - this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers()); - this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300)); - this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index 2858ea5562d06c11e5c7337a2b123f9be7a3f62e..aad51022eac584fbc058c2b25e2bf1929fccca6f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -25,6 +25,28 @@ public class MagmaCube extends Slime { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.magmaCubeRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.magmaCubeRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.magmaCubeControllable; -+ } -+ -+ @Override -+ public float getJumpPower() { -+ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving -+ } -+ // Purpur end -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F); - } -@@ -70,11 +92,12 @@ public class MagmaCube extends Slime { - } - - @Override -- protected void jumpFromGround() { -+ public void jumpFromGround() { // Purpur - protected -> public - Vec3 vec3 = this.getDeltaMovement(); - float f = (float)this.getSize() * 0.1F; - this.setDeltaMovement(vec3.x, (double)(this.getJumpPower() + f), vec3.z); - this.hasImpulse = true; -+ this.actualJump = false; // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 4a132c3eff6978e927bcd4df56b9ce0306af6d19..8c8ff7d291999cc8ab251a145db000871050236f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -60,6 +60,64 @@ public class Phantom extends FlyingMob implements Enemy { - this.lookControl = new Phantom.PhantomLookControl(this); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.phantomRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.phantomRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.phantomControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.phantomMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable() && !onGround) { -+ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED); -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ -+ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() { -+ return Monster.createMonsterAttributes().add(Attributes.FLYING_SPEED, 3.0D); -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) { -+ shoot(); -+ } -+ return false; -+ } -+ -+ public boolean shoot() { -+ org.bukkit.Location loc = ((org.bukkit.entity.LivingEntity) getBukkitEntity()).getEyeLocation(); -+ loc.setPitch(-loc.getPitch()); -+ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(100).add(loc.toVector()); -+ -+ org.purpurmc.purpur.entity.PhantomFlames flames = new org.purpurmc.purpur.entity.PhantomFlames(level(), this); -+ flames.canGrief = level().purpurConfig.phantomAllowGriefing; -+ flames.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), 1.0F, 5.0F); -+ level().addFreshEntity(flames); -+ return true; -+ } -+ // Purpur end -+ - @Override - public boolean isFlapping() { - return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0; -@@ -72,9 +130,11 @@ public class Phantom extends FlyingMob implements Enemy { - - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal()); - this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal()); - this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal()); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal()); - } - -@@ -145,6 +205,7 @@ public class Phantom extends FlyingMob implements Enemy { - @Override - public void aiStep() { - if (this.isAlive() && shouldBurnInDay && this.isSunBurnTick()) { // Paper - Configurable Burning -+ if (getRider() == null || !this.isControllable()) // Purpur - this.setSecondsOnFire(8); - } - -@@ -269,7 +330,7 @@ public class Phantom extends FlyingMob implements Enemy { - private AttackPhase() {} - } - -- private class PhantomMoveControl extends MoveControl { -+ private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - - private float speed = 0.1F; - -@@ -277,8 +338,19 @@ public class Phantom extends FlyingMob implements Enemy { - super(entity); - } - -+ // Purpur start -+ public void purpurTick(Player rider) { -+ if (!Phantom.this.onGround) { -+ // phantom is always in motion when flying -+ // TODO - FIX THIS -+ // rider.setForward(1.0F); -+ } -+ super.purpurTick(rider); -+ } -+ // Purpur end -+ - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (Phantom.this.horizontalCollision) { - Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F); - this.speed = 0.1F; -@@ -324,14 +396,20 @@ public class Phantom extends FlyingMob implements Enemy { - } - } - -- private class PhantomLookControl extends LookControl { -+ private class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - - public PhantomLookControl(Mob entity) { - super(entity); - } - -+ // Purpur start -+ public void purpurTick(Player rider) { -+ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F); -+ } -+ // Purpur end -+ - @Override -- public void tick() {} -+ public void vanillaTick() {} // Purpur - } - - private class PhantomBodyRotationControl extends BodyRotationControl { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -index cec545c3baa6599d47b9cf1a4b97de8771062a22..31d204d8d81ccc30371070af3678d82dc721618d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -62,15 +62,34 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.pillagerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pillagerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.pillagerControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F)); - this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0D, 8.0F)); - this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D)); - this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 15.0F, 1.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 15.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers()); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index e87c6c8ab585d6b0b38bbb8e42c0082e38f03250..8a99c74f1cb6b558421b11e8562ba6082492b2d2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -68,14 +68,39 @@ public class Ravager extends Raider { - this.setPathfindingMalus(BlockPathTypes.LEAVES, 0.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.ravagerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ravagerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.ravagerControllable; -+ } -+ -+ @Override -+ public void onMount(Player rider) { -+ super.onMount(rider); -+ getNavigation().stop(); -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(4, new Ravager.RavagerMeleeAttackGoal()); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(2, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers()); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)); - this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entityliving) -> { -@@ -153,7 +178,7 @@ public class Ravager extends Raider { - @Override - public void aiStep() { - super.aiStep(); -- if (this.isAlive()) { -+ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur - if (this.isImmobile()) { - this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0D); - } else { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index d2073de07bdad71b20e05046577b16649123967b..3e5548d0489b13b4a1d5c22c9d3bf19ead87928e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -98,12 +98,31 @@ public class Shulker extends AbstractGolem implements VariantHolder(this, Player.class, true)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -index 8b818a7cb835512c4bd2ea9641d4bfd904150332..4db19589d19d1a0e488ddd0b691e81152ac236e4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -@@ -26,6 +26,23 @@ public class Skeleton extends AbstractSkeleton { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.skeletonRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.skeletonRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.skeletonControllable; -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java -index 343433158507451152e5b2fc5e5fd0b0e6b229db..c74db4080ad3d726c4b5cb374e20e6df2a262d2f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Slime.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -64,6 +64,7 @@ public class Slime extends Mob implements Enemy { - public float squish; - public float oSquish; - private boolean wasOnGround; -+ protected boolean actualJump; // Purpur - - public Slime(EntityType type, Level world) { - super(type, world); -@@ -71,12 +72,48 @@ public class Slime extends Mob implements Enemy { - this.moveControl = new Slime.SlimeMoveControl(this); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.slimeRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.slimeRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.slimeControllable; -+ } -+ -+ @Override -+ public float getJumpPower() { -+ float height = super.getJumpPower(); -+ return getRider() != null && this.isControllable() && actualJump ? height * 1.5F : height; -+ } -+ -+ @Override -+ public boolean onSpacebar() { -+ if (onGround && getRider() != null && this.isControllable()) { -+ actualJump = true; -+ if (getRider().getForwardMot() == 0 || getRider().getStrafeMot() == 0) { -+ jumpFromGround(); // jump() here if not moving -+ } -+ } -+ return true; // do not jump() in wasd controller, let vanilla controller handle -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Slime.SlimeFloatGoal(this)); - this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this)); - this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this)); - this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> { - return Math.abs(entityliving.getY() - this.getY()) <= 4.0D; - })); -@@ -368,11 +405,12 @@ public class Slime extends Mob implements Enemy { - } - - @Override -- protected void jumpFromGround() { -+ public void jumpFromGround() { // Purpur - protected -> public - Vec3 vec3d = this.getDeltaMovement(); - - this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z); - this.hasImpulse = true; -+ this.actualJump = false; // Purpur - } - - @Nullable -@@ -406,7 +444,7 @@ public class Slime extends Mob implements Enemy { - return super.getDimensions(pose).scale(0.255F * (float) this.getSize()); - } - -- private static class SlimeMoveControl extends MoveControl { -+ private static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - - private float yRot; - private int jumpDelay; -@@ -425,21 +463,33 @@ public class Slime extends Mob implements Enemy { - } - - public void setWantedMovement(double speed) { -- this.speedModifier = speed; -+ this.setSpeedModifier(speed); // Purpur - this.operation = MoveControl.Operation.MOVE_TO; - } - - @Override - public void tick() { -+ // Purpur start -+ if (slime.getRider() != null && slime.isControllable()) { -+ purpurTick(slime.getRider()); -+ if (slime.getForwardMot() != 0 || slime.getStrafeMot() != 0) { -+ if (jumpDelay > 10) { -+ jumpDelay = 6; -+ } -+ } else { -+ jumpDelay = 20; -+ } -+ } else { -+ // Purpur end - this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F)); - this.mob.yHeadRot = this.mob.getYRot(); - this.mob.yBodyRot = this.mob.getYRot(); -- if (this.operation != MoveControl.Operation.MOVE_TO) { -+ } if ((slime.getRider() == null || !slime.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur - this.mob.setZza(0.0F); - } else { - this.operation = MoveControl.Operation.WAIT; - if (this.mob.onGround()) { -- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); -+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - if (this.jumpDelay-- <= 0) { - this.jumpDelay = this.slime.getJumpDelay(); - if (this.isAggressive) { -@@ -456,7 +506,7 @@ public class Slime extends Mob implements Enemy { - this.mob.setSpeed(0.0F); - } - } else { -- this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); -+ this.mob.setSpeed((float) (this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java -index 05432184077752b1d0cb764a5e39ed875748b2d6..9f6eaf76a39a9c3e7aed2e0ce91960200b4de664 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Spider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java -@@ -51,14 +51,33 @@ public class Spider extends Monster { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.spiderRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.spiderRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.spiderControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F)); - this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0])); - this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class)); - this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java -index 118b636a44e4b062e812e433f603b039276337da..137ee6d1aa8ec334009b6fab21b9f30303ace45f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Stray.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java -@@ -21,6 +21,23 @@ public class Stray extends AbstractSkeleton { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.strayRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.strayRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.strayControllable; -+ } -+ // Purpur end -+ - public static boolean checkStraySpawnRules(EntityType type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos blockPos = pos; - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index ebf54d6e36fdee2833250816fae14195ac45eb67..ccc76bf5431af723eef8e4d39c96daa44d1f6b5c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -100,6 +100,23 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.striderRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.striderRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.striderControllable; -+ } -+ // Purpur end -+ - public static boolean checkStriderSpawnRules(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); - -@@ -161,6 +178,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - @Override - protected void registerGoals() { - this.panicGoal = new PanicGoal(this, 1.65D); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, this.panicGoal); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.temptGoal = new TemptGoal(this, 1.4D, Strider.TEMPT_ITEMS, false); -@@ -470,7 +488,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - if (!enuminteractionresult.consumesAction()) { - ItemStack itemstack = player.getItemInHand(hand); - -- return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS; -+ return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : tryRide(player, hand); // Purpur - } else { - if (flag && !this.isSilent()) { - this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java -index 65cb385ab294e362d666a6d03c4496cdc3b64890..40f42c83d6417258123753ba9315ca27c2b43b22 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -63,6 +63,50 @@ public class Vex extends Monster implements TraceableEntity { - this.xpReward = 3; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.vexRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vexRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.vexControllable; -+ } -+ -+ @Override -+ public double getMaxY() { -+ return level().purpurConfig.vexMaxY; -+ } -+ -+ @Override -+ public void travel(Vec3 vec3) { -+ super.travel(vec3); -+ if (getRider() != null && this.isControllable()) { -+ float speed; -+ if (onGround) { -+ speed = (float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F; -+ } else { -+ speed = (float) getAttributeValue(Attributes.FLYING_SPEED); -+ } -+ setSpeed(speed); -+ Vec3 mot = getDeltaMovement(); -+ move(MoverType.SELF, mot.multiply(speed, 1.0, speed)); -+ setDeltaMovement(mot.scale(0.9D)); -+ } -+ } -+ -+ @Override -+ public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) { -+ return false; // no fall damage please -+ } -+ // Purpur end -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return dimensions.height - 0.28125F; -@@ -81,7 +125,7 @@ public class Vex extends Monster implements TraceableEntity { - - @Override - public void tick() { -- this.noPhysics = true; -+ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur - super.tick(); - this.noPhysics = false; - this.setNoGravity(true); -@@ -96,17 +140,19 @@ public class Vex extends Monster implements TraceableEntity { - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(4, new Vex.VexChargeAttackGoal()); - this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal()); - this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); - this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers()); - this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this)); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)); - } - - public static AttributeSupplier.Builder createAttributes() { -- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D); -+ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D).add(Attributes.FLYING_SPEED, 0.6D); // Purpur; - } - - @Override -@@ -235,14 +281,14 @@ public class Vex extends Monster implements TraceableEntity { - return 0.4D; - } - -- private class VexMoveControl extends MoveControl { -+ private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - - public VexMoveControl(Vex entityvex) { - super(entityvex); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - if (this.operation == MoveControl.Operation.MOVE_TO) { - Vec3 vec3d = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ()); - double d0 = vec3d.length(); -@@ -251,7 +297,7 @@ public class Vex extends Monster implements TraceableEntity { - this.operation = MoveControl.Operation.WAIT; - Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5D)); - } else { -- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.speedModifier * 0.05D / d0))); -+ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.getSpeedModifier() * 0.05D / d0))); // Purpur - if (Vex.this.getTarget() == null) { - Vec3 vec3d1 = Vex.this.getDeltaMovement(); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index 2acc531bd9e948251cac77d979f973678f576394..f8337259af80cede27e9a37c5634ccba9f837076 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -58,14 +58,33 @@ public class Vindicator extends AbstractIllager { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.vindicatorRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vindicatorRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.vindicatorControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Vindicator.VindicatorBreakDoorGoal(this)); - this.goalSelector.addGoal(2, new AbstractIllager.RaiderOpenDoorGoal(this)); - this.goalSelector.addGoal(3, new Raider.HoldGroundAttackGoal(this, 10.0F)); - this.goalSelector.addGoal(4, new Vindicator.VindicatorMeleeAttackGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, Raider.class)).setAlertOthers()); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index 701f6bf1d558cf0ec4bc1abb9e1f66d96ea5a6c9..c7ceeb28b53879395c697cbb3778563511bc1209 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -57,6 +57,23 @@ public class Witch extends Raider implements RangedAttackMob { - super(type, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.witchRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witchRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.witchControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - super.registerGoals(); -@@ -65,10 +82,12 @@ public class Witch extends Raider implements RangedAttackMob { - }); - this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, (Predicate) null); - this.goalSelector.addGoal(1, new FloatGoal(this)); -+ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 60, 10.0F)); - this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(3, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[]{Raider.class})); - this.targetSelector.addGoal(2, this.healRaidersGoal); - this.targetSelector.addGoal(3, this.attackPlayersGoal); -diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index 9a81cb0c3a5ac40ff50dc7c81f6196a447cd03c6..45c7d673cb15fc002538c2713e36f591fe33de92 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -35,6 +35,23 @@ public class WitherSkeleton extends AbstractSkeleton { - this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.witherSkeletonRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherSkeletonRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.witherSkeletonControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 7815af9b64ead32d6f7bcad6f86b2d21ec4aec22..393dde215894616c073efd28b529c9dac9815adc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -67,6 +67,23 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - this.xpReward = 5; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.zoglinRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zoglinRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.zoglinControllable; -+ } -+ // Purpur end -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -@@ -199,6 +216,7 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("zoglinBrain"); -+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - this.updateActivity(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 3f8c1d1d3c408fc4f15c4b5680bc22c86f104a9d..d3f1bcd9daeea282bef549ea18cedf0fc7a9ad8d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -106,11 +106,30 @@ public class Zombie extends Monster { - this(EntityType.ZOMBIE, world); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.zombieRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.zombieControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - if (this.level().paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0D, 3)); // Paper - this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); - this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.addBehaviourGoals(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 25ed5571b24e590bc95056020d84496492b53298..87ae149be7a3eaf14406a7df9f174574d77c63c5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -79,6 +79,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - }); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.zombieVillagerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieVillagerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.zombieVillagerControllable; -+ } -+ // Purpur end -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index aff140cfb2bbdce8b512080cf394c84c5c4ff807..fe9bdc4006b916748e55a976b6c8621070fb73c5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -63,6 +63,23 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.zombifiedPiglinRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombifiedPiglinRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.zombifiedPiglinControllable; -+ } -+ // Purpur end -+ - @Override - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index d6d61b91096d28eea1e5af69ea1c07890820ee7f..8ba7896a823d60065c1e293d0fcdb2d3ac76da77 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -67,6 +67,23 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - this.xpReward = 5; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.hoglinRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.hoglinRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.hoglinControllable; -+ } -+ // Purpur end -+ - @Override - public boolean canBeLeashed(Player player) { - return !this.isLeashed(); -@@ -130,7 +147,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("hoglinBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - HoglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index e235cc9d1b3ce59ab662ef3cf3ce0267ca78536d..c54f70f608093511f9ef1e7e904d9497a4710bdd 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -96,6 +96,23 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - this.xpReward = 5; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.piglinRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.piglinControllable; -+ } -+ // Purpur end -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -@@ -309,7 +326,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("piglinBrain"); -- if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish -+ if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - PiglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index e1be4a77fae0b9120781f460079269b85c993930..2d842c0dfce1c7e7229bd42b2a92c024a4162b68 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -41,6 +41,23 @@ public class PiglinBrute extends AbstractPiglin { - this.xpReward = 20; - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.piglinBruteRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinBruteRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.piglinBruteControllable; -+ } -+ // Purpur end -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D); - } -@@ -86,6 +103,7 @@ public class PiglinBrute extends AbstractPiglin { - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("piglinBruteBrain"); -+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - PiglinBruteAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 71db8bd6885377d55cfc571fccc21df6d8a57b54..0aa6874022d4ee8e38f2d85c45a2a4201c2a83fe 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -121,8 +121,32 @@ public class Warden extends Monster implements VibrationSystem { - this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F); - this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F); - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F); -+ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.wardenRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wardenRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.wardenControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ } -+ // Purpur end -+ - @Override - public Packet getAddEntityPacket() { - return new ClientboundAddEntityPacket(this, this.hasPose(Pose.EMERGING) ? 1 : 0); -@@ -396,19 +420,16 @@ public class Warden extends Monster implements VibrationSystem { - - @Contract("null->false") - public boolean canTargetEntity(@Nullable Entity entity) { -- boolean flag; -- -+ if (getRider() != null && isControllable()) return false; // Purpur - if (entity instanceof LivingEntity) { - LivingEntity entityliving = (LivingEntity) entity; - - if (this.level() == entity.level() && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) && !this.isAlliedTo(entity) && entityliving.getType() != EntityType.ARMOR_STAND && entityliving.getType() != EntityType.WARDEN && !entityliving.isInvulnerable() && !entityliving.isDeadOrDying() && this.level().getWorldBorder().isWithinBounds(entityliving.getBoundingBox())) { -- flag = true; -- return flag; -+ return true; // Purpur - wtf - } - } - -- flag = false; -- return flag; -+ return false; // Purpur - wtf - } - - public static void applyDarknessAround(ServerLevel world, Vec3 pos, @Nullable Entity entity, int range) { -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index c4ddf2661bca728d504918171295e10e307b18b4..7b58329067d8debeb34abe801d150038a362ffed 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -156,6 +156,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); - } - -+ // Purpur start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.villagerRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.villagerRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.villagerControllable; -+ } -+ -+ @Override -+ protected void registerGoals() { -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ } -+ // Purpur end -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -@@ -257,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - // Paper end - this.level().getProfiler().push("villagerBrain"); - // Pufferfish start -- if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { -+ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); // Paper - } - // Pufferfish end -@@ -317,7 +339,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) { - if (this.isBaby()) { - this.setUnhappy(); -- return InteractionResult.sidedSuccess(this.level().isClientSide); -+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur - } else { - boolean flag = this.getOffers().isEmpty(); - -@@ -330,8 +352,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - - if (flag) { -- return InteractionResult.sidedSuccess(this.level().isClientSide); -+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur - } else { -+ if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - if (!this.level().isClientSide && !this.offers.isEmpty()) { - this.startTrading(player); - } -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 0002f3a2f2e096d4b2015baf707707f6cc1a9582..7e5650a56d87a48df7ee0a862b22bf015bb48b2a 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -66,6 +66,23 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - //this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader // Paper - move back to MobSpawnerTrader - Vanilla behavior is that only traders spawned by it have this value set. - } - -+ // Purpur - start -+ @Override -+ public boolean isRidable() { -+ return level().purpurConfig.wanderingTraderRidable; -+ } -+ -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wanderingTraderRidableInWater; -+ } -+ -+ @Override -+ public boolean isControllable() { -+ return level().purpurConfig.wanderingTraderControllable; -+ } -+ // Purpur end -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -@@ -113,8 +130,9 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - } - - if (this.getOffers().isEmpty()) { -- return InteractionResult.sidedSuccess(this.level().isClientSide); -+ return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur - } else { -+ if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - if (!this.level().isClientSide) { - this.setTradingPlayer(player); - this.openTradingScreen(player, this.getDisplayName(), 1); -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 58152160d609d0e9d105153aeb166a56a7955603..cf008b8c7a2eb0c642fd9c2da376df86dcd588e2 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -198,6 +198,19 @@ public abstract class Player extends LivingEntity { - } - // CraftBukkit end - -+ // Purpur start -+ public abstract void resetLastActionTime(); -+ -+ @Override -+ public boolean processClick(InteractionHand hand) { -+ Entity vehicle = getRootVehicle(); -+ if (vehicle != null && vehicle.getRider() == this) { -+ return vehicle.onClick(hand); -+ } -+ return false; -+ } -+ // Purpur end -+ - public Player(Level world, BlockPos pos, float yaw, GameProfile gameProfile) { - super(EntityType.PLAYER, world); - this.lastItemInMainHand = ItemStack.EMPTY; -diff --git a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java -index 0bbe853f7df93f9dcd2b21d762939f8b6be069aa..7db9844083703944f59e112c6dc5e1a5e062d31c 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java -@@ -26,6 +26,12 @@ public class LlamaSpit extends Projectile { - this.setPos(owner.getX() - (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(owner.yBodyRot * 0.017453292F), owner.getEyeY() - 0.10000000149011612D, owner.getZ() + (double) (owner.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(owner.yBodyRot * 0.017453292F)); - } - -+ // Purpur start -+ public void super_tick() { -+ super.tick(); -+ } -+ // Purpur end -+ - @Override - public void tick() { - super.tick(); -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java -index cc0a3d9794d05b6bc6ab05f4f2ab8d83134b181d..e1f918d0bd2a70db1aba8bda8717149f58766825 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ProjectileUtil.java -@@ -33,7 +33,7 @@ public final class ProjectileUtil { - return getHitResult(vec32, entity, predicate, vec3, level); - } - -- private static HitResult getHitResult(Vec3 pos, Entity entity, Predicate predicate, Vec3 velocity, Level world) { -+ public static HitResult getHitResult(Vec3 pos, Entity entity, Predicate predicate, Vec3 velocity, Level world) { // Purpur - private -> public - Vec3 vec3 = pos.add(velocity); - HitResult hitResult = world.clip(new ClipContext(pos, vec3, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)); - if (hitResult.getType() != HitResult.Type.MISS) { -diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -index 06d1bdb9bd124b201c36d284c50d22bf50d3735a..937f57d8af629c4e913d7ccabf6adab15f91d3d0 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -@@ -109,6 +109,12 @@ public class WitherSkull extends AbstractHurtingProjectile { - - } - -+ @Override -+ public boolean canHitEntity(Entity target) { -+ // do not hit rider -+ return target != this.getRider() && super.canHitEntity(target); -+ } -+ - @Override - public boolean isPickable() { - return false; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 2dbe8b870fd39b4d22e9725912f443757ae70761..912d9e4b3168cf89bd263ba63e049a791beefc90 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -1507,4 +1507,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - return this.getHandle().getScoreboardName(); - } - // Paper end - entity scoreboard name -+ -+ // Purpur start -+ @Override -+ public org.bukkit.entity.Player getRider() { -+ net.minecraft.world.entity.player.Player rider = getHandle().getRider(); -+ return rider != null ? (org.bukkit.entity.Player) rider.getBukkitEntity() : null; -+ } -+ -+ @Override -+ public boolean hasRider() { -+ return getHandle().getRider() != null; -+ } -+ -+ @Override -+ public boolean isRidable() { -+ return getHandle().isRidable(); -+ } -+ -+ @Override -+ public boolean isRidableInWater() { -+ return !getHandle().dismountsUnderwater(); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index 676c44f688c41df66e304db30a05d6cc967bcf99..222fc8368b6bd0ca32811aebfa611dd5037076b5 100644 ---- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -590,6 +590,15 @@ public class CraftEventFactory { - // Paper end - craftServer.getPluginManager().callEvent(event); - -+ // Purpur start -+ if (who != null) { -+ switch (action) { -+ case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> who.processClick(InteractionHand.MAIN_HAND); -+ case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> who.processClick(InteractionHand.OFF_HAND); -+ } -+ } -+ // Purpur end -+ - return event; - } - -@@ -1031,6 +1040,7 @@ public class CraftEventFactory { - damageCause = DamageCause.ENTITY_EXPLOSION; - } - event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API -+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur - } - event.setCancelled(cancelled); - -@@ -1145,6 +1155,7 @@ public class CraftEventFactory { - } else { - entity.lastDamageCancelled = true; // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Keep track if the event was canceled - } -+ damager.getHandle().processClick(InteractionHand.MAIN_HAND); // Purpur - return event; - } - -@@ -1208,6 +1219,7 @@ public class CraftEventFactory { - EntityDamageEvent event; - if (damager != null) { - event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions, critical); // Paper - add critical damage API -+ damager.processClick(InteractionHand.MAIN_HAND); // Purpur - } else { - event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 61d193d8ddd87817bf2c560037d42366cff1eca9..8ad548fc059568d37675e017548b171d4dd1d555 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -172,4 +172,9 @@ public class PurpurConfig { - } - return builder.build(); - } -+ -+ public static String cannotRideMob = "You cannot mount that mob"; -+ private static void messages() { -+ cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); -+ } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5f0732c2b8f85185b6dfc1db3119c22e8be7f5da..4c25461a6d75a47425b66e04285792787d7193ee 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -89,4 +89,722 @@ public class PurpurWorldConfig { - final Map value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null); - return value.isEmpty() ? fallback : value; - } -+ -+ public boolean babiesAreRidable = true; -+ public boolean untamedTamablesAreRidable = true; -+ public boolean useNightVisionWhenRiding = false; -+ public boolean useDismountsUnderwaterTag = true; -+ private void ridableSettings() { -+ babiesAreRidable = getBoolean("ridable-settings.babies-are-ridable", babiesAreRidable); -+ untamedTamablesAreRidable = getBoolean("ridable-settings.untamed-tamables-are-ridable", untamedTamablesAreRidable); -+ useNightVisionWhenRiding = getBoolean("ridable-settings.use-night-vision", useNightVisionWhenRiding); -+ useDismountsUnderwaterTag = getBoolean("ridable-settings.use-dismounts-underwater-tag", useDismountsUnderwaterTag); -+ } -+ -+ public boolean allayRidable = false; -+ public boolean allayRidableInWater = true; -+ public boolean allayControllable = true; -+ private void allaySettings() { -+ allayRidable = getBoolean("mobs.allay.ridable", allayRidable); -+ allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater); -+ allayControllable = getBoolean("mobs.allay.controllable", allayControllable); -+ } -+ -+ public boolean axolotlRidable = false; -+ public boolean axolotlControllable = true; -+ private void axolotlSettings() { -+ axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); -+ axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); -+ } -+ -+ public boolean batRidable = false; -+ public boolean batRidableInWater = true; -+ public boolean batControllable = true; -+ public double batMaxY = 320D; -+ private void batSettings() { -+ batRidable = getBoolean("mobs.bat.ridable", batRidable); -+ batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater); -+ batControllable = getBoolean("mobs.bat.controllable", batControllable); -+ batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY); -+ } -+ -+ public boolean beeRidable = false; -+ public boolean beeRidableInWater = true; -+ public boolean beeControllable = true; -+ public double beeMaxY = 320D; -+ private void beeSettings() { -+ beeRidable = getBoolean("mobs.bee.ridable", beeRidable); -+ beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -+ beeControllable = getBoolean("mobs.bee.controllable", beeControllable); -+ beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY); -+ } -+ -+ public boolean blazeRidable = false; -+ public boolean blazeRidableInWater = true; -+ public boolean blazeControllable = true; -+ public double blazeMaxY = 320D; -+ private void blazeSettings() { -+ blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); -+ blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); -+ blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable); -+ blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY); -+ } -+ -+ public boolean camelRidableInWater = false; -+ private void camelSettings() { -+ camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); -+ } -+ -+ public boolean catRidable = false; -+ public boolean catRidableInWater = true; -+ public boolean catControllable = true; -+ private void catSettings() { -+ catRidable = getBoolean("mobs.cat.ridable", catRidable); -+ catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -+ catControllable = getBoolean("mobs.cat.controllable", catControllable); -+ } -+ -+ public boolean caveSpiderRidable = false; -+ public boolean caveSpiderRidableInWater = true; -+ public boolean caveSpiderControllable = true; -+ private void caveSpiderSettings() { -+ caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); -+ caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); -+ caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable); -+ } -+ -+ public boolean chickenRidable = false; -+ public boolean chickenRidableInWater = false; -+ public boolean chickenControllable = true; -+ private void chickenSettings() { -+ chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); -+ chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -+ chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable); -+ } -+ -+ public boolean codRidable = false; -+ public boolean codControllable = true; -+ private void codSettings() { -+ codRidable = getBoolean("mobs.cod.ridable", codRidable); -+ codControllable = getBoolean("mobs.cod.controllable", codControllable); -+ } -+ -+ public boolean cowRidable = false; -+ public boolean cowRidableInWater = true; -+ public boolean cowControllable = true; -+ private void cowSettings() { -+ cowRidable = getBoolean("mobs.cow.ridable", cowRidable); -+ cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); -+ cowControllable = getBoolean("mobs.cow.controllable", cowControllable); -+ } -+ -+ public boolean creeperRidable = false; -+ public boolean creeperRidableInWater = true; -+ public boolean creeperControllable = true; -+ private void creeperSettings() { -+ creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); -+ creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -+ creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable); -+ } -+ -+ public boolean dolphinRidable = false; -+ public boolean dolphinControllable = true; -+ public int dolphinSpitCooldown = 20; -+ public float dolphinSpitSpeed = 1.0F; -+ public float dolphinSpitDamage = 2.0F; -+ private void dolphinSettings() { -+ dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); -+ dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -+ dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown); -+ dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed); -+ dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage); -+ } -+ -+ public boolean donkeyRidableInWater = false; -+ private void donkeySettings() { -+ donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); -+ } -+ -+ public boolean drownedRidable = false; -+ public boolean drownedRidableInWater = true; -+ public boolean drownedControllable = true; -+ private void drownedSettings() { -+ drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); -+ drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -+ drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable); -+ } -+ -+ public boolean elderGuardianRidable = false; -+ public boolean elderGuardianControllable = true; -+ private void elderGuardianSettings() { -+ elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); -+ elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); -+ } -+ -+ public boolean enderDragonRidable = false; -+ public boolean enderDragonRidableInWater = true; -+ public boolean enderDragonControllable = true; -+ public double enderDragonMaxY = 320D; -+ private void enderDragonSettings() { -+ enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); -+ enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); -+ enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable); -+ enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY); -+ } -+ -+ public boolean endermanRidable = false; -+ public boolean endermanRidableInWater = true; -+ public boolean endermanControllable = true; -+ private void endermanSettings() { -+ endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); -+ endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -+ endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable); -+ } -+ -+ public boolean endermiteRidable = false; -+ public boolean endermiteRidableInWater = true; -+ public boolean endermiteControllable = true; -+ private void endermiteSettings() { -+ endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); -+ endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); -+ endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable); -+ } -+ -+ public boolean evokerRidable = false; -+ public boolean evokerRidableInWater = true; -+ public boolean evokerControllable = true; -+ private void evokerSettings() { -+ evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); -+ evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); -+ evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable); -+ } -+ -+ public boolean foxRidable = false; -+ public boolean foxRidableInWater = true; -+ public boolean foxControllable = true; -+ private void foxSettings() { -+ foxRidable = getBoolean("mobs.fox.ridable", foxRidable); -+ foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -+ foxControllable = getBoolean("mobs.fox.controllable", foxControllable); -+ } -+ -+ public boolean frogRidable = false; -+ public boolean frogRidableInWater = true; -+ public boolean frogControllable = true; -+ public float frogRidableJumpHeight = 0.65F; -+ private void frogSettings() { -+ frogRidable = getBoolean("mobs.frog.ridable", frogRidable); -+ frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater); -+ frogControllable = getBoolean("mobs.frog.controllable", frogControllable); -+ frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight); -+ } -+ -+ public boolean ghastRidable = false; -+ public boolean ghastRidableInWater = true; -+ public boolean ghastControllable = true; -+ public double ghastMaxY = 320D; -+ private void ghastSettings() { -+ ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); -+ ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); -+ ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable); -+ ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY); -+ } -+ -+ public boolean giantRidable = false; -+ public boolean giantRidableInWater = true; -+ public boolean giantControllable = true; -+ private void giantSettings() { -+ giantRidable = getBoolean("mobs.giant.ridable", giantRidable); -+ giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); -+ giantControllable = getBoolean("mobs.giant.controllable", giantControllable); -+ } -+ -+ public boolean glowSquidRidable = false; -+ public boolean glowSquidControllable = true; -+ private void glowSquidSettings() { -+ glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); -+ glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); -+ } -+ -+ public boolean goatRidable = false; -+ public boolean goatRidableInWater = true; -+ public boolean goatControllable = true; -+ private void goatSettings() { -+ goatRidable = getBoolean("mobs.goat.ridable", goatRidable); -+ goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); -+ goatControllable = getBoolean("mobs.goat.controllable", goatControllable); -+ } -+ -+ public boolean guardianRidable = false; -+ public boolean guardianControllable = true; -+ private void guardianSettings() { -+ guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); -+ guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); -+ } -+ -+ public boolean hoglinRidable = false; -+ public boolean hoglinRidableInWater = true; -+ public boolean hoglinControllable = true; -+ private void hoglinSettings() { -+ hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); -+ hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); -+ hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable); -+ } -+ -+ public boolean horseRidableInWater = false; -+ private void horseSettings() { -+ horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); -+ } -+ -+ public boolean huskRidable = false; -+ public boolean huskRidableInWater = true; -+ public boolean huskControllable = true; -+ private void huskSettings() { -+ huskRidable = getBoolean("mobs.husk.ridable", huskRidable); -+ huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); -+ huskControllable = getBoolean("mobs.husk.controllable", huskControllable); -+ } -+ -+ public boolean illusionerRidable = false; -+ public boolean illusionerRidableInWater = true; -+ public boolean illusionerControllable = true; -+ private void illusionerSettings() { -+ illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); -+ illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); -+ illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable); -+ } -+ -+ public boolean ironGolemRidable = false; -+ public boolean ironGolemRidableInWater = true; -+ public boolean ironGolemControllable = true; -+ public boolean ironGolemCanSwim = false; -+ private void ironGolemSettings() { -+ ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); -+ ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); -+ ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable); -+ ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim); -+ } -+ -+ public boolean llamaRidable = false; -+ public boolean llamaRidableInWater = false; -+ public boolean llamaControllable = true; -+ private void llamaSettings() { -+ llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); -+ llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); -+ llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable); -+ } -+ -+ public boolean magmaCubeRidable = false; -+ public boolean magmaCubeRidableInWater = true; -+ public boolean magmaCubeControllable = true; -+ private void magmaCubeSettings() { -+ magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable); -+ magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater); -+ magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable); -+ } -+ -+ public boolean mooshroomRidable = false; -+ public boolean mooshroomRidableInWater = true; -+ public boolean mooshroomControllable = true; -+ private void mooshroomSettings() { -+ mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); -+ mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); -+ mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable); -+ } -+ -+ public boolean muleRidableInWater = false; -+ private void muleSettings() { -+ muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); -+ } -+ -+ public boolean ocelotRidable = false; -+ public boolean ocelotRidableInWater = true; -+ public boolean ocelotControllable = true; -+ private void ocelotSettings() { -+ ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); -+ ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -+ ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable); -+ } -+ -+ public boolean pandaRidable = false; -+ public boolean pandaRidableInWater = true; -+ public boolean pandaControllable = true; -+ private void pandaSettings() { -+ pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); -+ pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); -+ pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable); -+ } -+ -+ public boolean parrotRidable = false; -+ public boolean parrotRidableInWater = true; -+ public boolean parrotControllable = true; -+ public double parrotMaxY = 320D; -+ private void parrotSettings() { -+ parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); -+ parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); -+ parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable); -+ parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY); -+ } -+ -+ public boolean phantomRidable = false; -+ public boolean phantomRidableInWater = true; -+ public boolean phantomControllable = true; -+ public double phantomMaxY = 320D; -+ public float phantomFlameDamage = 1.0F; -+ public int phantomFlameFireTime = 8; -+ public boolean phantomAllowGriefing = false; -+ private void phantomSettings() { -+ phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); -+ phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -+ phantomControllable = getBoolean("mobs.phantom.controllable", phantomControllable); -+ phantomMaxY = getDouble("mobs.phantom.ridable-max-y", phantomMaxY); -+ phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage); -+ phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime); -+ phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing); -+ } -+ -+ public boolean pigRidable = false; -+ public boolean pigRidableInWater = false; -+ public boolean pigControllable = true; -+ private void pigSettings() { -+ pigRidable = getBoolean("mobs.pig.ridable", pigRidable); -+ pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -+ pigControllable = getBoolean("mobs.pig.controllable", pigControllable); -+ } -+ -+ public boolean piglinRidable = false; -+ public boolean piglinRidableInWater = true; -+ public boolean piglinControllable = true; -+ private void piglinSettings() { -+ piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); -+ piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -+ piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable); -+ } -+ -+ public boolean piglinBruteRidable = false; -+ public boolean piglinBruteRidableInWater = true; -+ public boolean piglinBruteControllable = true; -+ private void piglinBruteSettings() { -+ piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); -+ piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); -+ piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable); -+ } -+ -+ public boolean pillagerRidable = false; -+ public boolean pillagerRidableInWater = true; -+ public boolean pillagerControllable = true; -+ private void pillagerSettings() { -+ pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); -+ pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); -+ pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable); -+ } -+ -+ public boolean polarBearRidable = false; -+ public boolean polarBearRidableInWater = true; -+ public boolean polarBearControllable = true; -+ private void polarBearSettings() { -+ polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); -+ polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); -+ polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable); -+ } -+ -+ public boolean pufferfishRidable = false; -+ public boolean pufferfishControllable = true; -+ private void pufferfishSettings() { -+ pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); -+ pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); -+ } -+ -+ public boolean rabbitRidable = false; -+ public boolean rabbitRidableInWater = true; -+ public boolean rabbitControllable = true; -+ private void rabbitSettings() { -+ rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); -+ rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -+ rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable); -+ } -+ -+ public boolean ravagerRidable = false; -+ public boolean ravagerRidableInWater = false; -+ public boolean ravagerControllable = true; -+ private void ravagerSettings() { -+ ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); -+ ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -+ ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable); -+ } -+ -+ public boolean salmonRidable = false; -+ public boolean salmonControllable = true; -+ private void salmonSettings() { -+ salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); -+ salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); -+ } -+ -+ public boolean sheepRidable = false; -+ public boolean sheepRidableInWater = true; -+ public boolean sheepControllable = true; -+ private void sheepSettings() { -+ sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); -+ sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -+ sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable); -+ } -+ -+ public boolean shulkerRidable = false; -+ public boolean shulkerRidableInWater = true; -+ public boolean shulkerControllable = true; -+ private void shulkerSettings() { -+ shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); -+ shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -+ shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable); -+ } -+ -+ public boolean silverfishRidable = false; -+ public boolean silverfishRidableInWater = true; -+ public boolean silverfishControllable = true; -+ private void silverfishSettings() { -+ silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); -+ silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); -+ silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable); -+ } -+ -+ public boolean skeletonRidable = false; -+ public boolean skeletonRidableInWater = true; -+ public boolean skeletonControllable = true; -+ private void skeletonSettings() { -+ skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); -+ skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -+ skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable); -+ } -+ -+ public boolean skeletonHorseRidableInWater = true; -+ public boolean skeletonHorseCanSwim = false; -+ private void skeletonHorseSettings() { -+ skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); -+ skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim); -+ } -+ -+ public boolean slimeRidable = false; -+ public boolean slimeRidableInWater = true; -+ public boolean slimeControllable = true; -+ private void slimeSettings() { -+ slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable); -+ slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater); -+ slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable); -+ } -+ -+ public boolean snowGolemRidable = false; -+ public boolean snowGolemRidableInWater = true; -+ public boolean snowGolemControllable = true; -+ public boolean snowGolemLeaveTrailWhenRidden = false; -+ private void snowGolemSettings() { -+ snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); -+ snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -+ snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable); -+ snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden); -+ } -+ -+ public boolean snifferRidable = false; -+ public boolean snifferRidableInWater = true; -+ public boolean snifferControllable = true; -+ private void snifferSettings() { -+ snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); -+ snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); -+ snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable); -+ } -+ -+ public boolean squidRidable = false; -+ public boolean squidControllable = true; -+ private void squidSettings() { -+ squidRidable = getBoolean("mobs.squid.ridable", squidRidable); -+ squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -+ } -+ -+ public boolean spiderRidable = false; -+ public boolean spiderRidableInWater = false; -+ public boolean spiderControllable = true; -+ private void spiderSettings() { -+ spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); -+ spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); -+ spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable); -+ } -+ -+ public boolean strayRidable = false; -+ public boolean strayRidableInWater = true; -+ public boolean strayControllable = true; -+ private void straySettings() { -+ strayRidable = getBoolean("mobs.stray.ridable", strayRidable); -+ strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); -+ strayControllable = getBoolean("mobs.stray.controllable", strayControllable); -+ } -+ -+ public boolean striderRidable = false; -+ public boolean striderRidableInWater = false; -+ public boolean striderControllable = true; -+ private void striderSettings() { -+ striderRidable = getBoolean("mobs.strider.ridable", striderRidable); -+ striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -+ striderControllable = getBoolean("mobs.strider.controllable", striderControllable); -+ } -+ -+ public boolean tadpoleRidable = false; -+ public boolean tadpoleRidableInWater = true; -+ public boolean tadpoleControllable = true; -+ private void tadpoleSettings() { -+ tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable); -+ tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater); -+ tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable); -+ } -+ -+ public boolean traderLlamaRidable = false; -+ public boolean traderLlamaRidableInWater = false; -+ public boolean traderLlamaControllable = true; -+ private void traderLlamaSettings() { -+ traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); -+ traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); -+ traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable); -+ } -+ -+ public boolean tropicalFishRidable = false; -+ public boolean tropicalFishControllable = true; -+ private void tropicalFishSettings() { -+ tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); -+ tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); -+ } -+ -+ public boolean turtleRidable = false; -+ public boolean turtleRidableInWater = true; -+ public boolean turtleControllable = true; -+ private void turtleSettings() { -+ turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); -+ turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); -+ turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable); -+ } -+ -+ public boolean vexRidable = false; -+ public boolean vexRidableInWater = true; -+ public boolean vexControllable = true; -+ public double vexMaxY = 320D; -+ private void vexSettings() { -+ vexRidable = getBoolean("mobs.vex.ridable", vexRidable); -+ vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); -+ vexControllable = getBoolean("mobs.vex.controllable", vexControllable); -+ vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY); -+ } -+ -+ public boolean villagerRidable = false; -+ public boolean villagerRidableInWater = true; -+ public boolean villagerControllable = true; -+ private void villagerSettings() { -+ villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); -+ villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -+ villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable); -+ } -+ -+ public boolean vindicatorRidable = false; -+ public boolean vindicatorRidableInWater = true; -+ public boolean vindicatorControllable = true; -+ private void vindicatorSettings() { -+ vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); -+ vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); -+ vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable); -+ } -+ -+ public boolean wanderingTraderRidable = false; -+ public boolean wanderingTraderRidableInWater = true; -+ public boolean wanderingTraderControllable = true; -+ private void wanderingTraderSettings() { -+ wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); -+ wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -+ wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable); -+ } -+ -+ public boolean wardenRidable = false; -+ public boolean wardenRidableInWater = true; -+ public boolean wardenControllable = true; -+ private void wardenSettings() { -+ wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable); -+ wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater); -+ wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable); -+ } -+ -+ public boolean witchRidable = false; -+ public boolean witchRidableInWater = true; -+ public boolean witchControllable = true; -+ private void witchSettings() { -+ witchRidable = getBoolean("mobs.witch.ridable", witchRidable); -+ witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); -+ witchControllable = getBoolean("mobs.witch.controllable", witchControllable); -+ } -+ -+ public boolean witherRidable = false; -+ public boolean witherRidableInWater = true; -+ public boolean witherControllable = true; -+ public double witherMaxY = 320D; -+ private void witherSettings() { -+ witherRidable = getBoolean("mobs.wither.ridable", witherRidable); -+ witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -+ witherControllable = getBoolean("mobs.wither.controllable", witherControllable); -+ witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY); -+ } -+ -+ public boolean witherSkeletonRidable = false; -+ public boolean witherSkeletonRidableInWater = true; -+ public boolean witherSkeletonControllable = true; -+ private void witherSkeletonSettings() { -+ witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); -+ witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); -+ witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable); -+ } -+ -+ public boolean wolfRidable = false; -+ public boolean wolfRidableInWater = true; -+ public boolean wolfControllable = true; -+ private void wolfSettings() { -+ wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); -+ wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); -+ wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable); -+ } -+ -+ public boolean zoglinRidable = false; -+ public boolean zoglinRidableInWater = true; -+ public boolean zoglinControllable = true; -+ private void zoglinSettings() { -+ zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); -+ zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); -+ zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable); -+ } -+ -+ public boolean zombieRidable = false; -+ public boolean zombieRidableInWater = true; -+ public boolean zombieControllable = true; -+ private void zombieSettings() { -+ zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); -+ zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -+ zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable); -+ } -+ -+ public boolean zombieHorseRidableInWater = false; -+ public boolean zombieHorseCanSwim = false; -+ private void zombieHorseSettings() { -+ zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); -+ zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); -+ } -+ -+ public boolean zombieVillagerRidable = false; -+ public boolean zombieVillagerRidableInWater = true; -+ public boolean zombieVillagerControllable = true; -+ private void zombieVillagerSettings() { -+ zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); -+ zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -+ zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable); -+ } -+ -+ public boolean zombifiedPiglinRidable = false; -+ public boolean zombifiedPiglinRidableInWater = true; -+ public boolean zombifiedPiglinControllable = true; -+ private void zombifiedPiglinSettings() { -+ zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); -+ zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -+ zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable); -+ } - } -diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ed494e0ad278813a0eb261101447b84cca3ad7aa ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java -@@ -0,0 +1,71 @@ -+package org.purpurmc.purpur.controller; -+ -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.attributes.Attributes; -+import net.minecraft.world.entity.player.Player; -+ -+public class FlyingMoveControllerWASD extends MoveControllerWASD { -+ protected final float groundSpeedModifier; -+ protected final float flyingSpeedModifier; -+ protected int tooHighCooldown = 0; -+ protected boolean setNoGravityFlag; -+ -+ public FlyingMoveControllerWASD(Mob entity) { -+ this(entity, 1.0F); -+ } -+ -+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) { -+ this(entity, groundSpeedModifier, 1.0F, true); -+ } -+ -+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) { -+ this(entity, groundSpeedModifier, flyingSpeedModifier, true); -+ } -+ -+ public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) { -+ super(entity); -+ this.groundSpeedModifier = groundSpeedModifier; -+ this.flyingSpeedModifier = flyingSpeedModifier; -+ this.setNoGravityFlag = setNoGravityFlag; -+ } -+ -+ @Override -+ public void purpurTick(Player rider) { -+ float forward = Math.max(0.0F, rider.getForwardMot()); -+ float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F); -+ float strafe = rider.getStrafeMot(); -+ -+ if (rider.jumping && spacebarEvent(entity)) { -+ entity.onSpacebar(); -+ } -+ -+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) { -+ if (tooHighCooldown <= 0) { -+ tooHighCooldown = 20; -+ } -+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D)); -+ vertical = 0.0F; -+ } -+ -+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float speed = (float) getSpeedModifier(); -+ -+ if (entity.onGround) { -+ speed *= groundSpeedModifier; // TODO = fix this! -+ } else { -+ speed *= flyingSpeedModifier; -+ } -+ -+ if (setNoGravityFlag) { -+ entity.setNoGravity(forward > 0); -+ } -+ -+ entity.setSpeed(speed); -+ entity.setVerticalMot(vertical); -+ entity.setStrafeMot(strafe); -+ entity.setForwardMot(forward); -+ -+ setForward(entity.getForwardMot()); -+ setStrafe(entity.getStrafeMot()); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java -new file mode 100644 -index 0000000000000000000000000000000000000000..9383c07fa53141127106a1f289366a040960d52e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java -@@ -0,0 +1,63 @@ -+package org.purpurmc.purpur.controller; -+ -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.attributes.Attributes; -+import net.minecraft.world.entity.player.Player; -+import net.minecraft.world.phys.Vec3; -+ -+public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD { -+ public FlyingWithSpacebarMoveControllerWASD(Mob entity) { -+ super(entity); -+ } -+ -+ public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) { -+ super(entity, groundSpeedModifier); -+ } -+ -+ @Override -+ public void purpurTick(Player rider) { -+ float forward = rider.getForwardMot(); -+ float strafe = rider.getStrafeMot() * 0.5F; -+ float vertical = 0; -+ -+ if (forward < 0.0F) { -+ forward *= 0.5F; -+ strafe *= 0.5F; -+ } -+ -+ float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED); -+ -+ if (entity.onGround) { -+ speed *= groundSpeedModifier; -+ } -+ -+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar()) { -+ entity.setNoGravity(true); -+ vertical = 1.0F; -+ } else { -+ entity.setNoGravity(false); -+ } -+ -+ if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) { -+ if (tooHighCooldown <= 0) { -+ tooHighCooldown = 20; -+ } -+ entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D)); -+ vertical = 0.0F; -+ } -+ -+ setSpeedModifier(speed); -+ entity.setSpeed((float) getSpeedModifier()); -+ entity.setVerticalMot(vertical); -+ entity.setStrafeMot(strafe); -+ entity.setForwardMot(forward); -+ -+ setForward(entity.getForwardMot()); -+ setStrafe(entity.getStrafeMot()); -+ -+ Vec3 mot = entity.getDeltaMovement(); -+ if (mot.y > 0.2D) { -+ entity.setDeltaMovement(mot.x, 0.2D, mot.z); -+ } -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b8c25c96e95dd5ec3ad9fa4c41bd6c08e144832d ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java -@@ -0,0 +1,76 @@ -+package org.purpurmc.purpur.controller; -+ -+ -+import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; -+import net.minecraft.util.Mth; -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.control.LookControl; -+import net.minecraft.world.entity.player.Player; -+ -+public class LookControllerWASD extends LookControl { -+ protected final Mob entity; -+ private float yOffset = 0; -+ private float xOffset = 0; -+ -+ public LookControllerWASD(Mob entity) { -+ super(entity); -+ this.entity = entity; -+ } -+ -+ // tick -+ @Override -+ public void tick() { -+ if (entity.getRider() != null && entity.isControllable()) { -+ purpurTick(entity.getRider()); -+ } else { -+ vanillaTick(); -+ } -+ } -+ -+ protected void purpurTick(Player rider) { -+ setYawPitch(rider.getYRot(), rider.getXRot()); -+ } -+ -+ public void vanillaTick() { -+ super.tick(); -+ } -+ -+ public void setYawPitch(float yRot, float xRot) { -+ entity.setXRot(normalizePitch(xRot + xOffset)); -+ entity.setYRot(normalizeYaw(yRot + yOffset)); -+ entity.setYHeadRot(entity.getYRot()); -+ entity.xRotO = entity.getXRot(); -+ entity.yRotO = entity.getYRot(); -+ -+ entity.tracker.broadcast(new ClientboundMoveEntityPacket -+ .PosRot(entity.getId(), -+ (short) 0, (short) 0, (short) 0, -+ (byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F), -+ (byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F), -+ entity.onGround)); -+ } -+ -+ public void setOffsets(float yaw, float pitch) { -+ yOffset = yaw; -+ xOffset = pitch; -+ } -+ -+ public float normalizeYaw(float yaw) { -+ yaw %= 360.0f; -+ if (yaw >= 180.0f) { -+ yaw -= 360.0f; -+ } else if (yaw < -180.0f) { -+ yaw += 360.0f; -+ } -+ return yaw; -+ } -+ -+ public float normalizePitch(float pitch) { -+ if (pitch > 90.0f) { -+ pitch = 90.0f; -+ } else if (pitch < -90.0f) { -+ pitch = -90.0f; -+ } -+ return pitch; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java -new file mode 100644 -index 0000000000000000000000000000000000000000..21fd6ea2a482758a3016e3bc2cdebe2d89267481 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java -@@ -0,0 +1,89 @@ -+package org.purpurmc.purpur.controller; -+ -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.attributes.Attributes; -+import net.minecraft.world.entity.ai.control.MoveControl; -+import net.minecraft.world.entity.player.Player; -+import org.purpurmc.purpur.event.entity.RidableSpacebarEvent; -+ -+public class MoveControllerWASD extends MoveControl { -+ protected final Mob entity; -+ private final double speedModifier; -+ -+ public MoveControllerWASD(Mob entity) { -+ this(entity, 1.0D); -+ } -+ -+ public MoveControllerWASD(Mob entity, double speedModifier) { -+ super(entity); -+ this.entity = entity; -+ this.speedModifier = speedModifier; -+ } -+ -+ @Override -+ public boolean hasWanted() { -+ return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted(); -+ } -+ -+ @Override -+ public void tick() { -+ if (entity.getRider() != null && entity.isControllable()) { -+ purpurTick(entity.getRider()); -+ } else { -+ vanillaTick(); -+ } -+ } -+ -+ public void vanillaTick() { -+ super.tick(); -+ } -+ -+ public void purpurTick(Player rider) { -+ float forward = rider.getForwardMot() * 0.5F; -+ float strafe = rider.getStrafeMot() * 0.25F; -+ -+ if (forward <= 0.0F) { -+ forward *= 0.5F; -+ } -+ -+ float yawOffset = 0; -+ if (strafe != 0) { -+ if (forward == 0) { -+ yawOffset += strafe > 0 ? -90 : 90; -+ forward = Math.abs(strafe * 2); -+ } else { -+ yawOffset += strafe > 0 ? -30 : 30; -+ strafe /= 2; -+ if (forward < 0) { -+ yawOffset += strafe > 0 ? -110 : 110; -+ forward *= -1; -+ } -+ } -+ } else if (forward < 0) { -+ yawOffset -= 180; -+ forward *= -1; -+ } -+ -+ ((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0); -+ -+ if (rider.jumping && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) { -+ entity.jumpFromGround(); -+ } -+ -+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier); -+ -+ entity.setSpeed((float) getSpeedModifier()); -+ entity.setForwardMot(forward); -+ -+ setForward(entity.getForwardMot()); -+ setStrafe(entity.getStrafeMot()); -+ } -+ -+ public static boolean spacebarEvent(Mob entity) { -+ if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent(); -+ } else { -+ return true; -+ } -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ba2a37dad43e238e54632975abea8ee6fafaa9e0 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java -@@ -0,0 +1,50 @@ -+package org.purpurmc.purpur.controller; -+ -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.attributes.Attributes; -+import net.minecraft.world.entity.player.Player; -+ -+public class WaterMoveControllerWASD extends MoveControllerWASD { -+ private final double speedModifier; -+ -+ public WaterMoveControllerWASD(Mob entity) { -+ this(entity, 1.0D); -+ } -+ -+ public WaterMoveControllerWASD(Mob entity, double speedModifier) { -+ super(entity); -+ this.speedModifier = speedModifier; -+ } -+ -+ @Override -+ public void purpurTick(Player rider) { -+ float forward = rider.getForwardMot(); -+ float strafe = rider.getStrafeMot() * 0.5F; // strafe slower by default -+ float vertical = -(rider.xRotO / 90); -+ -+ if (forward == 0.0F) { -+ // strafe slower if not moving forward -+ strafe *= 0.5F; -+ // do not move vertically if not moving forward -+ vertical = 0.0F; -+ } else if (forward < 0.0F) { -+ // water animals can't swim backwards -+ forward = 0.0F; -+ vertical = 0.0F; -+ } -+ -+ if (rider.jumping && spacebarEvent(entity)) { -+ entity.onSpacebar(); -+ } -+ -+ setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier); -+ entity.setSpeed((float) getSpeedModifier() * 0.1F); -+ -+ entity.setForwardMot(forward * (float) speedModifier); -+ entity.setStrafeMot(strafe * (float) speedModifier); -+ entity.setVerticalMot(vertical * (float) speedModifier); -+ -+ setForward(entity.getForwardMot()); -+ setStrafe(entity.getStrafeMot()); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -new file mode 100644 -index 0000000000000000000000000000000000000000..af057c1d7fd74f3dd806c5ce7f8b0ad06cab7b8e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -@@ -0,0 +1,99 @@ -+package org.purpurmc.purpur.entity; -+ -+import net.minecraft.core.particles.ParticleTypes; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.util.Mth; -+import net.minecraft.world.damagesource.DamageSource; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.entity.LivingEntity; -+import net.minecraft.world.entity.animal.Dolphin; -+import net.minecraft.world.entity.projectile.LlamaSpit; -+import net.minecraft.world.entity.projectile.ProjectileUtil; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.phys.BlockHitResult; -+import net.minecraft.world.phys.EntityHitResult; -+import net.minecraft.world.phys.HitResult; -+import net.minecraft.world.phys.Vec3; -+ -+public class DolphinSpit extends LlamaSpit { -+ public LivingEntity dolphin; -+ public int ticksLived; -+ -+ public DolphinSpit(EntityType type, Level world) { -+ super(type, world); -+ } -+ -+ public DolphinSpit(Level world, Dolphin dolphin) { -+ this(EntityType.LLAMA_SPIT, world); -+ setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin); -+ this.dolphin = dolphin; -+ this.setPos( -+ dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(dolphin.yBodyRot * 0.017453292F), -+ dolphin.getEyeY() - 0.10000000149011612D, -+ dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F)); -+ } -+ -+ public void tick() { -+ super_tick(); -+ -+ Vec3 mot = this.getDeltaMovement(); -+ HitResult hitResult = ProjectileUtil.getHitResult(this.position(), this, this::canHitEntity, mot, level()); -+ -+ this.preOnHit(hitResult); -+ -+ double x = this.getX() + mot.x; -+ double y = this.getY() + mot.y; -+ double z = this.getZ() + mot.z; -+ -+ this.updateRotation(); -+ -+ Vec3 motDouble = mot.scale(2.0); -+ for (int i = 0; i < 5; i++) { -+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.BUBBLE, -+ getX() + random.nextFloat() / 2 - 0.25F, -+ getY() + random.nextFloat() / 2 - 0.25F, -+ getZ() + random.nextFloat() / 2 - 0.25F, -+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true); -+ } -+ -+ if (++ticksLived > 20) { -+ this.discard(); -+ } else { -+ this.setDeltaMovement(mot.scale(0.99D)); -+ if (!this.isNoGravity()) { -+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D)); -+ } -+ -+ this.setPos(x, y, z); -+ } -+ } -+ -+ @Override -+ public void shoot(double x, double y, double z, float speed, float inaccuracy) { -+ setDeltaMovement(new Vec3(x, y, z).normalize().add( -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy) -+ .scale(speed)); -+ } -+ -+ @Override -+ protected void onHitEntity(EntityHitResult entityHitResult) { -+ Entity shooter = this.getOwner(); -+ if (shooter instanceof LivingEntity) { -+ entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.dolphinSpitDamage); -+ } -+ } -+ -+ @Override -+ protected void onHitBlock(BlockHitResult blockHitResult) { -+ if (this.hitCancelled) { -+ return; -+ } -+ BlockState state = this.level().getBlockState(blockHitResult.getBlockPos()); -+ state.onProjectileHit(this.level(), state, blockHitResult, this); -+ this.discard(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1fd6412e332ea8ee82b19c108e113624e51d764b ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -@@ -0,0 +1,114 @@ -+package org.purpurmc.purpur.entity; -+ -+import net.minecraft.core.particles.ParticleTypes; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.util.Mth; -+import net.minecraft.world.damagesource.DamageSource; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.entity.LivingEntity; -+import net.minecraft.world.entity.decoration.ArmorStand; -+import net.minecraft.world.entity.monster.Phantom; -+import net.minecraft.world.entity.projectile.LlamaSpit; -+import net.minecraft.world.entity.projectile.ProjectileUtil; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.state.BlockBehaviour; -+import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.phys.BlockHitResult; -+import net.minecraft.world.phys.EntityHitResult; -+import net.minecraft.world.phys.HitResult; -+import net.minecraft.world.phys.Vec3; -+ -+public class PhantomFlames extends LlamaSpit { -+ public Phantom phantom; -+ public int ticksLived; -+ public boolean canGrief = false; -+ -+ public PhantomFlames(EntityType type, Level world) { -+ super(type, world); -+ } -+ -+ public PhantomFlames(Level world, Phantom phantom) { -+ this(EntityType.LLAMA_SPIT, world); -+ setOwner(phantom.getRider() != null ? phantom.getRider() : phantom); -+ this.phantom = phantom; -+ this.setPos( -+ phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * 0.017453292F), -+ phantom.getEyeY() - 0.10000000149011612D, -+ phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * 0.017453292F)); -+ } -+ -+ public void tick() { -+ super_tick(); -+ -+ Vec3 mot = this.getDeltaMovement(); -+ HitResult hitResult = ProjectileUtil.getHitResult(this.position(), this, this::canHitEntity, mot, level()); -+ -+ this.preOnHit(hitResult); -+ -+ double x = this.getX() + mot.x; -+ double y = this.getY() + mot.y; -+ double z = this.getZ() + mot.z; -+ -+ this.updateRotation(); -+ -+ Vec3 motDouble = mot.scale(2.0); -+ for (int i = 0; i < 5; i++) { -+ ((ServerLevel) level()).sendParticles(null, ParticleTypes.FLAME, -+ getX() + random.nextFloat() / 2 - 0.25F, -+ getY() + random.nextFloat() / 2 - 0.25F, -+ getZ() + random.nextFloat() / 2 - 0.25F, -+ 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D, true); -+ } -+ -+ if (++ticksLived > 20) { -+ this.discard(); -+ } else if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) { -+ this.discard(); -+ } else if (this.isInWaterOrBubble()) { -+ this.discard(); -+ } else { -+ this.setDeltaMovement(mot.scale(0.99D)); -+ if (!this.isNoGravity()) { -+ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D)); -+ } -+ -+ this.setPos(x, y, z); -+ } -+ } -+ -+ @Override -+ public void shoot(double x, double y, double z, float speed, float inaccuracy) { -+ setDeltaMovement(new Vec3(x, y, z).normalize().add( -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, -+ random.nextGaussian() * (double) 0.0075F * (double) inaccuracy) -+ .scale(speed)); -+ } -+ -+ @Override -+ protected void onHitEntity(EntityHitResult entityHitResult) { -+ Entity shooter = this.getOwner(); -+ if (shooter instanceof LivingEntity) { -+ Entity target = entityHitResult.getEntity(); -+ if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) { -+ boolean hurt = target.hurt(target.damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.phantomFlameDamage); -+ if (hurt && level().purpurConfig.phantomFlameFireTime > 0) { -+ target.setSecondsOnFire(level().purpurConfig.phantomFlameFireTime); -+ } -+ } -+ } -+ } -+ -+ @Override -+ protected void onHitBlock(BlockHitResult blockHitResult) { -+ if (this.hitCancelled) { -+ return; -+ } -+ if (this.canGrief) { -+ BlockState state = this.level().getBlockState(blockHitResult.getBlockPos()); -+ state.onProjectileHit(this.level(), state, blockHitResult, this); -+ } -+ this.discard(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8babdaddd8b33278aea0369dbbeeb445abe45016 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java -@@ -0,0 +1,20 @@ -+package org.purpurmc.purpur.entity.ai; -+ -+import net.minecraft.world.entity.Mob; -+import net.minecraft.world.entity.ai.goal.Goal; -+ -+import java.util.EnumSet; -+ -+public class HasRider extends Goal { -+ public final Mob entity; -+ -+ public HasRider(Mob entity) { -+ this.entity = entity; -+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR)); -+ } -+ -+ @Override -+ public boolean canUse() { -+ return entity.getRider() != null && entity.isControllable(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java -new file mode 100644 -index 0000000000000000000000000000000000000000..432f4f3d82af2f19820890b68d33189a9f2c69f9 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java -@@ -0,0 +1,17 @@ -+package org.purpurmc.purpur.entity.ai; -+ -+import net.minecraft.world.entity.animal.horse.AbstractHorse; -+ -+public class HorseHasRider extends HasRider { -+ public final AbstractHorse horse; -+ -+ public HorseHasRider(AbstractHorse entity) { -+ super(entity); -+ this.horse = entity; -+ } -+ -+ @Override -+ public boolean canUse() { -+ return super.canUse() && horse.isSaddled(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java -new file mode 100644 -index 0000000000000000000000000000000000000000..18a95e043cbffa65eeaaf65ff7695e5dc939820c ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java -@@ -0,0 +1,17 @@ -+package org.purpurmc.purpur.entity.ai; -+ -+import net.minecraft.world.entity.animal.horse.Llama; -+ -+public class LlamaHasRider extends HasRider { -+ public final Llama llama; -+ -+ public LlamaHasRider(Llama entity) { -+ super(entity); -+ this.llama = entity; -+ } -+ -+ @Override -+ public boolean canUse() { -+ return super.canUse() && llama.isSaddled() && llama.isControllable(); -+ } -+} -diff --git a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java -index b2d510459bcf90a3611f3d91dae4ccc3d29b4079..7a052f6deaa30f8a177a2aaf172f9da6c308a22b 100644 ---- a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java -+++ b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java -@@ -37,7 +37,7 @@ public class VanillaMobGoalTest { - } - - List> classes; -- try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) { -+ try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft", "org.purpurmc.purpur.entity.ai").scan()) { // Purpur - classes = scanResult.getSubclasses(net.minecraft.world.entity.ai.goal.Goal.class.getName()).loadClasses(); - } - diff --git a/patches/server/0009-Configurable-entity-base-attributes.patch b/patches/server/0009-Configurable-entity-base-attributes.patch deleted file mode 100644 index 160b07283..000000000 --- a/patches/server/0009-Configurable-entity-base-attributes.patch +++ /dev/null @@ -1,2844 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 10 Dec 2020 16:44:54 -0600 -Subject: [PATCH] Configurable entity base attributes - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 14624fc143c9553930f181f7db93caecf24c0495..9e1124be5c6f297b4f6bc8a1bb2dc1476cac4d71 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -156,7 +156,7 @@ import org.bukkit.plugin.PluginManager; - // CraftBukkit end - - public abstract class Entity implements Nameable, EntityAccess, CommandSource { -- -+ public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur - // CraftBukkit start - private static final int CURRENT_LEVEL = 2; - public boolean preserveMotion = true; // Paper - keep initial motion on first setPositionRotation -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index c6a06e07f0b4bb29b5f4c70dfa53ff6db2e4e6ea..730958dab7f074930cdccb88a89aa26e2b6a112b 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -36,6 +36,11 @@ public class GlowSquid extends Squid { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth); -+ } -+ - @Override - protected ParticleOptions getInkParticle() { - return ParticleTypes.GLOW_SQUID_INK; -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 3c2892b9b2ade76d9d4b081ad5bce4991df11dda..0d07e666f4da002a353d03844d62a0e3aac44d9d 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -287,6 +287,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.useItem = ItemStack.EMPTY; - this.lastClimbablePos = Optional.empty(); - this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur -+ this.initAttributes(); // Purpur - this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit - // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor - this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue()); -@@ -302,6 +303,8 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (Tag) dynamicopsnbt.emptyMap())))); - } - -+ protected void initAttributes() {}// Purpur -+ - public Brain getBrain() { - return this.brain; - } -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index e184d2a89a89d4bf77a32a2d610175c5bbd38a03..c283900e6c43fda62428a6e6d8b70e512458e779 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -264,6 +264,18 @@ public class Bat extends AmbientCreature { - } - } - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.batMaxHealth); -+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.batFollowRange); -+ this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level().purpurConfig.batKnockbackResistance); -+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.batMovementSpeed); -+ this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level().purpurConfig.batFlyingSpeed); -+ this.getAttribute(Attributes.ARMOR).setBaseValue(this.level().purpurConfig.batArmor); -+ this.getAttribute(Attributes.ARMOR_TOUGHNESS).setBaseValue(this.level().purpurConfig.batArmorToughness); -+ this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level().purpurConfig.batAttackKnockback); -+ } -+ - @Override - public void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index 706ae64b894709601dccfb621d3c215f073e98e9..e7f95639a442cf602fef0ed7aec0f5f96aa6fd92 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -471,6 +471,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.beeMaxHealth); -+ } -+ - @Override - public int getRemainingPersistentAngerTime() { - return (Integer) this.entityData.get(Bee.DATA_REMAINING_ANGER_TIME); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 90ec526b135a6de6e7ba16fdd3a113ef92b42ef6..49a6ac765e71b311c91cbfd4cf9cde2daa729239 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -122,6 +122,11 @@ public class Cat extends TamableAnimal implements VariantHolder { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.catMaxHealth); -+ } -+ - public ResourceLocation getResourceLocation() { - return this.getVariant().texture(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index 600b7b4e510871de0dee4051158d344734f04966..ea404a84a43a02a5614d5142bb78a586edfc69f6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -71,6 +71,11 @@ public class Chicken extends Animal { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.chickenMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index e2a98b45e56a368de19bb65e304370a5998c7cb9..3d61c2d5da103de68242c16d85c703813979d179 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -25,6 +25,11 @@ public class Cod extends AbstractSchoolingFish { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.codMaxHealth); -+ } -+ - @Override - public ItemStack getBucketItemStack() { - return new ItemStack(Items.COD_BUCKET); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 11082cf272b72123d8917baa9a0d0e5367dca66b..621fe1c08dda65934437eeab679b6e5cd2d11d3d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -59,6 +59,11 @@ public class Cow extends Animal { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index 3dab78e2f39855f71bb7294521be71b3faa59b81..89e3dbfddc739f97fdb6ec9a5714530f03cc7092 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -158,6 +158,11 @@ public class Dolphin extends WaterAnimal { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.dolphinMaxHealth); -+ } -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index e0a21a0e8ff29f5b634fcb312881a1a93c6c3a44..d6370eb9af9842710052e0e497af52122f265488 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -179,6 +179,11 @@ public class Fox extends Animal implements VariantHolder { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.foxMaxHealth); -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 6815d7350a82c6d32f60aa6116466ebd06a920f9..95ff109511c97d603aeaf9e73c49397a841fcbce 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -81,6 +81,11 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - public boolean isControllable() { - return level().purpurConfig.ironGolemControllable; - } -+ -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ironGolemMaxHealth); -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 7b0e3b46e3d318ee856d53cfc5532f3432447438..780a11339d2492dd607e3ef91867ffbb9062e2cd 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -80,6 +80,11 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - } - wasOnGround = onGround; - } -+ -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.rabbitMaxHealth); -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 87c442fb198cad8671ad1419e589a5a67c4fdca8..742805994f29a18af444912b10af631d2c60cacf 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -25,6 +25,11 @@ public class Salmon extends AbstractSchoolingFish { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.salmonMaxHealth); -+ } -+ - @Override - public int getMaxSchoolSize() { - return 5; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index 0e2f10fa133281477ab96ba743266ade4d5f2119..3d212b09258b9777079d4bc7ce950f529cdce69e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -134,6 +134,11 @@ public class Sheep extends Animal implements Shearable { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.sheepMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.eatBlockGoal = new EatBlockGoal(this); -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index 04927f5f06d6dc14ad01319dd22583632c2c511b..3ffd9dd16c80399842ba138ed60abd66cd312dcb 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -71,6 +71,11 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snowGolemMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index 7fa03dbc442942b70f374096e346d54089d8f0a0..913b66be2111da862e706d4978825c64cfe8b00b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -70,6 +70,11 @@ public class Squid extends WaterAnimal { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.squidMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -index 71234b258157579d3a47064e7e299bb7fb90908f..7d02e552a27632939bb9c40a62f4e0df7bd60bbc 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -@@ -54,6 +54,11 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.tropicalFishMaxHealth); -+ } -+ - public static String getPredefinedName(int variant) { - return "entity.minecraft.tropical_fish.predefined." + variant; - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index 46b71439efdab39c28d29684913bec2a1ba6491a..6ae82b6293f236fc926f411de2badc496b35399b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -100,6 +100,11 @@ public class Turtle extends Animal { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.turtleMaxHealth); -+ } -+ - public void setHomePos(BlockPos pos) { - this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index 40d647d6b3132f8273d43a1addcb8c8116c7ec19..1a27e811169a58515c796344ffa1bb3c46d1014a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -125,6 +125,11 @@ public class Wolf extends TamableAnimal implements NeutralMob { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wolfMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index b76bead9c107889e9b2f11bdc24ad7da811f97ed..e1c0c122dde56b8dd797d1278340260150025cf9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -115,6 +115,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { - return this.modelRotationValues; -diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -index 76a38d235de3499ca19c3ccacd9289c7355012db..5ec8815cc69f5abe03a29224650edfb153c9f5d0 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -@@ -326,6 +326,23 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider - return this.dashCooldown; - } - -+ // Purpur start -+ @Override -+ public float generateMaxHealth(net.minecraft.util.RandomSource random) { -+ return (float) generateMaxHealth(this.level().purpurConfig.camelMaxHealthMin, this.level().purpurConfig.camelMaxHealthMax); -+ } -+ -+ @Override -+ public double generateJumpStrength(net.minecraft.util.RandomSource random) { -+ return generateJumpStrength(this.level().purpurConfig.camelJumpStrengthMin, this.level().purpurConfig.camelJumpStrengthMax); -+ } -+ -+ @Override -+ public double generateSpeed(net.minecraft.util.RandomSource random) { -+ return generateSpeed(this.level().purpurConfig.camelMovementSpeedMin, this.level().purpurConfig.camelMovementSpeedMax); -+ } -+ // Purpur end -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.CAMEL_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -index 4b656f4e1660978e3dd284093241003cabff4f23..7a7466092a2cd194abec3abdb77a7c649b4b433c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -@@ -160,6 +160,44 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth(random)); -+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.generateSpeed(random)); -+ this.getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(this.generateJumpStrength(random)); -+ } -+ -+ protected double generateMaxHealth(double min, double max) { -+ if (min == max) return min; -+ int diff = Mth.floor(max - min); -+ double base = max - diff; -+ int first = Mth.floor((double) diff / 2); -+ int rest = diff - first; -+ return base + random.nextInt(first + 1) + random.nextInt(rest + 1); -+ } -+ -+ protected double generateJumpStrength(double min, double max) { -+ if (min == max) return min; -+ return min + (max - min) * this.random.nextDouble(); -+ } -+ -+ protected double generateSpeed(double min, double max) { -+ if (min == max) return min; -+ return min + (max - min) * this.random.nextDouble(); -+ } -+ -+ protected float generateMaxHealth(RandomSource random) { -+ return 15.0F + (float) random.nextInt(8) + (float) random.nextInt(9); -+ } -+ -+ protected double generateJumpStrength(RandomSource random) { -+ return 0.4000000059604645D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D + random.nextDouble() * 0.2D; -+ } -+ -+ protected double generateSpeed(RandomSource random) { -+ return (0.44999998807907104D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D + random.nextDouble() * 0.3D) * 0.25D; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur -@@ -1258,7 +1296,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - entityData = new AgeableMob.AgeableMobGroupData(0.2F); - } - -- this.randomizeAttributes(world.getRandom()); -+ // this.randomizeAttributes(world.getRandom()); // Purpur - replaced by initAttributes() - return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData, entityNbt); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -index 6a5fb18523545ccc788b09ab4fbee1b14a64bc63..365273173e37e74470a1cf511334ea8cb25f8302 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -@@ -22,6 +22,21 @@ public class Donkey extends AbstractChestedHorse { - } - // Purpur end - -+ @Override -+ public float generateMaxHealth(net.minecraft.util.RandomSource random) { -+ return (float) generateMaxHealth(this.level().purpurConfig.donkeyMaxHealthMin, this.level().purpurConfig.donkeyMaxHealthMax); -+ } -+ -+ @Override -+ public double generateJumpStrength(net.minecraft.util.RandomSource random) { -+ return generateJumpStrength(this.level().purpurConfig.donkeyJumpStrengthMin, this.level().purpurConfig.donkeyJumpStrengthMax); -+ } -+ -+ @Override -+ public double generateSpeed(net.minecraft.util.RandomSource random) { -+ return generateSpeed(this.level().purpurConfig.donkeyMovementSpeedMin, this.level().purpurConfig.donkeyMovementSpeedMax); -+ } -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.DONKEY_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -index 3e50581033e88e8eddcbd85bfa890cbe0b88a7e6..92339f5a07dcb6bf7eb1bce6d584464ebd8430b5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -@@ -47,6 +47,21 @@ public class Horse extends AbstractHorse implements VariantHolder { - } - // Purpur end - -+ @Override -+ public float generateMaxHealth(RandomSource random) { -+ return (float) generateMaxHealth(this.level().purpurConfig.horseMaxHealthMin, this.level().purpurConfig.horseMaxHealthMax); -+ } -+ -+ @Override -+ public double generateJumpStrength(RandomSource random) { -+ return generateJumpStrength(this.level().purpurConfig.horseJumpStrengthMin, this.level().purpurConfig.horseJumpStrengthMax); -+ } -+ -+ @Override -+ public double generateSpeed(RandomSource random) { -+ return generateSpeed(this.level().purpurConfig.horseMovementSpeedMin, this.level().purpurConfig.horseMovementSpeedMax); -+ } -+ - @Override - protected void randomizeAttributes(RandomSource random) { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index 42d5b435728aa497dc34f0ac984e8637fe96343d..967d1ff37ecc9a2cf81a1a6be8ceeb96b42ff847 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -122,6 +122,21 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -index f83f09f7568c44a1165aac73f7127c5d1af0478f..0f3e6c19e55a18cde71f3cb84c1e92b433ebcb7f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -76,6 +76,13 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - } - // Purpur end - -+ @Override -+ protected void initAttributes() { -+ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.illusionerMovementSpeed); -+ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.illusionerFollowRange); -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.illusionerMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index aad51022eac584fbc058c2b25e2bf1929fccca6f..b472309f97b24f1d7b97d8b6d464c479c2d602d5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -47,6 +47,26 @@ public class MagmaCube extends Slime { - } - // Purpur end - -+ @Override -+ protected String getMaxHealthEquation() { -+ return level().purpurConfig.magmaCubeMaxHealth; -+ } -+ -+ @Override -+ protected String getAttackDamageEquation() { -+ return level().purpurConfig.magmaCubeAttackDamage; -+ } -+ -+ @Override -+ protected java.util.Map getMaxHealthCache() { -+ return level().purpurConfig.magmaCubeMaxHealthCache; -+ } -+ -+ @Override -+ protected java.util.Map getAttackDamageCache() { -+ return level().purpurConfig.magmaCubeAttackDamageCache; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 8c8ff7d291999cc8ab251a145db000871050236f..961300cb8bcc7b0aff476a435aa33e713bd520a6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -150,7 +150,10 @@ public class Phantom extends FlyingMob implements Enemy { - - private void updatePhantomSizeInfo() { - this.refreshDimensions(); -- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) (6 + this.getPhantomSize())); -+ // Purpur start -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomMaxHealth, () -> this.level().purpurConfig.phantomMaxHealthCache, () -> 20.0D)); -+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomAttackDamage, () -> this.level().purpurConfig.phantomAttackDamageCache, () -> (double) 6 + this.getPhantomSize())); -+ // Purpur end - } - - public int getPhantomSize() { -@@ -180,6 +183,21 @@ public class Phantom extends FlyingMob implements Enemy { - return true; - } - -+ private double getFromCache(java.util.function.Supplier equation, java.util.function.Supplier> cache, java.util.function.Supplier defaultValue) { -+ int size = getPhantomSize(); -+ Double value = cache.get().get(size); -+ if (value == null) { -+ try { -+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue(); -+ } catch (javax.script.ScriptException e) { -+ e.printStackTrace(); -+ value = defaultValue.get(); -+ } -+ cache.get().put(size, value); -+ } -+ return value; -+ } -+ - @Override - public void tick() { - super.tick(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -index 31d204d8d81ccc30371070af3678d82dc721618d..6a6349c7002439965422aa4979682b4ce6dfba1e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -79,6 +79,11 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pillagerMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index 8a99c74f1cb6b558421b11e8562ba6082492b2d2..92b14a6056dfbf07852fc0ee0e3eaf7b4c1657a4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -91,6 +91,11 @@ public class Ravager extends Raider { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ravagerMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 3e5548d0489b13b4a1d5c22c9d3bf19ead87928e..7d96ba5adeddf52ed712e320d5f56a37f30e138a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -115,6 +115,11 @@ public class Shulker extends AbstractGolem implements VariantHolder getMaxHealthCache() { -+ return level().purpurConfig.slimeMaxHealthCache; -+ } -+ -+ protected java.util.Map getAttackDamageCache() { -+ return level().purpurConfig.slimeAttackDamageCache; -+ } -+ -+ protected double getFromCache(java.util.function.Supplier equation, java.util.function.Supplier> cache, java.util.function.Supplier defaultValue) { -+ int size = getSize(); -+ Double value = cache.get().get(size); -+ if (value == null) { -+ try { -+ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue(); -+ } catch (javax.script.ScriptException e) { -+ e.printStackTrace(); -+ value = defaultValue.get(); -+ } -+ cache.get().put(size, value); -+ } -+ return value; -+ } - // Purpur end - - @Override -@@ -133,9 +164,9 @@ public class Slime extends Mob implements Enemy { - this.entityData.set(Slime.ID_SIZE, j); - this.reapplyPosition(); - this.refreshDimensions(); -- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double) (j * j)); -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(this::getMaxHealthEquation, this::getMaxHealthCache, () -> (double) size * size)); // Purpur - this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double) (0.2F + 0.1F * (float) j)); -- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) j); -+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(this::getAttackDamageEquation, this::getAttackDamageCache, () -> (double) j)); // Purpur - if (heal) { - this.setHealth(this.getMaxHealth()); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java -index 9f6eaf76a39a9c3e7aed2e0ce91960200b4de664..f302866ff34368c362540fa4163ad38060bf209f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Spider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java -@@ -68,6 +68,11 @@ public class Spider extends Monster { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.spiderMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java -index 137ee6d1aa8ec334009b6fab21b9f30303ace45f..2bfc34d300627c17b2b34442665527c34ebea78a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Stray.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java -@@ -38,6 +38,11 @@ public class Stray extends AbstractSkeleton { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.strayMaxHealth); -+ } -+ - public static boolean checkStraySpawnRules(EntityType type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos blockPos = pos; - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index ccc76bf5431af723eef8e4d39c96daa44d1f6b5c..eedfd4dffdc5586a0b50769b17c5515e5ccac916 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -117,6 +117,11 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.striderMaxHealth); -+ } -+ - public static boolean checkStriderSpawnRules(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java -index 40f42c83d6417258123753ba9315ca27c2b43b22..764fdf4654b4fa2184493b77976fc29aedf592c2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -107,6 +107,11 @@ public class Vex extends Monster implements TraceableEntity { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vexMaxHealth); -+ } -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return dimensions.height - 0.28125F; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index f8337259af80cede27e9a37c5634ccba9f837076..40858ac2a9b58108472748d0ef2c2fd5ef5cfd98 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -75,6 +75,11 @@ public class Vindicator extends AbstractIllager { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vindicatorMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index c7ceeb28b53879395c697cbb3778563511bc1209..289c9261d0019b558c4447a75b3b03229cdd9498 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -74,6 +74,11 @@ public class Witch extends Raider implements RangedAttackMob { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witchMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index 45c7d673cb15fc002538c2713e36f591fe33de92..25e2aca9dafe63ccaf21dc42e79563c5a3c8e638 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -52,6 +52,11 @@ public class WitherSkeleton extends AbstractSkeleton { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherSkeletonMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 393dde215894616c073efd28b529c9dac9815adc..323ec328226e3a46ef8afeaf07178bb9f4042134 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -84,6 +84,11 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zoglinMaxHealth); -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index d3f1bcd9daeea282bef549ea18cedf0fc7a9ad8d..b3a31549179760388bc2958b4339848d1f0b293d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -123,6 +123,11 @@ public class Zombie extends Monster { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -@@ -614,7 +619,7 @@ public class Zombie extends Monster { - } - - protected void randomizeReinforcementsChance() { -- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.10000000149011612D); -+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieSpawnReinforcements); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 87ae149be7a3eaf14406a7df9f174574d77c63c5..c89680f83275169728f923e70a17cb88d4480f01 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -96,6 +96,16 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieVillagerMaxHealth); -+ } -+ -+ @Override -+ protected void randomizeReinforcementsChance() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieVillagerSpawnReinforcements); -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index fe9bdc4006b916748e55a976b6c8621070fb73c5..8f22bfcde4bb8ad73794f2b98b156113e5a2a6c9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -80,6 +80,11 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth); -+ } -+ - @Override - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -@@ -267,7 +272,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - - @Override - protected void randomizeReinforcementsChance() { -- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D); -+ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombifiedPiglinSpawnReinforcements); // Purpur - } - - @Nullable -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 8ba7896a823d60065c1e293d0fcdb2d3ac76da77..66f2695403a04c2e9540bf2ec290bf1cc5a377ca 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -84,6 +84,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.hoglinMaxHealth); -+ } -+ - @Override - public boolean canBeLeashed(Player player) { - return !this.isLeashed(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index c54f70f608093511f9ef1e7e904d9497a4710bdd..e45c061931c5ca03e204b78e60010a906d3ec945 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -113,6 +113,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinMaxHealth); -+ } -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index 2d842c0dfce1c7e7229bd42b2a92c024a4162b68..04e54c241078e6cd6419a21ba1bf913fd3b413d1 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -58,6 +58,11 @@ public class PiglinBrute extends AbstractPiglin { - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinBruteMaxHealth); -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D); - } -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 7b58329067d8debeb34abe801d150038a362ffed..773e9ea9036ecfe48cae481484e9f5e64b6cc29b 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -178,6 +178,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.villagerMaxHealth); -+ } -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 7e5650a56d87a48df7ee0a862b22bf015bb48b2a..33702273c7fabdb8e5bfb3d0e15530f109e318be 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -83,6 +83,11 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - } - // Purpur end - -+ @Override -+ public void initAttributes() { -+ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wanderingTraderMaxHealth); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 4c25461a6d75a47425b66e04285792787d7193ee..27ce55687c7c59fdfdcc4553240ea8b023919b77 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -112,99 +112,190 @@ public class PurpurWorldConfig { - - public boolean axolotlRidable = false; - public boolean axolotlControllable = true; -+ public double axolotlMaxHealth = 14.0D; - private void axolotlSettings() { - axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); - axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); -+ axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth); - } - - public boolean batRidable = false; - public boolean batRidableInWater = true; - public boolean batControllable = true; - public double batMaxY = 320D; -+ public double batMaxHealth = 6.0D; -+ public double batFollowRange = 16.0D; -+ public double batKnockbackResistance = 0.0D; -+ public double batMovementSpeed = 0.6D; -+ public double batFlyingSpeed = 0.6D; -+ public double batArmor = 0.0D; -+ public double batArmorToughness = 0.0D; -+ public double batAttackKnockback = 0.0D; - private void batSettings() { - batRidable = getBoolean("mobs.bat.ridable", batRidable); - batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater); - batControllable = getBoolean("mobs.bat.controllable", batControllable); - batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.bat.attributes.max-health", batMaxHealth); -+ set("mobs.bat.attributes.max-health", null); -+ set("mobs.bat.attributes.max_health", oldValue); -+ } -+ batMaxHealth = getDouble("mobs.bat.attributes.max_health", batMaxHealth); -+ batFollowRange = getDouble("mobs.bat.attributes.follow_range", batFollowRange); -+ batKnockbackResistance = getDouble("mobs.bat.attributes.knockback_resistance", batKnockbackResistance); -+ batMovementSpeed = getDouble("mobs.bat.attributes.movement_speed", batMovementSpeed); -+ batFlyingSpeed = getDouble("mobs.bat.attributes.flying_speed", batFlyingSpeed); -+ batArmor = getDouble("mobs.bat.attributes.armor", batArmor); -+ batArmorToughness = getDouble("mobs.bat.attributes.armor_toughness", batArmorToughness); -+ batAttackKnockback = getDouble("mobs.bat.attributes.attack_knockback", batAttackKnockback); - } - - public boolean beeRidable = false; - public boolean beeRidableInWater = true; - public boolean beeControllable = true; - public double beeMaxY = 320D; -+ public double beeMaxHealth = 10.0D; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); - beeControllable = getBoolean("mobs.bee.controllable", beeControllable); - beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.bee.attributes.max-health", beeMaxHealth); -+ set("mobs.bee.attributes.max-health", null); -+ set("mobs.bee.attributes.max_health", oldValue); -+ } -+ beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth); - } - - public boolean blazeRidable = false; - public boolean blazeRidableInWater = true; - public boolean blazeControllable = true; - public double blazeMaxY = 320D; -+ public double blazeMaxHealth = 20.0D; - private void blazeSettings() { - blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); - blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); - blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable); - blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.blaze.attributes.max-health", blazeMaxHealth); -+ set("mobs.blaze.attributes.max-health", null); -+ set("mobs.blaze.attributes.max_health", oldValue); -+ } -+ blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth); - } - - public boolean camelRidableInWater = false; -+ public double camelMaxHealthMin = 32.0D; -+ public double camelMaxHealthMax = 32.0D; -+ public double camelJumpStrengthMin = 0.42D; -+ public double camelJumpStrengthMax = 0.42D; -+ public double camelMovementSpeedMin = 0.09D; -+ public double camelMovementSpeedMax = 0.09D; - private void camelSettings() { - camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); -+ camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); -+ camelMaxHealthMax = getDouble("mobs.camel.attributes.max_health.max", camelMaxHealthMax); -+ camelJumpStrengthMin = getDouble("mobs.camel.attributes.jump_strength.min", camelJumpStrengthMin); -+ camelJumpStrengthMax = getDouble("mobs.camel.attributes.jump_strength.max", camelJumpStrengthMax); -+ camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin); -+ camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax); - } - - public boolean catRidable = false; - public boolean catRidableInWater = true; - public boolean catControllable = true; -+ public double catMaxHealth = 10.0D; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); - catControllable = getBoolean("mobs.cat.controllable", catControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.cat.attributes.max-health", catMaxHealth); -+ set("mobs.cat.attributes.max-health", null); -+ set("mobs.cat.attributes.max_health", oldValue); -+ } -+ catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth); - } - - public boolean caveSpiderRidable = false; - public boolean caveSpiderRidableInWater = true; - public boolean caveSpiderControllable = true; -+ public double caveSpiderMaxHealth = 12.0D; - private void caveSpiderSettings() { - caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); - caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); - caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.cave_spider.attributes.max-health", caveSpiderMaxHealth); -+ set("mobs.cave_spider.attributes.max-health", null); -+ set("mobs.cave_spider.attributes.max_health", oldValue); -+ } -+ caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth); - } - - public boolean chickenRidable = false; - public boolean chickenRidableInWater = false; - public boolean chickenControllable = true; -+ public double chickenMaxHealth = 4.0D; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); - chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.chicken.attributes.max-health", chickenMaxHealth); -+ set("mobs.chicken.attributes.max-health", null); -+ set("mobs.chicken.attributes.max_health", oldValue); -+ } -+ chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth); - } - - public boolean codRidable = false; - public boolean codControllable = true; -+ public double codMaxHealth = 3.0D; - private void codSettings() { - codRidable = getBoolean("mobs.cod.ridable", codRidable); - codControllable = getBoolean("mobs.cod.controllable", codControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.cod.attributes.max-health", codMaxHealth); -+ set("mobs.cod.attributes.max-health", null); -+ set("mobs.cod.attributes.max_health", oldValue); -+ } -+ codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth); - } - - public boolean cowRidable = false; - public boolean cowRidableInWater = true; - public boolean cowControllable = true; -+ public double cowMaxHealth = 10.0D; - private void cowSettings() { - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); - cowControllable = getBoolean("mobs.cow.controllable", cowControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.cow.attributes.max-health", cowMaxHealth); -+ set("mobs.cow.attributes.max-health", null); -+ set("mobs.cow.attributes.max_health", oldValue); -+ } -+ cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth); - } - - public boolean creeperRidable = false; - public boolean creeperRidableInWater = true; - public boolean creeperControllable = true; -+ public double creeperMaxHealth = 20.0D; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); - creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.creeper.attributes.max-health", creeperMaxHealth); -+ set("mobs.creeper.attributes.max-health", null); -+ set("mobs.creeper.attributes.max_health", oldValue); -+ } -+ creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth); - } - - public boolean dolphinRidable = false; -@@ -212,80 +303,161 @@ public class PurpurWorldConfig { - public int dolphinSpitCooldown = 20; - public float dolphinSpitSpeed = 1.0F; - public float dolphinSpitDamage = 2.0F; -+ public double dolphinMaxHealth = 10.0D; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); - dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown); - dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed); - dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.dolphin.attributes.max-health", dolphinMaxHealth); -+ set("mobs.dolphin.attributes.max-health", null); -+ set("mobs.dolphin.attributes.max_health", oldValue); -+ } -+ dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth); - } - - public boolean donkeyRidableInWater = false; -+ public double donkeyMaxHealthMin = 15.0D; -+ public double donkeyMaxHealthMax = 30.0D; -+ public double donkeyJumpStrengthMin = 0.5D; -+ public double donkeyJumpStrengthMax = 0.5D; -+ public double donkeyMovementSpeedMin = 0.175D; -+ public double donkeyMovementSpeedMax = 0.175D; - private void donkeySettings() { - donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); -+ if (PurpurConfig.version < 10) { -+ double oldMin = getDouble("mobs.donkey.attributes.max-health.min", donkeyMaxHealthMin); -+ double oldMax = getDouble("mobs.donkey.attributes.max-health.max", donkeyMaxHealthMax); -+ set("mobs.donkey.attributes.max-health", null); -+ set("mobs.donkey.attributes.max_health.min", oldMin); -+ set("mobs.donkey.attributes.max_health.max", oldMax); -+ } -+ donkeyMaxHealthMin = getDouble("mobs.donkey.attributes.max_health.min", donkeyMaxHealthMin); -+ donkeyMaxHealthMax = getDouble("mobs.donkey.attributes.max_health.max", donkeyMaxHealthMax); -+ donkeyJumpStrengthMin = getDouble("mobs.donkey.attributes.jump_strength.min", donkeyJumpStrengthMin); -+ donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax); -+ donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin); -+ donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax); - } - - public boolean drownedRidable = false; - public boolean drownedRidableInWater = true; - public boolean drownedControllable = true; -+ public double drownedMaxHealth = 20.0D; -+ public double drownedSpawnReinforcements = 0.1D; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); - drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.drowned.attributes.max-health", drownedMaxHealth); -+ set("mobs.drowned.attributes.max-health", null); -+ set("mobs.drowned.attributes.max_health", oldValue); -+ } -+ drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth); -+ drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements); - } - - public boolean elderGuardianRidable = false; - public boolean elderGuardianControllable = true; -+ public double elderGuardianMaxHealth = 80.0D; - private void elderGuardianSettings() { - elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); - elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.elder_guardian.attributes.max-health", elderGuardianMaxHealth); -+ set("mobs.elder_guardian.attributes.max-health", null); -+ set("mobs.elder_guardian.attributes.max_health", oldValue); -+ } -+ elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth); - } - - public boolean enderDragonRidable = false; - public boolean enderDragonRidableInWater = true; - public boolean enderDragonControllable = true; - public double enderDragonMaxY = 320D; -+ public double enderDragonMaxHealth = 200.0D; - private void enderDragonSettings() { - enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); - enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); - enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable); - enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY); -+ if (PurpurConfig.version < 8) { -+ double oldValue = getDouble("mobs.ender_dragon.max-health", enderDragonMaxHealth); -+ set("mobs.ender_dragon.max-health", null); -+ set("mobs.ender_dragon.attributes.max_health", oldValue); -+ } else if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.ender_dragon.attributes.max-health", enderDragonMaxHealth); -+ set("mobs.ender_dragon.attributes.max-health", null); -+ set("mobs.ender_dragon.attributes.max_health", oldValue); -+ } -+ enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth); - } - - public boolean endermanRidable = false; - public boolean endermanRidableInWater = true; - public boolean endermanControllable = true; -+ public double endermanMaxHealth = 40.0D; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); - endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.enderman.attributes.max-health", endermanMaxHealth); -+ set("mobs.enderman.attributes.max-health", null); -+ set("mobs.enderman.attributes.max_health", oldValue); -+ } -+ endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); - } - - public boolean endermiteRidable = false; - public boolean endermiteRidableInWater = true; - public boolean endermiteControllable = true; -+ public double endermiteMaxHealth = 8.0D; - private void endermiteSettings() { - endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); - endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); - endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.endermite.attributes.max-health", endermiteMaxHealth); -+ set("mobs.endermite.attributes.max-health", null); -+ set("mobs.endermite.attributes.max_health", oldValue); -+ } -+ endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth); - } - - public boolean evokerRidable = false; - public boolean evokerRidableInWater = true; - public boolean evokerControllable = true; -+ public double evokerMaxHealth = 24.0D; - private void evokerSettings() { - evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); - evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); - evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.evoker.attributes.max-health", evokerMaxHealth); -+ set("mobs.evoker.attributes.max-health", null); -+ set("mobs.evoker.attributes.max_health", oldValue); -+ } -+ evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); - } - - public boolean foxRidable = false; - public boolean foxRidableInWater = true; - public boolean foxControllable = true; -+ public double foxMaxHealth = 10.0D; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); - foxControllable = getBoolean("mobs.fox.controllable", foxControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.fox.attributes.max-health", foxMaxHealth); -+ set("mobs.fox.attributes.max-health", null); -+ set("mobs.fox.attributes.max_health", oldValue); -+ } -+ foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth); - } - - public boolean frogRidable = false; -@@ -303,147 +475,316 @@ public class PurpurWorldConfig { - public boolean ghastRidableInWater = true; - public boolean ghastControllable = true; - public double ghastMaxY = 320D; -+ public double ghastMaxHealth = 10.0D; - private void ghastSettings() { - ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); - ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); - ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable); - ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.ghast.attributes.max-health", ghastMaxHealth); -+ set("mobs.ghast.attributes.max-health", null); -+ set("mobs.ghast.attributes.max_health", oldValue); -+ } -+ ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth); - } - - public boolean giantRidable = false; - public boolean giantRidableInWater = true; - public boolean giantControllable = true; -+ public double giantMovementSpeed = 0.5D; -+ public double giantAttackDamage = 50.0D; -+ public double giantMaxHealth = 100.0D; - private void giantSettings() { - giantRidable = getBoolean("mobs.giant.ridable", giantRidable); - giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); - giantControllable = getBoolean("mobs.giant.controllable", giantControllable); -+ giantMovementSpeed = getDouble("mobs.giant.movement-speed", giantMovementSpeed); -+ giantAttackDamage = getDouble("mobs.giant.attack-damage", giantAttackDamage); -+ if (PurpurConfig.version < 8) { -+ double oldValue = getDouble("mobs.giant.max-health", giantMaxHealth); -+ set("mobs.giant.max-health", null); -+ set("mobs.giant.attributes.max_health", oldValue); -+ } else if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.giant.attributes.max-health", giantMaxHealth); -+ set("mobs.giant.attributes.max-health", null); -+ set("mobs.giant.attributes.max_health", oldValue); -+ } -+ giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth); - } - - public boolean glowSquidRidable = false; - public boolean glowSquidControllable = true; -+ public double glowSquidMaxHealth = 10.0D; - private void glowSquidSettings() { - glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); - glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); -+ glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth); - } - - public boolean goatRidable = false; - public boolean goatRidableInWater = true; - public boolean goatControllable = true; -+ public double goatMaxHealth = 10.0D; - private void goatSettings() { - goatRidable = getBoolean("mobs.goat.ridable", goatRidable); - goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); - goatControllable = getBoolean("mobs.goat.controllable", goatControllable); -+ goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth); - } - - public boolean guardianRidable = false; - public boolean guardianControllable = true; -+ public double guardianMaxHealth = 30.0D; - private void guardianSettings() { - guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); - guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.guardian.attributes.max-health", guardianMaxHealth); -+ set("mobs.guardian.attributes.max-health", null); -+ set("mobs.guardian.attributes.max_health", oldValue); -+ } -+ guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth); - } - - public boolean hoglinRidable = false; - public boolean hoglinRidableInWater = true; - public boolean hoglinControllable = true; -+ public double hoglinMaxHealth = 40.0D; - private void hoglinSettings() { - hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); - hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); - hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.hoglin.attributes.max-health", hoglinMaxHealth); -+ set("mobs.hoglin.attributes.max-health", null); -+ set("mobs.hoglin.attributes.max_health", oldValue); -+ } -+ hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth); - } - - public boolean horseRidableInWater = false; -+ public double horseMaxHealthMin = 15.0D; -+ public double horseMaxHealthMax = 30.0D; -+ public double horseJumpStrengthMin = 0.4D; -+ public double horseJumpStrengthMax = 1.0D; -+ public double horseMovementSpeedMin = 0.1125D; -+ public double horseMovementSpeedMax = 0.3375D; - private void horseSettings() { - horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); -+ if (PurpurConfig.version < 10) { -+ double oldMin = getDouble("mobs.horse.attributes.max-health.min", horseMaxHealthMin); -+ double oldMax = getDouble("mobs.horse.attributes.max-health.max", horseMaxHealthMax); -+ set("mobs.horse.attributes.max-health", null); -+ set("mobs.horse.attributes.max_health.min", oldMin); -+ set("mobs.horse.attributes.max_health.max", oldMax); -+ } -+ horseMaxHealthMin = getDouble("mobs.horse.attributes.max_health.min", horseMaxHealthMin); -+ horseMaxHealthMax = getDouble("mobs.horse.attributes.max_health.max", horseMaxHealthMax); -+ horseJumpStrengthMin = getDouble("mobs.horse.attributes.jump_strength.min", horseJumpStrengthMin); -+ horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax); -+ horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin); -+ horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax); - } - - public boolean huskRidable = false; - public boolean huskRidableInWater = true; - public boolean huskControllable = true; -+ public double huskMaxHealth = 20.0D; -+ public double huskSpawnReinforcements = 0.1D; - private void huskSettings() { - huskRidable = getBoolean("mobs.husk.ridable", huskRidable); - huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); - huskControllable = getBoolean("mobs.husk.controllable", huskControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.husk.attributes.max-health", huskMaxHealth); -+ set("mobs.husk.attributes.max-health", null); -+ set("mobs.husk.attributes.max_health", oldValue); -+ } -+ huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth); -+ huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements); - } - - public boolean illusionerRidable = false; - public boolean illusionerRidableInWater = true; - public boolean illusionerControllable = true; -+ public double illusionerMovementSpeed = 0.5D; -+ public double illusionerFollowRange = 18.0D; -+ public double illusionerMaxHealth = 32.0D; - private void illusionerSettings() { - illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); - illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); - illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable); -+ illusionerMovementSpeed = getDouble("mobs.illusioner.movement-speed", illusionerMovementSpeed); -+ illusionerFollowRange = getDouble("mobs.illusioner.follow-range", illusionerFollowRange); -+ if (PurpurConfig.version < 8) { -+ double oldValue = getDouble("mobs.illusioner.max-health", illusionerMaxHealth); -+ set("mobs.illusioner.max-health", null); -+ set("mobs.illusioner.attributes.max_health", oldValue); -+ } else if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.illusioner.attributes.max-health", illusionerMaxHealth); -+ set("mobs.illusioner.attributes.max-health", null); -+ set("mobs.illusioner.attributes.max_health", oldValue); -+ } -+ illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth); - } - - public boolean ironGolemRidable = false; - public boolean ironGolemRidableInWater = true; - public boolean ironGolemControllable = true; - public boolean ironGolemCanSwim = false; -+ public double ironGolemMaxHealth = 100.0D; - private void ironGolemSettings() { - ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); - ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); - ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable); - ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.iron_golem.attributes.max-health", ironGolemMaxHealth); -+ set("mobs.iron_golem.attributes.max-health", null); -+ set("mobs.iron_golem.attributes.max_health", oldValue); -+ } -+ ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth); - } - - public boolean llamaRidable = false; - public boolean llamaRidableInWater = false; - public boolean llamaControllable = true; -+ public double llamaMaxHealthMin = 15.0D; -+ public double llamaMaxHealthMax = 30.0D; -+ public double llamaJumpStrengthMin = 0.5D; -+ public double llamaJumpStrengthMax = 0.5D; -+ public double llamaMovementSpeedMin = 0.175D; -+ public double llamaMovementSpeedMax = 0.175D; - private void llamaSettings() { - llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); - llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); - llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable); -+ if (PurpurConfig.version < 10) { -+ double oldMin = getDouble("mobs.llama.attributes.max-health.min", llamaMaxHealthMin); -+ double oldMax = getDouble("mobs.llama.attributes.max-health.max", llamaMaxHealthMax); -+ set("mobs.llama.attributes.max-health", null); -+ set("mobs.llama.attributes.max_health.min", oldMin); -+ set("mobs.llama.attributes.max_health.max", oldMax); -+ } -+ llamaMaxHealthMin = getDouble("mobs.llama.attributes.max_health.min", llamaMaxHealthMin); -+ llamaMaxHealthMax = getDouble("mobs.llama.attributes.max_health.max", llamaMaxHealthMax); -+ llamaJumpStrengthMin = getDouble("mobs.llama.attributes.jump_strength.min", llamaJumpStrengthMin); -+ llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax); -+ llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin); -+ llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax); - } - - public boolean magmaCubeRidable = false; - public boolean magmaCubeRidableInWater = true; - public boolean magmaCubeControllable = true; -+ public String magmaCubeMaxHealth = "size * size"; -+ public String magmaCubeAttackDamage = "size"; -+ public Map magmaCubeMaxHealthCache = new HashMap<>(); -+ public Map magmaCubeAttackDamageCache = new HashMap<>(); - private void magmaCubeSettings() { - magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable); - magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater); - magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable); -+ if (PurpurConfig.version < 10) { -+ String oldValue = getString("mobs.magma_cube.attributes.max-health", magmaCubeMaxHealth); -+ set("mobs.magma_cube.attributes.max-health", null); -+ set("mobs.magma_cube.attributes.max_health", oldValue); -+ } -+ magmaCubeMaxHealth = getString("mobs.magma_cube.attributes.max_health", magmaCubeMaxHealth); -+ magmaCubeAttackDamage = getString("mobs.magma_cube.attributes.attack_damage", magmaCubeAttackDamage); -+ magmaCubeMaxHealthCache.clear(); -+ magmaCubeAttackDamageCache.clear(); - } - - public boolean mooshroomRidable = false; - public boolean mooshroomRidableInWater = true; - public boolean mooshroomControllable = true; -+ public double mooshroomMaxHealth = 10.0D; - private void mooshroomSettings() { - mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); - mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); - mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.mooshroom.attributes.max-health", mooshroomMaxHealth); -+ set("mobs.mooshroom.attributes.max-health", null); -+ set("mobs.mooshroom.attributes.max_health", oldValue); -+ } -+ mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth); - } - - public boolean muleRidableInWater = false; -+ public double muleMaxHealthMin = 15.0D; -+ public double muleMaxHealthMax = 30.0D; -+ public double muleJumpStrengthMin = 0.5D; -+ public double muleJumpStrengthMax = 0.5D; -+ public double muleMovementSpeedMin = 0.175D; -+ public double muleMovementSpeedMax = 0.175D; - private void muleSettings() { - muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); -+ if (PurpurConfig.version < 10) { -+ double oldMin = getDouble("mobs.mule.attributes.max-health.min", muleMaxHealthMin); -+ double oldMax = getDouble("mobs.mule.attributes.max-health.max", muleMaxHealthMax); -+ set("mobs.mule.attributes.max-health", null); -+ set("mobs.mule.attributes.max_health.min", oldMin); -+ set("mobs.mule.attributes.max_health.max", oldMax); -+ } -+ muleMaxHealthMin = getDouble("mobs.mule.attributes.max_health.min", muleMaxHealthMin); -+ muleMaxHealthMax = getDouble("mobs.mule.attributes.max_health.max", muleMaxHealthMax); -+ muleJumpStrengthMin = getDouble("mobs.mule.attributes.jump_strength.min", muleJumpStrengthMin); -+ muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax); -+ muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin); -+ muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax); - } - - public boolean ocelotRidable = false; - public boolean ocelotRidableInWater = true; - public boolean ocelotControllable = true; -+ public double ocelotMaxHealth = 10.0D; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); - ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.ocelot.attributes.max-health", ocelotMaxHealth); -+ set("mobs.ocelot.attributes.max-health", null); -+ set("mobs.ocelot.attributes.max_health", oldValue); -+ } -+ ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth); - } - - public boolean pandaRidable = false; - public boolean pandaRidableInWater = true; - public boolean pandaControllable = true; -+ public double pandaMaxHealth = 20.0D; - private void pandaSettings() { - pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); - pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); - pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.panda.attributes.max-health", pandaMaxHealth); -+ set("mobs.panda.attributes.max-health", null); -+ set("mobs.panda.attributes.max_health", oldValue); -+ } -+ pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth); - } - - public boolean parrotRidable = false; - public boolean parrotRidableInWater = true; - public boolean parrotControllable = true; - public double parrotMaxY = 320D; -+ public double parrotMaxHealth = 6.0D; - private void parrotSettings() { - parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); - parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); - parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable); - parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.parrot.attributes.max-health", parrotMaxHealth); -+ set("mobs.parrot.attributes.max-health", null); -+ set("mobs.parrot.attributes.max_health", oldValue); -+ } -+ parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth); - } - - public boolean phantomRidable = false; -@@ -453,6 +794,10 @@ public class PurpurWorldConfig { - public float phantomFlameDamage = 1.0F; - public int phantomFlameFireTime = 8; - public boolean phantomAllowGriefing = false; -+ public String phantomMaxHealth = "20.0"; -+ public String phantomAttackDamage = "6 + size"; -+ public Map phantomMaxHealthCache = new HashMap<>(); -+ public Map phantomAttackDamageCache = new HashMap<>(); - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -461,189 +806,361 @@ public class PurpurWorldConfig { - phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage); - phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime); - phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.phantom.attributes.max-health", Double.parseDouble(phantomMaxHealth)); -+ set("mobs.phantom.attributes.max-health", null); -+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue)); -+ } -+ if (PurpurConfig.version < 25) { -+ double oldValue = getDouble("mobs.phantom.attributes.max_health", Double.parseDouble(phantomMaxHealth)); -+ set("mobs.phantom.attributes.max_health", String.valueOf(oldValue)); -+ } -+ phantomMaxHealth = getString("mobs.phantom.attributes.max_health", phantomMaxHealth); -+ phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage); -+ phantomMaxHealthCache.clear(); -+ phantomAttackDamageCache.clear(); - } - - public boolean pigRidable = false; - public boolean pigRidableInWater = false; - public boolean pigControllable = true; -+ public double pigMaxHealth = 10.0D; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); - pigControllable = getBoolean("mobs.pig.controllable", pigControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.pig.attributes.max-health", pigMaxHealth); -+ set("mobs.pig.attributes.max-health", null); -+ set("mobs.pig.attributes.max_health", oldValue); -+ } -+ pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth); - } - - public boolean piglinRidable = false; - public boolean piglinRidableInWater = true; - public boolean piglinControllable = true; -+ public double piglinMaxHealth = 16.0D; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); - piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.piglin.attributes.max-health", piglinMaxHealth); -+ set("mobs.piglin.attributes.max-health", null); -+ set("mobs.piglin.attributes.max_health", oldValue); -+ } -+ piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); - } - - public boolean piglinBruteRidable = false; - public boolean piglinBruteRidableInWater = true; - public boolean piglinBruteControllable = true; -+ public double piglinBruteMaxHealth = 50.0D; - private void piglinBruteSettings() { - piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); - piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); - piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.piglin_brute.attributes.max-health", piglinBruteMaxHealth); -+ set("mobs.piglin_brute.attributes.max-health", null); -+ set("mobs.piglin_brute.attributes.max_health", oldValue); -+ } -+ piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth); - } - - public boolean pillagerRidable = false; - public boolean pillagerRidableInWater = true; - public boolean pillagerControllable = true; -+ public double pillagerMaxHealth = 24.0D; - private void pillagerSettings() { - pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); - pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); - pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.pillager.attributes.max-health", pillagerMaxHealth); -+ set("mobs.pillager.attributes.max-health", null); -+ set("mobs.pillager.attributes.max_health", oldValue); -+ } -+ pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); - } - - public boolean polarBearRidable = false; - public boolean polarBearRidableInWater = true; - public boolean polarBearControllable = true; -+ public double polarBearMaxHealth = 30.0D; - private void polarBearSettings() { - polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); - polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); - polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.polar_bear.attributes.max-health", polarBearMaxHealth); -+ set("mobs.polar_bear.attributes.max-health", null); -+ set("mobs.polar_bear.attributes.max_health", oldValue); -+ } -+ polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth); - } - - public boolean pufferfishRidable = false; - public boolean pufferfishControllable = true; -+ public double pufferfishMaxHealth = 3.0D; - private void pufferfishSettings() { - pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); - pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.pufferfish.attributes.max-health", pufferfishMaxHealth); -+ set("mobs.pufferfish.attributes.max-health", null); -+ set("mobs.pufferfish.attributes.max_health", oldValue); -+ } -+ pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth); - } - - public boolean rabbitRidable = false; - public boolean rabbitRidableInWater = true; - public boolean rabbitControllable = true; -+ public double rabbitMaxHealth = 3.0D; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); - rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.rabbit.attributes.max-health", rabbitMaxHealth); -+ set("mobs.rabbit.attributes.max-health", null); -+ set("mobs.rabbit.attributes.max_health", oldValue); -+ } -+ rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth); - } - - public boolean ravagerRidable = false; - public boolean ravagerRidableInWater = false; - public boolean ravagerControllable = true; -+ public double ravagerMaxHealth = 100.0D; - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); - ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.ravager.attributes.max-health", ravagerMaxHealth); -+ set("mobs.ravager.attributes.max-health", null); -+ set("mobs.ravager.attributes.max_health", oldValue); -+ } -+ ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); - } - - public boolean salmonRidable = false; - public boolean salmonControllable = true; -+ public double salmonMaxHealth = 3.0D; - private void salmonSettings() { - salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); - salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.salmon.attributes.max-health", salmonMaxHealth); -+ set("mobs.salmon.attributes.max-health", null); -+ set("mobs.salmon.attributes.max_health", oldValue); -+ } -+ salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth); - } - - public boolean sheepRidable = false; - public boolean sheepRidableInWater = true; - public boolean sheepControllable = true; -+ public double sheepMaxHealth = 8.0D; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); - sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.sheep.attributes.max-health", sheepMaxHealth); -+ set("mobs.sheep.attributes.max-health", null); -+ set("mobs.sheep.attributes.max_health", oldValue); -+ } -+ sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth); - } - - public boolean shulkerRidable = false; - public boolean shulkerRidableInWater = true; - public boolean shulkerControllable = true; -+ public double shulkerMaxHealth = 30.0D; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); - shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.shulker.attributes.max-health", shulkerMaxHealth); -+ set("mobs.shulker.attributes.max-health", null); -+ set("mobs.shulker.attributes.max_health", oldValue); -+ } -+ shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth); - } - - public boolean silverfishRidable = false; - public boolean silverfishRidableInWater = true; - public boolean silverfishControllable = true; -+ public double silverfishMaxHealth = 8.0D; - private void silverfishSettings() { - silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); - silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); - silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.silverfish.attributes.max-health", silverfishMaxHealth); -+ set("mobs.silverfish.attributes.max-health", null); -+ set("mobs.silverfish.attributes.max_health", oldValue); -+ } -+ silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth); - } - - public boolean skeletonRidable = false; - public boolean skeletonRidableInWater = true; - public boolean skeletonControllable = true; -+ public double skeletonMaxHealth = 20.0D; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); - skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.skeleton.attributes.max-health", skeletonMaxHealth); -+ set("mobs.skeleton.attributes.max-health", null); -+ set("mobs.skeleton.attributes.max_health", oldValue); -+ } -+ skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth); - } - - public boolean skeletonHorseRidableInWater = true; - public boolean skeletonHorseCanSwim = false; -+ public double skeletonHorseMaxHealthMin = 15.0D; -+ public double skeletonHorseMaxHealthMax = 15.0D; -+ public double skeletonHorseJumpStrengthMin = 0.4D; -+ public double skeletonHorseJumpStrengthMax = 1.0D; -+ public double skeletonHorseMovementSpeedMin = 0.2D; -+ public double skeletonHorseMovementSpeedMax = 0.2D; - private void skeletonHorseSettings() { - skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); - skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.skeleton_horse.attributes.max-health", skeletonHorseMaxHealthMin); -+ set("mobs.skeleton_horse.attributes.max-health", null); -+ set("mobs.skeleton_horse.attributes.max_health.min", oldValue); -+ set("mobs.skeleton_horse.attributes.max_health.max", oldValue); -+ } -+ skeletonHorseMaxHealthMin = getDouble("mobs.skeleton_horse.attributes.max_health.min", skeletonHorseMaxHealthMin); -+ skeletonHorseMaxHealthMax = getDouble("mobs.skeleton_horse.attributes.max_health.max", skeletonHorseMaxHealthMax); -+ skeletonHorseJumpStrengthMin = getDouble("mobs.skeleton_horse.attributes.jump_strength.min", skeletonHorseJumpStrengthMin); -+ skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax); -+ skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin); -+ skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax); - } - - public boolean slimeRidable = false; - public boolean slimeRidableInWater = true; - public boolean slimeControllable = true; -+ public String slimeMaxHealth = "size * size"; -+ public String slimeAttackDamage = "size"; -+ public Map slimeMaxHealthCache = new HashMap<>(); -+ public Map slimeAttackDamageCache = new HashMap<>(); - private void slimeSettings() { - slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable); - slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater); - slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable); -+ if (PurpurConfig.version < 10) { -+ String oldValue = getString("mobs.slime.attributes.max-health", slimeMaxHealth); -+ set("mobs.slime.attributes.max-health", null); -+ set("mobs.slime.attributes.max_health", oldValue); -+ } -+ slimeMaxHealth = getString("mobs.slime.attributes.max_health", slimeMaxHealth); -+ slimeAttackDamage = getString("mobs.slime.attributes.attack_damage", slimeAttackDamage); -+ slimeMaxHealthCache.clear(); -+ slimeAttackDamageCache.clear(); - } - - public boolean snowGolemRidable = false; - public boolean snowGolemRidableInWater = true; - public boolean snowGolemControllable = true; - public boolean snowGolemLeaveTrailWhenRidden = false; -+ public double snowGolemMaxHealth = 4.0D; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); - snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable); - snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.snow_golem.attributes.max-health", snowGolemMaxHealth); -+ set("mobs.snow_golem.attributes.max-health", null); -+ set("mobs.snow_golem.attributes.max_health", oldValue); -+ } -+ snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth); - } - - public boolean snifferRidable = false; - public boolean snifferRidableInWater = true; - public boolean snifferControllable = true; -+ public double snifferMaxHealth = 14.0D; - private void snifferSettings() { - snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); - snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); - snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable); -+ snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth); - } - - public boolean squidRidable = false; - public boolean squidControllable = true; -+ public double squidMaxHealth = 10.0D; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.squid.attributes.max-health", squidMaxHealth); -+ set("mobs.squid.attributes.max-health", null); -+ set("mobs.squid.attributes.max_health", oldValue); -+ } -+ squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth); - } - - public boolean spiderRidable = false; - public boolean spiderRidableInWater = false; - public boolean spiderControllable = true; -+ public double spiderMaxHealth = 16.0D; - private void spiderSettings() { - spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); - spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); - spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.spider.attributes.max-health", spiderMaxHealth); -+ set("mobs.spider.attributes.max-health", null); -+ set("mobs.spider.attributes.max_health", oldValue); -+ } -+ spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth); - } - - public boolean strayRidable = false; - public boolean strayRidableInWater = true; - public boolean strayControllable = true; -+ public double strayMaxHealth = 20.0D; - private void straySettings() { - strayRidable = getBoolean("mobs.stray.ridable", strayRidable); - strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); - strayControllable = getBoolean("mobs.stray.controllable", strayControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.stray.attributes.max-health", strayMaxHealth); -+ set("mobs.stray.attributes.max-health", null); -+ set("mobs.stray.attributes.max_health", oldValue); -+ } -+ strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth); - } - - public boolean striderRidable = false; - public boolean striderRidableInWater = false; - public boolean striderControllable = true; -+ public double striderMaxHealth = 20.0D; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); - striderControllable = getBoolean("mobs.strider.controllable", striderControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.strider.attributes.max-health", striderMaxHealth); -+ set("mobs.strider.attributes.max-health", null); -+ set("mobs.strider.attributes.max_health", oldValue); -+ } -+ striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth); - } - - public boolean tadpoleRidable = false; -@@ -658,64 +1175,125 @@ public class PurpurWorldConfig { - public boolean traderLlamaRidable = false; - public boolean traderLlamaRidableInWater = false; - public boolean traderLlamaControllable = true; -+ public double traderLlamaMaxHealthMin = 15.0D; -+ public double traderLlamaMaxHealthMax = 30.0D; -+ public double traderLlamaJumpStrengthMin = 0.5D; -+ public double traderLlamaJumpStrengthMax = 0.5D; -+ public double traderLlamaMovementSpeedMin = 0.175D; -+ public double traderLlamaMovementSpeedMax = 0.175D; - private void traderLlamaSettings() { - traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); - traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); - traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable); -+ if (PurpurConfig.version < 10) { -+ double oldMin = getDouble("mobs.trader_llama.attributes.max-health.min", traderLlamaMaxHealthMin); -+ double oldMax = getDouble("mobs.trader_llama.attributes.max-health.max", traderLlamaMaxHealthMax); -+ set("mobs.trader_llama.attributes.max-health", null); -+ set("mobs.trader_llama.attributes.max_health.min", oldMin); -+ set("mobs.trader_llama.attributes.max_health.max", oldMax); -+ } -+ traderLlamaMaxHealthMin = getDouble("mobs.trader_llama.attributes.max_health.min", traderLlamaMaxHealthMin); -+ traderLlamaMaxHealthMax = getDouble("mobs.trader_llama.attributes.max_health.max", traderLlamaMaxHealthMax); -+ traderLlamaJumpStrengthMin = getDouble("mobs.trader_llama.attributes.jump_strength.min", traderLlamaJumpStrengthMin); -+ traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax); -+ traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin); -+ traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax); - } - - public boolean tropicalFishRidable = false; - public boolean tropicalFishControllable = true; -+ public double tropicalFishMaxHealth = 3.0D; - private void tropicalFishSettings() { - tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); - tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.tropical_fish.attributes.max-health", tropicalFishMaxHealth); -+ set("mobs.tropical_fish.attributes.max-health", null); -+ set("mobs.tropical_fish.attributes.max_health", oldValue); -+ } -+ tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth); - } - - public boolean turtleRidable = false; - public boolean turtleRidableInWater = true; - public boolean turtleControllable = true; -+ public double turtleMaxHealth = 30.0D; - private void turtleSettings() { - turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); - turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); - turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.turtle.attributes.max-health", turtleMaxHealth); -+ set("mobs.turtle.attributes.max-health", null); -+ set("mobs.turtle.attributes.max_health", oldValue); -+ } -+ turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth); - } - - public boolean vexRidable = false; - public boolean vexRidableInWater = true; - public boolean vexControllable = true; - public double vexMaxY = 320D; -+ public double vexMaxHealth = 14.0D; - private void vexSettings() { - vexRidable = getBoolean("mobs.vex.ridable", vexRidable); - vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); - vexControllable = getBoolean("mobs.vex.controllable", vexControllable); - vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.vex.attributes.max-health", vexMaxHealth); -+ set("mobs.vex.attributes.max-health", null); -+ set("mobs.vex.attributes.max_health", oldValue); -+ } -+ vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth); - } - - public boolean villagerRidable = false; - public boolean villagerRidableInWater = true; - public boolean villagerControllable = true; -+ public double villagerMaxHealth = 20.0D; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); - villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.villager.attributes.max-health", villagerMaxHealth); -+ set("mobs.villager.attributes.max-health", null); -+ set("mobs.villager.attributes.max_health", oldValue); -+ } -+ villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth); - } - - public boolean vindicatorRidable = false; - public boolean vindicatorRidableInWater = true; - public boolean vindicatorControllable = true; -+ public double vindicatorMaxHealth = 24.0D; - private void vindicatorSettings() { - vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); - vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); - vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.vindicator.attributes.max-health", vindicatorMaxHealth); -+ set("mobs.vindicator.attributes.max-health", null); -+ set("mobs.vindicator.attributes.max_health", oldValue); -+ } -+ vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth); - } - - public boolean wanderingTraderRidable = false; - public boolean wanderingTraderRidableInWater = true; - public boolean wanderingTraderControllable = true; -+ public double wanderingTraderMaxHealth = 20.0D; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); - wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.wandering_trader.attributes.max-health", wanderingTraderMaxHealth); -+ set("mobs.wandering_trader.attributes.max-health", null); -+ set("mobs.wandering_trader.attributes.max_health", oldValue); -+ } -+ wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth); - } - - public boolean wardenRidable = false; -@@ -730,81 +1308,165 @@ public class PurpurWorldConfig { - public boolean witchRidable = false; - public boolean witchRidableInWater = true; - public boolean witchControllable = true; -+ public double witchMaxHealth = 26.0D; - private void witchSettings() { - witchRidable = getBoolean("mobs.witch.ridable", witchRidable); - witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); - witchControllable = getBoolean("mobs.witch.controllable", witchControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.witch.attributes.max-health", witchMaxHealth); -+ set("mobs.witch.attributes.max-health", null); -+ set("mobs.witch.attributes.max_health", oldValue); -+ } -+ witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth); - } - - public boolean witherRidable = false; - public boolean witherRidableInWater = true; - public boolean witherControllable = true; - public double witherMaxY = 320D; -+ public double witherMaxHealth = 300.0D; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); - witherControllable = getBoolean("mobs.wither.controllable", witherControllable); - witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY); -+ if (PurpurConfig.version < 8) { -+ double oldValue = getDouble("mobs.wither.max-health", witherMaxHealth); -+ set("mobs.wither.max_health", null); -+ set("mobs.wither.attributes.max-health", oldValue); -+ } else if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.wither.attributes.max-health", witherMaxHealth); -+ set("mobs.wither.attributes.max-health", null); -+ set("mobs.wither.attributes.max_health", oldValue); -+ } -+ witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth); - } - - public boolean witherSkeletonRidable = false; - public boolean witherSkeletonRidableInWater = true; - public boolean witherSkeletonControllable = true; -+ public double witherSkeletonMaxHealth = 20.0D; - private void witherSkeletonSettings() { - witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); - witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); - witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.wither_skeleton.attributes.max-health", witherSkeletonMaxHealth); -+ set("mobs.wither_skeleton.attributes.max-health", null); -+ set("mobs.wither_skeleton.attributes.max_health", oldValue); -+ } -+ witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth); - } - - public boolean wolfRidable = false; - public boolean wolfRidableInWater = true; - public boolean wolfControllable = true; -+ public double wolfMaxHealth = 8.0D; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); - wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); - wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.wolf.attributes.max-health", wolfMaxHealth); -+ set("mobs.wolf.attributes.max-health", null); -+ set("mobs.wolf.attributes.max_health", oldValue); -+ } -+ wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); - } - - public boolean zoglinRidable = false; - public boolean zoglinRidableInWater = true; - public boolean zoglinControllable = true; -+ public double zoglinMaxHealth = 40.0D; - private void zoglinSettings() { - zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); - zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); - zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.zoglin.attributes.max-health", zoglinMaxHealth); -+ set("mobs.zoglin.attributes.max-health", null); -+ set("mobs.zoglin.attributes.max_health", oldValue); -+ } -+ zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth); - } - - public boolean zombieRidable = false; - public boolean zombieRidableInWater = true; - public boolean zombieControllable = true; -+ public double zombieMaxHealth = 20.0D; -+ public double zombieSpawnReinforcements = 0.1D; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); - zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.zombie.attributes.max-health", zombieMaxHealth); -+ set("mobs.zombie.attributes.max-health", null); -+ set("mobs.zombie.attributes.max_health", oldValue); -+ } -+ zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth); -+ zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements); - } - - public boolean zombieHorseRidableInWater = false; - public boolean zombieHorseCanSwim = false; -+ public double zombieHorseMaxHealthMin = 15.0D; -+ public double zombieHorseMaxHealthMax = 15.0D; -+ public double zombieHorseJumpStrengthMin = 0.4D; -+ public double zombieHorseJumpStrengthMax = 1.0D; -+ public double zombieHorseMovementSpeedMin = 0.2D; -+ public double zombieHorseMovementSpeedMax = 0.2D; - private void zombieHorseSettings() { - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); - zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.zombie_horse.attributes.max-health", zombieHorseMaxHealthMin); -+ set("mobs.zombie_horse.attributes.max-health", null); -+ set("mobs.zombie_horse.attributes.max_health.min", oldValue); -+ set("mobs.zombie_horse.attributes.max_health.max", oldValue); -+ } -+ zombieHorseMaxHealthMin = getDouble("mobs.zombie_horse.attributes.max_health.min", zombieHorseMaxHealthMin); -+ zombieHorseMaxHealthMax = getDouble("mobs.zombie_horse.attributes.max_health.max", zombieHorseMaxHealthMax); -+ zombieHorseJumpStrengthMin = getDouble("mobs.zombie_horse.attributes.jump_strength.min", zombieHorseJumpStrengthMin); -+ zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax); -+ zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin); -+ zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax); - } - - public boolean zombieVillagerRidable = false; - public boolean zombieVillagerRidableInWater = true; - public boolean zombieVillagerControllable = true; -+ public double zombieVillagerMaxHealth = 20.0D; -+ public double zombieVillagerSpawnReinforcements = 0.1D; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); - zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.zombie_villager.attributes.max-health", zombieVillagerMaxHealth); -+ set("mobs.zombie_villager.attributes.max-health", null); -+ set("mobs.zombie_villager.attributes.max_health", oldValue); -+ } -+ zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth); -+ zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements); - } - - public boolean zombifiedPiglinRidable = false; - public boolean zombifiedPiglinRidableInWater = true; - public boolean zombifiedPiglinControllable = true; -+ public double zombifiedPiglinMaxHealth = 20.0D; -+ public double zombifiedPiglinSpawnReinforcements = 0.0D; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); - zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable); -+ if (PurpurConfig.version < 10) { -+ double oldValue = getDouble("mobs.zombified_piglin.attributes.max-health", zombifiedPiglinMaxHealth); -+ set("mobs.zombified_piglin.attributes.max-health", null); -+ set("mobs.zombified_piglin.attributes.max_health", oldValue); -+ } -+ zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth); -+ zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements); - } - } diff --git a/patches/server/0010-Barrels-and-enderchests-6-rows.patch b/patches/server/0010-Barrels-and-enderchests-6-rows.patch deleted file mode 100644 index 14ac1755b..000000000 --- a/patches/server/0010-Barrels-and-enderchests-6-rows.patch +++ /dev/null @@ -1,288 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 23 May 2019 21:50:37 -0500 -Subject: [PATCH] Barrels and enderchests 6 rows - - -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 78636bf26ac8efaffd8a8f4b5bbc703d6b670b7c..42801a26b9d5f8af2d0ce1fef864bb031cfa8965 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1170,6 +1170,27 @@ public abstract class PlayerList { - player.getBukkitEntity().recalculatePermissions(); // CraftBukkit - this.server.getCommands().sendCommands(player); - } // Paper -+ -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) { -+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkit = player.getBukkitEntity(); -+ if (bukkit.hasPermission("purpur.enderchest.rows.six")) { -+ player.sixRowEnderchestSlotCount = 54; -+ } else if (bukkit.hasPermission("purpur.enderchest.rows.five")) { -+ player.sixRowEnderchestSlotCount = 45; -+ } else if (bukkit.hasPermission("purpur.enderchest.rows.four")) { -+ player.sixRowEnderchestSlotCount = 36; -+ } else if (bukkit.hasPermission("purpur.enderchest.rows.three")) { -+ player.sixRowEnderchestSlotCount = 27; -+ } else if (bukkit.hasPermission("purpur.enderchest.rows.two")) { -+ player.sixRowEnderchestSlotCount = 18; -+ } else if (bukkit.hasPermission("purpur.enderchest.rows.one")) { -+ player.sixRowEnderchestSlotCount = 9; -+ } -+ } else { -+ player.sixRowEnderchestSlotCount = -1; -+ } -+ //Purpur end - } - - public boolean isWhiteListed(GameProfile profile) { -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index cf008b8c7a2eb0c642fd9c2da376df86dcd588e2..96726f06b83193fd7c5156efe6411b10eb1db435 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -187,6 +187,7 @@ public abstract class Player extends LivingEntity { - public boolean affectsSpawning = true; - public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; - // Paper end -+ public int sixRowEnderchestSlotCount = -1; // Purpur - - // CraftBukkit start - public boolean fauxSleeping; -diff --git a/src/main/java/net/minecraft/world/inventory/ChestMenu.java b/src/main/java/net/minecraft/world/inventory/ChestMenu.java -index 0dbfd23bbfc6ad203f048142f8c90ef741849fe1..9a80427d2bb470b6b1638e59aba57216676dcbd2 100644 ---- a/src/main/java/net/minecraft/world/inventory/ChestMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/ChestMenu.java -@@ -67,10 +67,30 @@ public class ChestMenu extends AbstractContainerMenu { - return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, 6); - } - -+ // Purpur start -+ public static ChestMenu oneRow(int syncId, Inventory playerInventory, Container inventory) { -+ return new ChestMenu(MenuType.GENERIC_9x1, syncId, playerInventory, inventory, 1); -+ } -+ -+ public static ChestMenu twoRows(int syncId, Inventory playerInventory, Container inventory) { -+ return new ChestMenu(MenuType.GENERIC_9x2, syncId, playerInventory, inventory, 2); -+ } -+ // Purpur end -+ - public static ChestMenu threeRows(int syncId, Inventory playerInventory, Container inventory) { - return new ChestMenu(MenuType.GENERIC_9x3, syncId, playerInventory, inventory, 3); - } - -+ // Purpur start -+ public static ChestMenu fourRows(int syncId, Inventory playerInventory, Container inventory) { -+ return new ChestMenu(MenuType.GENERIC_9x4, syncId, playerInventory, inventory, 4); -+ } -+ -+ public static ChestMenu fiveRows(int syncId, Inventory playerInventory, Container inventory) { -+ return new ChestMenu(MenuType.GENERIC_9x5, syncId, playerInventory, inventory, 5); -+ } -+ // Purpur end -+ - public static ChestMenu sixRows(int syncId, Inventory playerInventory, Container inventory) { - return new ChestMenu(MenuType.GENERIC_9x6, syncId, playerInventory, inventory, 6); - } -diff --git a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java -index 4703f23316f82a1a942907b46d2d6dcb7d70ec37..162798f57a05b78121fa6c4fadf5adee80fbe221 100644 ---- a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java -+++ b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java -@@ -30,11 +30,18 @@ public class PlayerEnderChestContainer extends SimpleContainer { - } - - public PlayerEnderChestContainer(Player owner) { -- super(27); -+ super(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? 54 : 27); // Purpur - this.owner = owner; - // CraftBukkit end - } - -+ // Purpur start -+ @Override -+ public int getContainerSize() { -+ return owner.sixRowEnderchestSlotCount < 0 ? super.getContainerSize() : owner.sixRowEnderchestSlotCount; -+ } -+ // Purpur end -+ - public void setActiveChest(EnderChestBlockEntity blockEntity) { - this.activeChest = blockEntity; - } -diff --git a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java -index 7385e91f32f070e86a4e0fd3d214f55d832c7979..7b73de87236a60ce7343c29ec147e1866b448ba3 100644 ---- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java -@@ -85,6 +85,34 @@ public class EnderChestBlock extends AbstractChestBlock i - EnderChestBlockEntity enderChestBlockEntity = (EnderChestBlockEntity)blockEntity; - playerEnderChestContainer.setActiveChest(enderChestBlockEntity); - player.openMenu(new SimpleMenuProvider((syncId, inventory, playerx) -> { -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows) { -+ if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) { -+ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity(); -+ if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) { -+ player.sixRowEnderchestSlotCount = 54; -+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer); -+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.five")) { -+ player.sixRowEnderchestSlotCount = 45; -+ return ChestMenu.fiveRows(syncId, inventory, playerEnderChestContainer); -+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.four")) { -+ player.sixRowEnderchestSlotCount = 36; -+ return ChestMenu.fourRows(syncId, inventory, playerEnderChestContainer); -+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.three")) { -+ player.sixRowEnderchestSlotCount = 27; -+ return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer); -+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.two")) { -+ player.sixRowEnderchestSlotCount = 18; -+ return ChestMenu.twoRows(syncId, inventory, playerEnderChestContainer); -+ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.one")) { -+ player.sixRowEnderchestSlotCount = 9; -+ return ChestMenu.oneRow(syncId, inventory, playerEnderChestContainer); -+ } -+ } -+ player.sixRowEnderchestSlotCount = -1; -+ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer); -+ } -+ // Purpur end - return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer); - }, CONTAINER_TITLE)); - player.awardStat(Stats.OPEN_ENDERCHEST); -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java -index 416aa989ebb18a8741cc9d605a1180ab830f6643..e38a0adf5463c48311ad08b8d2e5b5c2d989a3b5 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BarrelBlockEntity.java -@@ -67,7 +67,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { - - public BarrelBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.BARREL, pos, state); -- this.items = NonNullList.withSize(27, ItemStack.EMPTY); -+ // Purpur start -+ this.items = NonNullList.withSize(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { -+ case 6 -> 54; -+ case 5 -> 45; -+ case 4 -> 36; -+ case 2 -> 18; -+ case 1 -> 9; -+ default -> 27; -+ }, ItemStack.EMPTY); -+ // Purpur end - this.openersCounter = new ContainerOpenersCounter() { - @Override - protected void onOpen(Level world, BlockPos pos, BlockState state) { -@@ -118,7 +127,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { - - @Override - public int getContainerSize() { -- return 27; -+ // Purpur start -+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { -+ case 6 -> 54; -+ case 5 -> 45; -+ case 4 -> 36; -+ case 2 -> 18; -+ case 1 -> 9; -+ default -> 27; -+ }; -+ // Purpur end - } - - @Override -@@ -138,7 +156,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { - - @Override - protected AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) { -- return ChestMenu.threeRows(syncId, playerInventory, this); -+ // Purpur start -+ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { -+ case 6 -> ChestMenu.sixRows(syncId, playerInventory, this); -+ case 5 -> ChestMenu.fiveRows(syncId, playerInventory, this); -+ case 4 -> ChestMenu.fourRows(syncId, playerInventory, this); -+ case 2 -> ChestMenu.twoRows(syncId, playerInventory, this); -+ case 1 -> ChestMenu.oneRow(syncId, playerInventory, this); -+ default -> ChestMenu.threeRows(syncId, playerInventory, this); -+ }; -+ // Purpur end - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java -index 633e6f4922ccaf59979a22885162f42c65bf628a..15001e7c8eae3a89f9d8669be532d56b70d538ef 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java -@@ -181,8 +181,19 @@ public class CraftContainer extends AbstractContainerMenu { - case PLAYER: - case CHEST: - case ENDER_CHEST: -+ // Purpur start -+ this.delegate = new ChestMenu(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? MenuType.GENERIC_9x6 : MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); -+ break; - case BARREL: -- this.delegate = new ChestMenu(MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); -+ this.delegate = new ChestMenu(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { -+ case 6 -> MenuType.GENERIC_9x6; -+ case 5 -> MenuType.GENERIC_9x5; -+ case 4 -> MenuType.GENERIC_9x4; -+ case 2 -> MenuType.GENERIC_9x2; -+ case 1 -> MenuType.GENERIC_9x1; -+ default -> MenuType.GENERIC_9x3; -+ }, windowId, bottom, top, top.getContainerSize() / 9); -+ // Purpur end - break; - case DISPENSER: - case DROPPER: -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -index 471ae4458e7ea7c29d7551b32cec98180fbccd4e..23db63c78e9fcf86cd498b3ed36ca50253c2fe97 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -@@ -83,7 +83,7 @@ public class CraftInventory implements Inventory { - - @Override - public void setContents(ItemStack[] items) { -- Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); -+ // Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); // Purpur - - for (int i = 0; i < this.getSize(); i++) { - if (i >= items.length) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 8ad548fc059568d37675e017548b171d4dd1d555..7937023274acf3a1efdd21bbdd2f933f4399baeb 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -177,4 +177,39 @@ public class PurpurConfig { - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - } -+ -+ public static int barrelRows = 3; -+ public static boolean enderChestSixRows = false; -+ public static boolean enderChestPermissionRows = false; -+ private static void blockSettings() { -+ if (version < 3) { -+ boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -+ set("settings.blocks.barrel.six-rows", oldValue); -+ set("settings.packed-barrels", null); -+ oldValue = getBoolean("settings.large-ender-chests", true); -+ set("settings.blocks.ender_chest.six-rows", oldValue); -+ set("settings.large-ender-chests", null); -+ } -+ if (version < 20) { -+ boolean oldValue = getBoolean("settings.blocks.barrel.six-rows", false); -+ set("settings.blocks.barrel.rows", oldValue ? 6 : 3); -+ set("settings.blocks.barrel.six-rows", null); -+ } -+ barrelRows = getInt("settings.blocks.barrel.rows", barrelRows); -+ if (barrelRows < 1 || barrelRows > 6) { -+ Bukkit.getLogger().severe("settings.blocks.barrel.rows must be 1-6, resetting to default"); -+ barrelRows = 3; -+ } -+ org.bukkit.event.inventory.InventoryType.BARREL.setDefaultSize(switch (barrelRows) { -+ case 6 -> 54; -+ case 5 -> 45; -+ case 4 -> 36; -+ case 2 -> 18; -+ case 1 -> 9; -+ default -> 27; -+ }); -+ enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); -+ org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); -+ enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); -+ } - } diff --git a/patches/server/0011-Llama-API.patch b/patches/server/0011-Llama-API.patch deleted file mode 100644 index 70e7fd31b..000000000 --- a/patches/server/0011-Llama-API.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 18 Oct 2019 22:50:12 -0500 -Subject: [PATCH] Llama API - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -index 21725aee29e9120d1c7e1e19f91c21a73a28844f..849f0c7c6d13df00d90211a48d8b56ab156812b8 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -@@ -22,6 +22,7 @@ public class LlamaFollowCaravanGoal extends Goal { - - @Override - public boolean canUse() { -+ if (!this.llama.shouldJoinCaravan) return false; // Purpur - if (!this.llama.isLeashed() && !this.llama.inCaravan()) { - List list = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0D, 4.0D, 9.0D), (entity) -> { - EntityType entityType = entity.getType(); -@@ -71,6 +72,7 @@ public class LlamaFollowCaravanGoal extends Goal { - - @Override - public boolean canContinueToUse() { -+ if (!this.llama.shouldJoinCaravan) return false; // Purpur - if (this.llama.inCaravan() && this.llama.getCaravanHead().isAlive() && this.firstIsLeashed(this.llama, 0)) { - double d = this.llama.distanceToSqr(this.llama.getCaravanHead()); - if (d > 676.0D) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index 967d1ff37ecc9a2cf81a1a6be8ceeb96b42ff847..72fe2dd69a9ae51e14ec91a83eac2dd74bd76cca 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -73,6 +73,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, Level world) { - super(type, world); -@@ -168,7 +169,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder -Date: Thu, 8 Aug 2019 15:29:15 -0500 -Subject: [PATCH] AFK API - -Adds the option for display names to be used in the afk broadcast - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 33d13eaa0edef9e1e6652affbc8e04fa51c31df1..f9571a379214d24a24e0b6b6277636666c1661f1 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2122,8 +2122,68 @@ public class ServerPlayer extends Player { - - public void resetLastActionTime() { - this.lastActionTime = Util.getMillis(); -+ this.setAfk(false); // Purpur - } - -+ // Purpur Start -+ private boolean isAfk = false; -+ -+ @Override -+ public void setAfk(boolean afk) { -+ if (this.isAfk == afk) { -+ return; -+ } -+ -+ String msg = afk ? org.purpurmc.purpur.PurpurConfig.afkBroadcastAway : org.purpurmc.purpur.PurpurConfig.afkBroadcastBack; -+ -+ org.purpurmc.purpur.event.PlayerAFKEvent event = new org.purpurmc.purpur.event.PlayerAFKEvent(this.getBukkitEntity(), afk, this.level().purpurConfig.idleTimeoutKick, msg, !Bukkit.isPrimaryThread()); -+ if (!event.callEvent() || event.shouldKick()) { -+ return; -+ } -+ -+ this.isAfk = afk; -+ -+ if (!afk) { -+ resetLastActionTime(); -+ } -+ -+ msg = event.getBroadcastMsg(); -+ if (msg != null && !msg.isEmpty()) { -+ String playerName = this.getGameProfile().getName(); -+ if (org.purpurmc.purpur.PurpurConfig.afkBroadcastUseDisplayName) { -+ net.kyori.adventure.text.Component playerDisplayNameComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getBukkitEntity().getDisplayName()); -+ playerName = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(playerDisplayNameComponent); -+ } -+ server.getPlayerList().broadcastMiniMessage(String.format(msg, playerName), false); -+ } -+ -+ if (this.level().purpurConfig.idleTimeoutUpdateTabList) { -+ String scoreboardName = getScoreboardName(); -+ String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName()); -+ String[] split = playerListName.split(scoreboardName); -+ String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, ""); -+ String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, ""); -+ if (afk) { -+ getBukkitEntity().setPlayerListName(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix + prefix + scoreboardName + suffix + org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, true); -+ } else { -+ getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix); -+ } -+ } -+ -+ ((ServerLevel) this.level()).updateSleepingPlayerList(); -+ } -+ -+ @Override -+ public boolean isAfk() { -+ return this.isAfk; -+ } -+ -+ @Override -+ public boolean canBeCollidedWith() { -+ return !this.isAfk() && super.canBeCollidedWith(); -+ } -+ // Purpur End -+ - public ServerStatsCounter getStats() { - return this.stats; - } -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index e936fa6e638a7ff4b087ecb4247c467be6fe9c57..f23e8f89e09551d245fcef5507ecd061ed1b3ca9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -342,6 +342,20 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private boolean justTeleported = false; - private boolean hasMoved; // Spigot - -+ // Purpur start -+ private final com.google.common.cache.LoadingCache kickPermissionCache = com.google.common.cache.CacheBuilder.newBuilder() -+ .maximumSize(1000) -+ .expireAfterWrite(1, java.util.concurrent.TimeUnit.MINUTES) -+ .build( -+ new com.google.common.cache.CacheLoader<>() { -+ @Override -+ public Boolean load(CraftPlayer player) { -+ return player.hasPermission("purpur.bypassIdleKick"); -+ } -+ } -+ ); -+ // Purpur end -+ - public CraftPlayer getCraftPlayer() { - return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); - } -@@ -435,6 +449,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - - if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits. -+ // Purpur start -+ this.player.setAfk(true); -+ if (!this.player.level().purpurConfig.idleTimeoutKick || (!Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")) && kickPermissionCache.getUnchecked(this.player.getBukkitEntity()))) { -+ return; -+ } -+ // Purpur end - this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 - this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause - } -@@ -753,6 +773,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.lastYaw = to.getYaw(); - this.lastPitch = to.getPitch(); - -+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur -+ - // Skip the first time we do this - if (true) { // Spigot - don't skip any move events - Location oldTo = to.clone(); -@@ -1582,7 +1604,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - if (!event.isAllowed()) { - flag2 = true; // Paper - diff on change, this should be moved wrongly - if (event.getLogWarning()) -- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString()); -+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur - } - // Paper end - } -@@ -1644,6 +1666,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.lastYaw = to.getYaw(); - this.lastPitch = to.getPitch(); - -+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur -+ - // Skip the first time we do this - if (from.getX() != Double.MAX_VALUE) { - Location oldTo = to.clone(); -diff --git a/src/main/java/net/minecraft/server/players/SleepStatus.java b/src/main/java/net/minecraft/server/players/SleepStatus.java -index 823efad652d8ff9e96b99375b102fef6f017716e..caa8a69bde0c212c36dd990a67836ac2f95548c0 100644 ---- a/src/main/java/net/minecraft/server/players/SleepStatus.java -+++ b/src/main/java/net/minecraft/server/players/SleepStatus.java -@@ -19,7 +19,7 @@ public class SleepStatus { - - public boolean areEnoughDeepSleeping(int percentage, List players) { - // CraftBukkit start -- int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping; }).count(); -+ int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping || (eh.level().purpurConfig.idleTimeoutCountAsSleeping && eh.isAfk()); }).count(); // Purpur - boolean anyDeepSleep = players.stream().anyMatch(Player::isSleepingLongEnough); - - return anyDeepSleep && j >= this.sleepersNeeded(percentage); -@@ -52,7 +52,7 @@ public class SleepStatus { - - if (!entityplayer.isSpectator()) { - ++this.activePlayers; -- if (entityplayer.isSleeping() || entityplayer.fauxSleeping) { // CraftBukkit -+ if ((entityplayer.isSleeping() || entityplayer.fauxSleeping) || (entityplayer.level().purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk())) { // CraftBukkit // Purpur - ++this.sleepingPlayers; - } - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java -index 3ff999734d14e2b6e7828e117f5ee32a60c26bc1..cfa9607241c3e69777ffc317206996c2f783437a 100644 ---- a/src/main/java/net/minecraft/world/entity/EntitySelector.java -+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java -@@ -39,6 +39,7 @@ public final class EntitySelector { - return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks; - }; - // Paper end -+ public static Predicate notAfk = (player) -> !player.isAfk(); // Purpur - - private EntitySelector() {} - // Paper start -diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -index d25307ae8bbdf10ae067ec70fc2cb957b852a0eb..54bdb81785b617e13e67530752395f2a0c6d703a 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -@@ -64,6 +64,10 @@ public class TargetingConditions { - return false; - } else if (this.selector != null && !this.selector.test(targetEntity)) { - return false; -+ // Purpur start -+ } else if (!targetEntity.level().purpurConfig.idleTimeoutTargetPlayer && targetEntity instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) { -+ return false; -+ // Purpur end - } else { - if (baseEntity == null) { - if (this.isCombat && (!targetEntity.canBeSeenAsEnemy() || targetEntity.level().getDifficulty() == Difficulty.PEACEFUL)) { -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 96726f06b83193fd7c5156efe6411b10eb1db435..7b39cb5346925c14f3f144d622ca7e5855420aa6 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -193,6 +193,13 @@ public abstract class Player extends LivingEntity { - public boolean fauxSleeping; - public int oldLevel = -1; - -+ public void setAfk(boolean afk) { -+ } -+ -+ public boolean isAfk() { -+ return false; -+ } -+ - @Override - public CraftHumanEntity getBukkitEntity() { - return (CraftHumanEntity) super.getBukkitEntity(); -diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java -index 3b959f42d958bf0f426853aee56753d6c455fcdb..d17abb283ea818244df0379d6b57fc634071e0b9 100644 ---- a/src/main/java/net/minecraft/world/level/EntityGetter.java -+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java -@@ -154,7 +154,7 @@ public interface EntityGetter { - - default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) { - for(Player player : this.players()) { -- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) { -+ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) { - double d = player.distanceToSqr(x, y, z); - if (range < 0.0D || d < range * range) { - return true; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 7f7375c0a3e680ef60fb9fc6157b72bb0812d1c3..d0986b7b130f790c21d2324d0014d258bc36c527 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -517,10 +517,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - - @Override - public void setPlayerListName(String name) { -+ // Purpur start -+ setPlayerListName(name, false); -+ } -+ public void setPlayerListName(String name, boolean useMM) { -+ // Purpur end - if (name == null) { - name = getName(); - } -- this.getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name); -+ this.getHandle().listName = name.equals(getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur - for (ServerPlayer player : (List) server.getHandle().players) { - if (player.getBukkitEntity().canSee(this)) { - player.connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, this.getHandle())); -@@ -3273,5 +3278,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - public boolean usesPurpurClient() { - return getHandle().purpurClient; - } -+ -+ @Override -+ public boolean isAfk() { -+ return getHandle().isAfk(); -+ } -+ -+ @Override -+ public void setAfk(boolean setAfk) { -+ getHandle().setAfk(setAfk); -+ } -+ -+ @Override -+ public void resetIdleTimer() { -+ getHandle().resetLastActionTime(); -+ } - // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 7937023274acf3a1efdd21bbdd2f933f4399baeb..3fba9f640ef03219c89a2a405e4d5a5870e366bf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -174,8 +174,18 @@ public class PurpurConfig { - } - - public static String cannotRideMob = "You cannot mount that mob"; -+ public static String afkBroadcastAway = "%s is now AFK"; -+ public static String afkBroadcastBack = "%s is no longer AFK"; -+ public static boolean afkBroadcastUseDisplayName = false; -+ public static String afkTabListPrefix = "[AFK] "; -+ public static String afkTabListSuffix = ""; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); -+ afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -+ afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack); -+ afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName); -+ afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix))); -+ afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); - } - - public static int barrelRows = 3; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 27ce55687c7c59fdfdcc4553240ea8b023919b77..3ed1a91c93f659306acaf8bad6820f49208554c8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -90,6 +90,24 @@ public class PurpurWorldConfig { - return value.isEmpty() ? fallback : value; - } - -+ public boolean idleTimeoutKick = true; -+ public boolean idleTimeoutTickNearbyEntities = true; -+ public boolean idleTimeoutCountAsSleeping = false; -+ public boolean idleTimeoutUpdateTabList = false; -+ public boolean idleTimeoutTargetPlayer = true; -+ private void playerSettings() { -+ if (PurpurConfig.version < 19) { -+ boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -+ set("gameplay-mechanics.player.idle-timeout.mods-target", null); -+ set("gameplay-mechanics.player.idle-timeout.mobs-target", oldVal); -+ } -+ idleTimeoutKick = System.getenv("PURPUR_FORCE_IDLE_KICK") == null ? getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick) : Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")); -+ idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities); -+ idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping); -+ idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList); -+ idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer); -+ } -+ - public boolean babiesAreRidable = true; - public boolean untamedTamablesAreRidable = true; - public boolean useNightVisionWhenRiding = false; -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 68557964e27fa1e5ba218178f9bcc0b28e3a78d9..12adaba78ec30e463963f99c0d78e844756143a1 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -203,6 +203,7 @@ public class ActivationRange - continue; - } - -+ if (!player.level().purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur - // Paper start - int worldHeight = world.getHeight(); - ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange ); diff --git a/patches/server/0013-Bring-back-server-name.patch b/patches/server/0013-Bring-back-server-name.patch deleted file mode 100644 index be8189ab9..000000000 --- a/patches/server/0013-Bring-back-server-name.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 26 May 2019 15:19:14 -0500 -Subject: [PATCH] Bring back server name - - -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -index 818289e831e3dad29345c43265e2efd7689bc500..1ea3012995c738c67b31e997c138f824f9e69ba1 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -58,6 +58,7 @@ public class DedicatedServerProperties extends Settings -Date: Sat, 21 Mar 2020 11:47:39 -0500 -Subject: [PATCH] Configurable server mod name - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index da825e7667032cb656585367bf4b96bc18f57aa6..80c085498d9b61bae2e06df873c1ad66cc7d8ae5 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1696,7 +1696,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return org.purpurmc.purpur.PurpurConfig.serverModName; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! - } - - public SystemReport fillSystemReport(SystemReport details) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6b768cff1318b4bcebeed95345f3cfdce05a2c16..3d72f71c851e2d46e9cbeab9cdbadc532cb90eb2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -188,6 +188,11 @@ public class PurpurConfig { - afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); - } - -+ public static String serverModName = "Purpur"; -+ private static void serverModName() { -+ serverModName = getString("settings.server-mod-name", serverModName); -+ } -+ - public static int barrelRows = 3; - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; diff --git a/patches/server/0015-LivingEntity-safeFallDistance.patch b/patches/server/0015-LivingEntity-safeFallDistance.patch deleted file mode 100644 index 29de6cddb..000000000 --- a/patches/server/0015-LivingEntity-safeFallDistance.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 5 May 2019 12:58:45 -0500 -Subject: [PATCH] LivingEntity safeFallDistance - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 0d07e666f4da002a353d03844d62a0e3aac44d9d..92745269118d8a0cb91cbbebdd7700f7737d2b9a 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -253,6 +253,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - private boolean skipDropExperience; - // CraftBukkit start - public int expToDrop; -+ public float safeFallDistance = 3.0F; // Purpur - public boolean forceDrops; - public ArrayList drops = new ArrayList(); - public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; -@@ -353,7 +354,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.tryAddSoulSpeed(); - } - -- if (!this.level().isClientSide && this.fallDistance > 3.0F && onGround && !state.isAir()) { -+ if (!this.level().isClientSide && this.fallDistance > this.safeFallDistance && onGround && !state.isAir()) { // Purpur - double d1 = this.getX(); - double d2 = this.getY(); - double d3 = this.getZ(); -@@ -368,7 +369,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - d3 = (double) landedPosition.getZ() + 0.5D + d5 / d6 * 0.5D; - } - -- float f = (float) Mth.ceil(this.fallDistance - 3.0F); -+ float f = (float) Mth.ceil(this.fallDistance - this.safeFallDistance); // Purpur - double d7 = Math.min((double) (0.2F + f / 15.0F), 2.5D); - int i = (int) (150.0D * d7); - -@@ -2054,7 +2055,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - MobEffectInstance mobeffect = this.getEffect(MobEffects.JUMP); - float f2 = mobeffect == null ? 0.0F : (float) (mobeffect.getAmplifier() + 1); - -- return Mth.ceil((fallDistance - 3.0F - f2) * damageMultiplier); -+ return Mth.ceil((fallDistance - this.safeFallDistance - f2) * damageMultiplier); // Purpur - } - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -index 296cc895fbd090b4d43807f4ca393f6dd853e0ce..8400f02a37bdef4a9f92879a6b186ebe1f2f145e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java -@@ -384,7 +384,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - - @Override - protected int calculateFallDamage(float fallDistance, float damageMultiplier) { -- return Mth.ceil((fallDistance * 0.5F - 3.0F) * damageMultiplier); -+ return Mth.ceil((fallDistance * 0.5F - this.safeFallDistance) * damageMultiplier); - } - - protected int getInventorySize() { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java -index 3f7d21a5be726d301e1bcaa11f1788b51c23a7cd..db95323da1aef267aa4fbe56aaff63cb8684e15b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -12,6 +12,7 @@ import net.minecraft.world.level.LevelReader; - public class Giant extends Monster { - public Giant(EntityType type, Level world) { - super(type, world); -+ this.safeFallDistance = 10.0F; // Purpur - } - - // Purpur start -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index 7bc8b62ff6aa355f3f025fc6c3a3d4c6b355853b..f7ae6d11c6043a2037dbfd160b409579bd9a35fd 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -1094,4 +1094,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - getHandle().knockback(strength, directionX, directionZ); - }; - // Paper end -+ -+ // Purpur start -+ @Override -+ public float getSafeFallDistance() { -+ return getHandle().safeFallDistance; -+ } -+ -+ @Override -+ public void setSafeFallDistance(float safeFallDistance) { -+ getHandle().safeFallDistance = safeFallDistance; -+ } -+ // Purpur end - } diff --git a/patches/server/0016-Lagging-threshold.patch b/patches/server/0016-Lagging-threshold.patch deleted file mode 100644 index 558ee9057..000000000 --- a/patches/server/0016-Lagging-threshold.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 23 Jul 2019 10:07:16 -0500 -Subject: [PATCH] Lagging threshold - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a8ce4fd1a94ad4cc3bc189625851d34a47f42167..54f3c35ad02a09865c1bce38e9405dad048142b3 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -307,6 +307,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Fri, 5 Jul 2019 18:21:00 -0500 -Subject: [PATCH] PlayerSetSpawnerTypeWithEggEvent - - -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index aa5cec6d56d7a8e80861aa4c9b4a74ca3e64be8c..08beb4c4dfcb0986cdebb4d0cacc25e4e9c17674 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -315,6 +315,16 @@ public class EntityType implements FeatureElement, EntityTypeT - return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType) type.build(id)); // CraftBukkit - decompile error - } - -+ // Purpur start -+ public static EntityType getFromBukkitType(org.bukkit.entity.EntityType bukkitType) { -+ return getFromKey(new ResourceLocation(bukkitType.getKey().toString())); -+ } -+ -+ public static EntityType getFromKey(ResourceLocation location) { -+ return BuiltInRegistries.ENTITY_TYPE.get(location); -+ } -+ // Purpur end -+ - public static ResourceLocation getKey(EntityType type) { - return BuiltInRegistries.ENTITY_TYPE.getKey(type); - } -@@ -530,6 +540,16 @@ public class EntityType implements FeatureElement, EntityTypeT - return this.category; - } - -+ // Purpur start -+ public String getName() { -+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath(); -+ } -+ -+ public String getTranslatedName() { -+ return getDescription().getString(); -+ } -+ // Purpur end -+ - public String getDescriptionId() { - if (this.descriptionId == null) { - this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this)); -diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java -index 741719301e6fc91a598e74342810c4185e6fde26..6fbff9c02fbabf03c9c649a9ea6128021081f9cd 100644 ---- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java -+++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java -@@ -68,6 +68,15 @@ public class SpawnEggItem extends Item { - SpawnerBlockEntity tileentitymobspawner = (SpawnerBlockEntity) tileentity; - EntityType entitytypes = this.getType(itemstack.getTag()); - -+ // Purpur start -+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); -+ org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.CreatureSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(entitytypes.getName())); -+ if (!event.callEvent()) { -+ return InteractionResult.FAIL; -+ } -+ entitytypes = EntityType.getFromBukkitType(event.getEntityType()); -+ // Purpur end -+ - tileentitymobspawner.setEntityId(entitytypes, world.getRandom()); - tileentity.setChanged(); - world.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3); diff --git a/patches/server/0018-Player-invulnerabilities.patch b/patches/server/0018-Player-invulnerabilities.patch deleted file mode 100644 index df1e66750..000000000 --- a/patches/server/0018-Player-invulnerabilities.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 2 May 2020 20:55:44 -0500 -Subject: [PATCH] Player invulnerabilities - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index f9571a379214d24a24e0b6b6277636666c1661f1..ffc10f3cb841eafbf90b7f26b485991ced31d4d1 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -279,6 +279,7 @@ public class ServerPlayer extends Player { - public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event - public boolean purpurClient = false; // Purpur -+ public boolean acceptingResourcePack = false; // Purpur - - private final java.util.concurrent.atomic.AtomicReference viewDistances = new java.util.concurrent.atomic.AtomicReference<>(new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.ViewDistances(-1, -1, -1)); - public io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -419,6 +420,7 @@ public class ServerPlayer extends Player { - this.bukkitPickUpLoot = true; - this.maxHealthCache = this.getMaxHealth(); - this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper -+ this.spawnInvulnerableTime = world.purpurConfig.playerSpawnInvulnerableTicks; // Purpur - } - - // Yes, this doesn't match Vanilla, but it's the best we can do for now. -@@ -1100,6 +1102,12 @@ public class ServerPlayer extends Player { - - } - -+ // Purpur start -+ public boolean isSpawnInvulnerable() { -+ return spawnInvulnerableTime > 0 || frozen; -+ } -+ // Purpur end -+ - @Override - public boolean hurt(DamageSource source, float amount) { - if (this.isInvulnerableTo(source)) { -@@ -1107,7 +1115,7 @@ public class ServerPlayer extends Player { - } else { - boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL); - -- if (!flag && this.spawnInvulnerableTime > 0 && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { -+ if (!flag && isSpawnInvulnerable() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { // Purpur - return false; - } else { - Entity entity = source.getEntity(); -@@ -1286,6 +1294,7 @@ public class ServerPlayer extends Player { - } - // Paper end - -+ this.spawnInvulnerableTime = worldserver.purpurConfig.playerSpawnInvulnerableTicks; // Purpur - return this; - } - } -@@ -2108,6 +2117,7 @@ public class ServerPlayer extends Player { - } - - public void sendTexturePack(String url, String hash, boolean required, @Nullable Component resourcePackPrompt) { -+ this.acceptingResourcePack = true; // Purpur - this.connection.send(new ClientboundResourcePackPacket(url, hash, required, resourcePackPrompt)); - } - -@@ -2695,9 +2705,17 @@ public class ServerPlayer extends Player { - - @Override - public boolean isImmobile() { -- return super.isImmobile() || (this.connection != null && this.connection.isDisconnected()); // Paper -+ return super.isImmobile() || frozen || (this.connection != null && this.connection.isDisconnected()); // Paper // Purpur - } - -+ // Purpur start -+ private boolean frozen = false; -+ -+ public void setFrozen(boolean frozen) { -+ this.frozen = frozen; -+ } -+ // Purpur end -+ - @Override - public Scoreboard getScoreboard() { - return this.getBukkitEntity().getScoreboard().getHandle(); -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index f23e8f89e09551d245fcef5507ecd061ed1b3ca9..81cbb6a55fd3e6a3b9ec96cc54b43e123c243482 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2149,12 +2149,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - @Override - public void handleResourcePackResponse(ServerboundResourcePackPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); -+ // Purpur start -+ if (player.level().purpurConfig.playerInvulnerableWhileAcceptingResourcePack && !this.player.acceptingResourcePack) { -+ ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack packet exploitation attempt", this.player.getName()); -+ this.disconnect(Component.translatable("multiplayer.texturePrompt.failure.line1"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // "Server resource pack couldn't be applied" -+ return; -+ } -+ // Purpur end - if (packet.getAction() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) { - ServerGamePacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getGameProfile().getName()); // Paper - Don't print component in resource pack rejection message - this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - add cause - } - // Paper start - PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action.ordinal()]; -+ if (player.level().purpurConfig.playerInvulnerableWhileAcceptingResourcePack) player.setFrozen(packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED); // Purpur -+ this.player.acceptingResourcePack = packStatus == PlayerResourcePackStatusEvent.Status.ACCEPTED; // Purpur - player.getBukkitEntity().setResourcePackStatus(packStatus); - this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(this.getCraftPlayer(), packStatus)); // CraftBukkit - // Paper end -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 42801a26b9d5f8af2d0ce1fef864bb031cfa8965..2cac536ebbc21a23a2219c630fe477bf9c00ce65 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -985,6 +985,8 @@ public abstract class PlayerList { - } - // Paper end - -+ entityplayer1.spawnInvulnerableTime = entityplayer1.level().purpurConfig.playerSpawnInvulnerableTicks; // Purpur -+ - // CraftBukkit end - return entityplayer1; - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index d0986b7b130f790c21d2324d0014d258bc36c527..c6ede1fac5c68f2912a9c401765bd10b09afe7f9 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3293,5 +3293,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - public void resetIdleTimer() { - getHandle().resetLastActionTime(); - } -+ -+ @Override -+ public boolean isSpawnInvulnerable() { -+ return getHandle().isSpawnInvulnerable(); -+ } -+ -+ @Override -+ public int getSpawnInvulnerableTicks() { -+ return getHandle().spawnInvulnerableTime; -+ } -+ -+ @Override -+ public void setSpawnInvulnerableTicks(int spawnInvulnerableTime) { -+ getHandle().spawnInvulnerableTime = spawnInvulnerableTime; -+ } - // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3ed1a91c93f659306acaf8bad6820f49208554c8..f521d0e73908037c76c5669b401e422040e2a77b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -95,6 +95,8 @@ public class PurpurWorldConfig { - public boolean idleTimeoutCountAsSleeping = false; - public boolean idleTimeoutUpdateTabList = false; - public boolean idleTimeoutTargetPlayer = true; -+ public int playerSpawnInvulnerableTicks = 60; -+ public boolean playerInvulnerableWhileAcceptingResourcePack = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -106,6 +108,8 @@ public class PurpurWorldConfig { - idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping); - idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList); - idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer); -+ playerSpawnInvulnerableTicks = getInt("gameplay-mechanics.player.spawn-invulnerable-ticks", playerSpawnInvulnerableTicks); -+ playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack); - } - - public boolean babiesAreRidable = true; diff --git a/patches/server/0019-Anvil-API.patch b/patches/server/0019-Anvil-API.patch deleted file mode 100644 index 03553fdbb..000000000 --- a/patches/server/0019-Anvil-API.patch +++ /dev/null @@ -1,174 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 19 Apr 2020 00:17:56 -0500 -Subject: [PATCH] Anvil API - - -diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -index 706b354ac9a1a6a4a1e61b2a109180d1dd22bbbd..9ca261c9f21279558961649cb4849ac379d67573 100644 ---- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -@@ -76,6 +76,7 @@ public abstract class AbstractContainerMenu { - @Nullable - private ContainerSynchronizer synchronizer; - private boolean suppressRemoteUpdates; -+ @javax.annotation.Nullable protected ItemStack activeQuickItem = null; // Purpur - - // CraftBukkit start - public boolean checkReachable = true; -diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -index e0c3a4ba27e21c3692e601acd0af60873bcbb84c..b500a04b8135604f0159a741b3d228c9e87b2a46 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -23,6 +23,13 @@ import org.slf4j.Logger; - import org.bukkit.craftbukkit.inventory.CraftInventoryView; - // CraftBukkit end - -+// Purpur start -+import net.minecraft.nbt.IntTag; -+import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; -+import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -+import net.minecraft.server.level.ServerPlayer; -+// Purpur end -+ - public class AnvilMenu extends ItemCombinerMenu { - - public static final int INPUT_SLOT = 0; -@@ -51,6 +58,8 @@ public class AnvilMenu extends ItemCombinerMenu { - public int maximumRepairCost = 40; - private CraftInventoryView bukkitEntity; - // CraftBukkit end -+ public boolean bypassCost = false; // Purpur -+ public boolean canDoUnsafeEnchants = false; // Purpur - - public AnvilMenu(int syncId, Inventory inventory) { - this(syncId, inventory, ContainerLevelAccess.NULL); -@@ -78,12 +87,15 @@ public class AnvilMenu extends ItemCombinerMenu { - - @Override - protected boolean mayPickup(Player player, boolean present) { -- return (player.getAbilities().instabuild || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && present; // CraftBukkit - allow cost 0 like a free item -+ return (player.getAbilities().instabuild || player.experienceLevel >= this.cost.get()) && (bypassCost || this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST) && present; // CraftBukkit - allow cost 0 like a free item // Purpur - } - - @Override - protected void onTake(Player player, ItemStack stack) { -+ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur -+ if (org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)).callEvent(); // Purpur - if (!player.getAbilities().instabuild) { -+ if (bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur - player.giveExperienceLevels(-this.cost.get()); - } - -@@ -134,6 +146,12 @@ public class AnvilMenu extends ItemCombinerMenu { - - @Override - public void createResult() { -+ // Purpur start -+ bypassCost = false; -+ canDoUnsafeEnchants = false; -+ if (org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent(getBukkitView()).callEvent(); -+ // Purpur end -+ - ItemStack itemstack = this.inputSlots.getItem(0); - - this.cost.set(1); -@@ -210,7 +228,7 @@ public class AnvilMenu extends ItemCombinerMenu { - int i2 = (Integer) map1.get(enchantment); - - i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1); -- boolean flag3 = enchantment.canEnchant(itemstack); -+ boolean flag3 = canDoUnsafeEnchants || enchantment.canEnchant(itemstack); // Purpur - - if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) { - flag3 = true; -@@ -222,7 +240,7 @@ public class AnvilMenu extends ItemCombinerMenu { - Enchantment enchantment1 = (Enchantment) iterator1.next(); - - if (enchantment1 != enchantment && !enchantment.isCompatibleWith(enchantment1)) { -- flag3 = false; -+ flag3 = canDoUnsafeEnchants; // Purpur - ++i; - } - } -@@ -293,6 +311,13 @@ public class AnvilMenu extends ItemCombinerMenu { - this.cost.set(this.maximumRepairCost - 1); // CraftBukkit - } - -+ // Purpur start -+ if (bypassCost && cost.get() >= maximumRepairCost) { -+ itemstack.addTagElement("Purpur.realCost", IntTag.valueOf(cost.get())); -+ cost.set(maximumRepairCost - 1); -+ } -+ // Purpur end -+ - if (this.cost.get() >= this.maximumRepairCost && !this.player.getAbilities().instabuild) { // CraftBukkit - itemstack1 = ItemStack.EMPTY; - } -@@ -315,6 +340,12 @@ public class AnvilMenu extends ItemCombinerMenu { - org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemstack1); // CraftBukkit - sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686: Always send completed inventory to stay in sync with client - this.broadcastChanges(); -+ // Purpur start -+ if (canDoUnsafeEnchants && itemstack1 != ItemStack.EMPTY) { -+ ((ServerPlayer) player).connection.send(new ClientboundContainerSetSlotPacket(containerId, incrementStateId(), 2, itemstack1)); -+ ((ServerPlayer) player).connection.send(new ClientboundContainerSetDataPacket(containerId, 0, cost.get())); -+ } -+ // Purpur end - } - } - -diff --git a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -index ff770b9ce68a62418de0c7ed389650626fa1dcb2..102739c0089ff3f6b3432f954304d43a3dfebc35 100644 ---- a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -@@ -177,7 +177,9 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { - return ItemStack.EMPTY; - } - -+ this.activeQuickItem = itemstack; // Purpur - slot1.onTake(player, itemstack1); -+ this.activeQuickItem = null; // Purpur - } - - return itemstack; -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java -index 88d3ca586ff6905f18a8ab9f0e229f440ed44088..27dd4eb4781a3c75772860c11db886e1038cecd2 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java -@@ -9,7 +9,7 @@ import org.bukkit.inventory.AnvilInventory; - public class CraftInventoryAnvil extends CraftResultInventory implements AnvilInventory { - - private final Location location; -- private final AnvilMenu container; -+ public final AnvilMenu container; // Purpur - private -> public - - public CraftInventoryAnvil(Location location, Container inventory, Container resultInventory, AnvilMenu container) { - super(inventory, resultInventory); -@@ -57,4 +57,26 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn - Preconditions.checkArgument(levels >= 0, "Maximum repair cost must be positive (or 0)"); - container.maximumRepairCost = levels; - } -+ -+ // Purpur start -+ @Override -+ public boolean canBypassCost() { -+ return container.bypassCost; -+ } -+ -+ @Override -+ public void setBypassCost(boolean bypassCost) { -+ container.bypassCost = bypassCost; -+ } -+ -+ @Override -+ public boolean canDoUnsafeEnchants() { -+ return container.canDoUnsafeEnchants; -+ } -+ -+ @Override -+ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) { -+ container.canDoUnsafeEnchants = canDoUnsafeEnchants; -+ } -+ // Purpur end - } diff --git a/patches/server/0020-Alternative-Keepalive-Handling.patch b/patches/server/0020-Alternative-Keepalive-Handling.patch deleted file mode 100644 index 2cb2cd2c3..000000000 --- a/patches/server/0020-Alternative-Keepalive-Handling.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 11 Oct 2019 00:17:39 -0500 -Subject: [PATCH] Alternative Keepalive Handling - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 81cbb6a55fd3e6a3b9ec96cc54b43e123c243482..317342df5d3afe65e09853fd9ffe4aca127152d2 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -265,6 +265,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private long keepAliveTime = Util.getMillis(); - private boolean keepAlivePending; - private long keepAliveChallenge; -+ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur - // CraftBukkit start - multithreaded fields - private final AtomicInteger chatSpamTickCount = new AtomicInteger(); - private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits -@@ -417,6 +418,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - long currentTime = Util.getMillis(); - long elapsedTime = currentTime - this.keepAliveTime; - -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { -+ if (elapsedTime >= 1000L) { // 1 second -+ if (!processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { -+ LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); -+ disconnect(Component.translatable("disconnect.timeout"), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); -+ } else { -+ keepAliveTime = currentTime; // hijack this field for 1 second intervals -+ keepAlives.add(currentTime); // currentTime is ID -+ send(new ClientboundKeepAlivePacket(currentTime)); -+ } -+ } -+ } else -+ // Purpur end -+ - if (this.keepAlivePending) { - if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected - ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info -@@ -3580,6 +3596,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - @Override - public void handleKeepAlive(ServerboundKeepAlivePacket packet) { -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { -+ long id = packet.getId(); -+ if (keepAlives.size() > 0 && keepAlives.contains(id)) { -+ int ping = (int) (Util.getMillis() - id); -+ player.latency = (player.latency * 3 + ping) / 4; -+ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest -+ } -+ } else -+ // Purpur end - //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread - if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { - int i = (int) (Util.getMillis() - this.keepAliveTime); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 34afed28faeb56352b48faf8078a5fb07912eb5e..ad239ba9259c5a63b40261ebc44224893496b47d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -198,6 +198,11 @@ public class PurpurConfig { - laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold); - } - -+ public static boolean useAlternateKeepAlive = false; -+ private static void useAlternateKeepAlive() { -+ useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); -+ } -+ - public static int barrelRows = 3; - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; diff --git a/patches/server/0021-Silk-touch-spawners.patch b/patches/server/0021-Silk-touch-spawners.patch deleted file mode 100644 index feeaf3d03..000000000 --- a/patches/server/0021-Silk-touch-spawners.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 9 May 2019 14:27:37 -0500 -Subject: [PATCH] Silk touch spawners - - -diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java -index 5f20e075c532f0f1d413242949d1738c0c152bf7..e6f8cb165f7e3da5f0edfc952d14059516de8acf 100644 ---- a/src/main/java/net/minecraft/world/item/Items.java -+++ b/src/main/java/net/minecraft/world/item/Items.java -@@ -294,7 +294,7 @@ public class Items { - public static final Item PURPUR_BLOCK = registerBlock(Blocks.PURPUR_BLOCK); - public static final Item PURPUR_PILLAR = registerBlock(Blocks.PURPUR_PILLAR); - public static final Item PURPUR_STAIRS = registerBlock(Blocks.PURPUR_STAIRS); -- public static final Item SPAWNER = registerBlock(Blocks.SPAWNER); -+ public static final Item SPAWNER = registerBlock(new org.purpurmc.purpur.item.SpawnerItem(Blocks.SPAWNER, new Item.Properties().rarity(Rarity.EPIC))); // Purpur - public static final Item CHEST = registerBlock(Blocks.CHEST); - public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE); - public static final Item FARMLAND = registerBlock(Blocks.FARMLAND); -diff --git a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java -index 936d844a5a246138c9f9ae4ae6e318242b8f1420..93f5f226cf6fd6110e4daa02b3f5d9ad253814a0 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java -@@ -40,6 +40,58 @@ public class SpawnerBlock extends BaseEntityBlock { - return createTickerHelper(type, BlockEntityType.MOB_SPAWNER, world.isClientSide ? SpawnerBlockEntity::clientTick : SpawnerBlockEntity::serverTick); - } - -+ // Purpur start -+ @Override -+ public void playerDestroy(Level level, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack, boolean includeDrops) { -+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.drop.spawners") && isSilkTouch(level, stack)) { -+ Optional> type = net.minecraft.world.entity.EntityType.by(((SpawnerBlockEntity) blockEntity).getSpawner().nextSpawnData.getEntityToSpawn()); -+ -+ net.minecraft.world.entity.EntityType entityType = type.orElse(null); -+ final net.kyori.adventure.text.Component mobName = io.papermc.paper.adventure.PaperAdventure.asAdventure(entityType == null ? Component.empty() : entityType.getDescription()); -+ CompoundTag display = new CompoundTag(); -+ CompoundTag tag = new CompoundTag(); -+ -+ String name = level.purpurConfig.silkTouchSpawnerName; -+ if (name != null && !name.isEmpty() && !name.equals("Monster Spawner")) { -+ net.kyori.adventure.text.Component displayName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName)); -+ if (name.startsWith("")) { -+ displayName = displayName.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); -+ } -+ display.put("Name", net.minecraft.nbt.StringTag.valueOf(io.papermc.paper.adventure.PaperAdventure.asJsonString(displayName, java.util.Locale.ROOT))); -+ tag.put("display", display); -+ } -+ -+ List lore = level.purpurConfig.silkTouchSpawnerLore; -+ if (lore != null && !lore.isEmpty()) { -+ net.minecraft.nbt.ListTag list = new net.minecraft.nbt.ListTag(); -+ for (String line : lore) { -+ net.kyori.adventure.text.Component lineComponent = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(line, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName)); -+ if (line.startsWith("")) { -+ lineComponent = lineComponent.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); -+ } -+ list.add(net.minecraft.nbt.StringTag.valueOf(io.papermc.paper.adventure.PaperAdventure.asJsonString(lineComponent, java.util.Locale.ROOT))); -+ } -+ display.put("Lore", list); -+ tag.put("display", display); -+ } -+ -+ ItemStack item = new ItemStack(Blocks.SPAWNER.asItem()); -+ if (entityType != null) { -+ tag.putString("Purpur.mob_type", entityType.getName()); -+ tag.putDouble("HideFlags", 32); // hides the "Interact with Spawn Egg" tooltip -+ item.setTag(tag); -+ } -+ -+ popResource(level, pos, item); -+ } -+ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops); -+ } -+ -+ private boolean isSilkTouch(Level level, ItemStack stack) { -+ return stack != null && level.purpurConfig.silkTouchTools.contains(stack.getItem()) && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH, stack) >= level.purpurConfig.minimumSilkTouchSpawnerRequire; -+ } -+ // Purpur end -+ - @Override - public void spawnAfterBreak(BlockState state, ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) { - super.spawnAfterBreak(state, world, pos, tool, dropExperience); -@@ -48,6 +100,7 @@ public class SpawnerBlock extends BaseEntityBlock { - - @Override - public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) { -+ if (isSilkTouch(worldserver, itemstack)) return 0; // Purpur - if (flag) { - int i = 15 + worldserver.random.nextInt(15) + worldserver.random.nextInt(15); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f521d0e73908037c76c5669b401e422040e2a77b..ef20a9601527281d052c531e7e769ea480622481 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -112,6 +112,38 @@ public class PurpurWorldConfig { - playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack); - } - -+ public boolean silkTouchEnabled = false; -+ public String silkTouchSpawnerName = "Monster Spawner"; -+ public List silkTouchSpawnerLore = new ArrayList<>(); -+ public List silkTouchTools = new ArrayList<>(); -+ public int minimumSilkTouchSpawnerRequire = 1; -+ private void silkTouchSettings() { -+ if (PurpurConfig.version < 21) { -+ String oldName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName); -+ set("gameplay-mechanics.silk-touch.spawner-name", "" + ChatColor.toMM(oldName.replace("{mob}", ""))); -+ List list = new ArrayList<>(); -+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a ")) -+ .forEach(line -> list.add("" + ChatColor.toMM(line.toString().replace("{mob}", "")))); -+ set("gameplay-mechanics.silk-touch.spawner-lore", list); -+ } -+ silkTouchEnabled = getBoolean("gameplay-mechanics.silk-touch.enabled", silkTouchEnabled); -+ silkTouchSpawnerName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName); -+ minimumSilkTouchSpawnerRequire = getInt("gameplay-mechanics.silk-touch.minimal-level", minimumSilkTouchSpawnerRequire); -+ silkTouchSpawnerLore.clear(); -+ getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a ")) -+ .forEach(line -> silkTouchSpawnerLore.add(line.toString())); -+ silkTouchTools.clear(); -+ getList("gameplay-mechanics.silk-touch.tools", List.of( -+ "minecraft:iron_pickaxe", -+ "minecraft:golden_pickaxe", -+ "minecraft:diamond_pickaxe", -+ "minecraft:netherite_pickaxe" -+ )).forEach(key -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); -+ if (item != Items.AIR) silkTouchTools.add(item); -+ }); -+ } -+ - public boolean babiesAreRidable = true; - public boolean untamedTamablesAreRidable = true; - public boolean useNightVisionWhenRiding = false; -diff --git a/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java -new file mode 100644 -index 0000000000000000000000000000000000000000..c038fb2bbb0f0e78380bc24bbd6348b869669a90 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java -@@ -0,0 +1,36 @@ -+package org.purpurmc.purpur.item; -+ -+import net.minecraft.core.BlockPos; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.entity.player.Player; -+import net.minecraft.world.item.BlockItem; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.block.entity.BlockEntity; -+import net.minecraft.world.level.block.entity.SpawnerBlockEntity; -+import net.minecraft.world.level.block.state.BlockState; -+ -+public class SpawnerItem extends BlockItem { -+ -+ public SpawnerItem(Block block, Properties settings) { -+ super(block, settings); -+ } -+ -+ @Override -+ protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player, ItemStack stack, BlockState state) { -+ boolean handled = super.updateCustomBlockEntityTag(pos, level, player, stack, state); -+ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.place.spawners")) { -+ BlockEntity spawner = level.getBlockEntity(pos); -+ if (spawner instanceof SpawnerBlockEntity && stack.hasTag()) { -+ CompoundTag tag = stack.getTag(); -+ if (tag.contains("Purpur.mob_type")) { -+ EntityType.byString(tag.getString("Purpur.mob_type")).ifPresent(type -> -+ ((SpawnerBlockEntity) spawner).getSpawner().setEntityId(type, level, level.random, pos)); -+ } -+ } -+ } -+ return handled; -+ } -+} diff --git a/patches/server/0022-Add-turtle-egg-block-options.patch b/patches/server/0022-Add-turtle-egg-block-options.patch deleted file mode 100644 index d2cc049d4..000000000 --- a/patches/server/0022-Add-turtle-egg-block-options.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 6 Jun 2019 22:15:46 -0500 -Subject: [PATCH] Add turtle egg block options - - -diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -index 6c1a0e6f961e46a1a89850746a71e97b32514adf..1942649e868fc985a488034c411a6721595ecc67 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -193,6 +193,25 @@ public class TurtleEggBlock extends Block { - } - - private boolean canDestroyEgg(Level world, Entity entity) { -- return !(entity instanceof Turtle) && !(entity instanceof Bat) ? (!(entity instanceof LivingEntity) ? false : entity instanceof Player || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) : false; -+ // Purpur start -+ if (entity instanceof Turtle || entity instanceof Bat) { -+ return false; -+ } -+ if (world.purpurConfig.turtleEggsBreakFromExpOrbs && entity instanceof net.minecraft.world.entity.ExperienceOrb) { -+ return true; -+ } -+ if (world.purpurConfig.turtleEggsBreakFromItems && entity instanceof net.minecraft.world.entity.item.ItemEntity) { -+ return true; -+ } -+ if (world.purpurConfig.turtleEggsBreakFromMinecarts && entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) { -+ return true; -+ } -+ if (!(entity instanceof LivingEntity)) { -+ return false; -+ } -+ if (entity instanceof Player) return true; -+ -+ return world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ // Purpur end - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ef20a9601527281d052c531e7e769ea480622481..1b06b531cc334b00b8f12634bd36766619098f16 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -144,6 +144,15 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean turtleEggsBreakFromExpOrbs = true; -+ public boolean turtleEggsBreakFromItems = true; -+ public boolean turtleEggsBreakFromMinecarts = true; -+ private void turtleEggSettings() { -+ turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs); -+ turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems); -+ turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); -+ } -+ - public boolean babiesAreRidable = true; - public boolean untamedTamablesAreRidable = true; - public boolean useNightVisionWhenRiding = false; diff --git a/patches/server/0023-Logger-settings-suppressing-pointless-logs.patch b/patches/server/0023-Logger-settings-suppressing-pointless-logs.patch deleted file mode 100644 index de40b9614..000000000 --- a/patches/server/0023-Logger-settings-suppressing-pointless-logs.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 19 Oct 2019 00:52:12 -0500 -Subject: [PATCH] Logger settings (suppressing pointless logs) - - -diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java -index acc49f66bf34e2507d0ee6fec0a56b11bfc68f46..52891c4a4260d1938f2f4565b5219ad303555638 100644 ---- a/src/main/java/net/minecraft/server/PlayerAdvancements.java -+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java -@@ -147,6 +147,7 @@ public class PlayerAdvancements { - if (advancement == null) { - // CraftBukkit start - if (entry.getKey().getNamespace().equals("minecraft")) { -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressIgnoredAdvancementWarnings) // Purpur - PlayerAdvancements.LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry.getKey(), this.playerSavePath); - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java -index 877498729c66de9aa6a27c9148f7494d7895615c..acd7468ee3c86d3456e96e4ec3d7e6a4c612e89d 100644 ---- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java -+++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java -@@ -297,6 +297,7 @@ public class WorldGenRegion implements WorldGenLevel { - return true; - } else { - // Paper start -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressSetBlockFarChunk) // Purpur - if (!hasSetFarWarned) { - Util.logAndPauseIfInIde("Detected setBlock in a far chunk [" + i + ", " + j + "], pos: " + pos + ", status: " + this.generatingStatus + (this.currentlyGenerating == null ? "" : ", currently generating: " + (String) this.currentlyGenerating.get())); - hasSetFarWarned = true; -diff --git a/src/main/java/net/minecraft/stats/ServerRecipeBook.java b/src/main/java/net/minecraft/stats/ServerRecipeBook.java -index ea29e07a105f3ba6a878bdccf36e7eaf66280280..d5dce6c8d85938d61a57a78f82381d26daf8f87a 100644 ---- a/src/main/java/net/minecraft/stats/ServerRecipeBook.java -+++ b/src/main/java/net/minecraft/stats/ServerRecipeBook.java -@@ -125,6 +125,7 @@ public class ServerRecipeBook extends RecipeBook { - Optional> optional = recipeManager.byKey(minecraftkey); - - if (!optional.isPresent()) { -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressUnrecognizedRecipeErrors) // Purpur - ServerRecipeBook.LOGGER.error("Tried to load unrecognized recipe: {} removed now.", minecraftkey); - } else { - handler.accept((Recipe) optional.get()); -diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -index 2677e21d8239bf0361a3bc5c9a50c328e54d70f6..544a79d5da661aff19e2019f7b83a3a49350bb68 100644 ---- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -+++ b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -@@ -256,6 +256,7 @@ public final class CraftLegacy { - } - - static { -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressInitLegacyMaterialError) // Purpur - LOGGER.warn("Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug!"); // Paper - doesn't need to be an error - if (MinecraftServer.getServer() != null && MinecraftServer.getServer().isDebugging()) { - new Exception().printStackTrace(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index fa27bb42697cd5741b7308b34cd768232604a20f..9db231480f0763c2b6514a956ec9398b98b7d1bc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -237,4 +237,15 @@ public class PurpurConfig { - org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - } -+ -+ public static boolean loggerSuppressInitLegacyMaterialError = false; -+ public static boolean loggerSuppressIgnoredAdvancementWarnings = false; -+ public static boolean loggerSuppressUnrecognizedRecipeErrors = false; -+ public static boolean loggerSuppressSetBlockFarChunk = false; -+ private static void loggerSettings() { -+ loggerSuppressInitLegacyMaterialError = getBoolean("settings.logger.suppress-init-legacy-material-errors", loggerSuppressInitLegacyMaterialError); -+ loggerSuppressIgnoredAdvancementWarnings = getBoolean("settings.logger.suppress-ignored-advancement-warnings", loggerSuppressIgnoredAdvancementWarnings); -+ loggerSuppressUnrecognizedRecipeErrors = getBoolean("settings.logger.suppress-unrecognized-recipe-errors", loggerSuppressUnrecognizedRecipeErrors); -+ loggerSuppressSetBlockFarChunk = getBoolean("settings.logger.suppress-setblock-in-far-chunk-errors", loggerSuppressSetBlockFarChunk); -+ } - } diff --git a/patches/server/0025-Giants-AI-settings.patch b/patches/server/0025-Giants-AI-settings.patch deleted file mode 100644 index c472515ae..000000000 --- a/patches/server/0025-Giants-AI-settings.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 12 May 2019 00:43:12 -0500 -Subject: [PATCH] Giants AI settings - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java -index db95323da1aef267aa4fbe56aaff63cb8684e15b..12e27b36b3f9949eb644175dd346c487277b2d39 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -1,13 +1,36 @@ - package net.minecraft.world.entity.monster; - - import net.minecraft.core.BlockPos; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.world.Difficulty; -+import net.minecraft.world.DifficultyInstance; - import net.minecraft.world.entity.EntityDimensions; - import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.entity.EquipmentSlot; -+import net.minecraft.world.entity.MobSpawnType; - import net.minecraft.world.entity.Pose; -+import net.minecraft.world.entity.SpawnGroupData; - import net.minecraft.world.entity.ai.attributes.AttributeSupplier; - import net.minecraft.world.entity.ai.attributes.Attributes; -+import net.minecraft.world.entity.ai.goal.FloatGoal; -+import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; -+import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; -+import net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal; -+import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; -+import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; -+import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; -+import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; -+import net.minecraft.world.entity.animal.IronGolem; -+import net.minecraft.world.entity.animal.Turtle; -+import net.minecraft.world.entity.npc.Villager; -+import net.minecraft.world.entity.player.Player; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.item.Items; - import net.minecraft.world.level.Level; - import net.minecraft.world.level.LevelReader; -+import net.minecraft.world.level.ServerLevelAccessor; -+ -+import javax.annotation.Nullable; - - public class Giant extends Monster { - public Giant(EntityType type, Level world) { -@@ -33,8 +56,23 @@ public class Giant extends Monster { - - @Override - protected void registerGoals() { -- this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -- this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ if (level().purpurConfig.giantHaveAI) { -+ this.goalSelector.addGoal(0, new FloatGoal(this)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D)); -+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 16.0F)); -+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); -+ this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0D)); -+ if (level().purpurConfig.giantHaveHostileAI) { -+ this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class)); -+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); -+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Villager.class, false)); -+ this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); -+ this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, true)); -+ } -+ } - } - // Purpur end - -@@ -45,6 +83,32 @@ public class Giant extends Monster { - this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.giantAttackDamage); - } - -+ @Override -+ public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -+ SpawnGroupData groupData = super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); -+ if (groupData == null) { -+ populateDefaultEquipmentSlots(this.random, difficulty); -+ populateDefaultEquipmentEnchantments(this.random, difficulty); -+ } -+ return groupData; -+ } -+ -+ @Override -+ protected void populateDefaultEquipmentSlots(net.minecraft.util.RandomSource random, DifficultyInstance difficulty) { -+ super.populateDefaultEquipmentSlots(this.random, difficulty); -+ // TODO make configurable -+ if (random.nextFloat() < (level().getDifficulty() == Difficulty.HARD ? 0.1F : 0.05F)) { -+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD)); -+ } -+ } -+ -+ @Override -+ public float getJumpPower() { -+ // make giants jump as high as everything else relative to their size -+ // 1.0 makes bottom of feet about as high as their waist when they jump -+ return level().purpurConfig.giantJumpHeight; -+ } -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return 10.440001F; -@@ -56,6 +120,6 @@ public class Giant extends Monster { - - @Override - public float getWalkTargetValue(BlockPos pos, LevelReader world) { -- return world.getPathfindingCostFromLightLevels(pos); -+ return super.getWalkTargetValue(pos, world); // Purpur - fix light requirements for natural spawns - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1b06b531cc334b00b8f12634bd36766619098f16..916249a2ad6721182f0e71e785db3b50839d4961 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -558,6 +558,10 @@ public class PurpurWorldConfig { - public double giantMovementSpeed = 0.5D; - public double giantAttackDamage = 50.0D; - public double giantMaxHealth = 100.0D; -+ public float giantStepHeight = 2.0F; -+ public float giantJumpHeight = 1.0F; -+ public boolean giantHaveAI = false; -+ public boolean giantHaveHostileAI = false; - private void giantSettings() { - giantRidable = getBoolean("mobs.giant.ridable", giantRidable); - giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); -@@ -574,6 +578,10 @@ public class PurpurWorldConfig { - set("mobs.giant.attributes.max_health", oldValue); - } - giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth); -+ giantStepHeight = (float) getDouble("mobs.giant.step-height", giantStepHeight); -+ giantJumpHeight = (float) getDouble("mobs.giant.jump-height", giantJumpHeight); -+ giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI); -+ giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI); - } - - public boolean glowSquidRidable = false; diff --git a/patches/server/0026-Zombie-horse-naturally-spawn.patch b/patches/server/0026-Zombie-horse-naturally-spawn.patch deleted file mode 100644 index bcbc7122f..000000000 --- a/patches/server/0026-Zombie-horse-naturally-spawn.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 7 Jul 2019 19:52:16 -0500 -Subject: [PATCH] Zombie horse naturally spawn - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 4c3b5a26a6b04afff3a707929ced3c62b5256a67..11f92c1011a1accaf485e5785d2e9ebc8440406c 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -971,10 +971,18 @@ public class ServerLevel extends Level implements WorldGenLevel { - boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper - - if (flag1) { -- SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this); -+ // Purpur start -+ net.minecraft.world.entity.animal.horse.AbstractHorse entityhorseskeleton; -+ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) { -+ entityhorseskeleton = EntityType.ZOMBIE_HORSE.create(this); -+ } else { -+ entityhorseskeleton = EntityType.SKELETON_HORSE.create(this); -+ if (entityhorseskeleton != null) ((SkeletonHorse) entityhorseskeleton).setTrap(true); -+ } -+ // Purpur end - - if (entityhorseskeleton != null) { -- entityhorseskeleton.setTrap(true); -+ //entityhorseskeleton.setTrap(true); // Purpur - moved up - entityhorseskeleton.setAge(0); - entityhorseskeleton.setPos((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()); - this.addFreshEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 916249a2ad6721182f0e71e785db3b50839d4961..1f636491c6da434708359224107cfc1de1cd6034 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1488,6 +1488,7 @@ public class PurpurWorldConfig { - public double zombieHorseJumpStrengthMax = 1.0D; - public double zombieHorseMovementSpeedMin = 0.2D; - public double zombieHorseMovementSpeedMax = 0.2D; -+ public double zombieHorseSpawnChance = 0.0D; - private void zombieHorseSettings() { - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); - zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); -@@ -1503,6 +1504,7 @@ public class PurpurWorldConfig { - zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax); - zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin); - zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax); -+ zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance); - } - - public boolean zombieVillagerRidable = false; diff --git a/patches/server/0027-Charged-creeper-naturally-spawn.patch b/patches/server/0027-Charged-creeper-naturally-spawn.patch deleted file mode 100644 index 0071bedad..000000000 --- a/patches/server/0027-Charged-creeper-naturally-spawn.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 29 Nov 2019 22:37:44 -0600 -Subject: [PATCH] Charged creeper naturally spawn - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index e31094f270117dec3053620c06e409953fb6d710..a1e3f29ce49dc41ad70f74ee224250fe9ebea175 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -255,6 +255,14 @@ public class Creeper extends Monster implements PowerableMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.creeperMaxHealth); - } - -+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.MobSpawnType spawnReason, @Nullable net.minecraft.world.entity.SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -+ double chance = world.getLevel().purpurConfig.creeperChargedChance; -+ if (chance > 0D && random.nextDouble() <= chance) { -+ setPowered(true); -+ } -+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); -+ } -+ - @Override - protected SoundEvent getHurtSound(DamageSource source) { - return SoundEvents.CREEPER_HURT; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1f636491c6da434708359224107cfc1de1cd6034..607298a7fe2f2f7b2320a028d1343c1575decf31 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -349,6 +349,7 @@ public class PurpurWorldConfig { - public boolean creeperRidableInWater = true; - public boolean creeperControllable = true; - public double creeperMaxHealth = 20.0D; -+ public double creeperChargedChance = 0.0D; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -359,6 +360,7 @@ public class PurpurWorldConfig { - set("mobs.creeper.attributes.max_health", oldValue); - } - creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth); -+ creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance); - } - - public boolean dolphinRidable = false; diff --git a/patches/server/0028-Rabbit-naturally-spawn-toast-and-killer.patch b/patches/server/0028-Rabbit-naturally-spawn-toast-and-killer.patch deleted file mode 100644 index c7968f43b..000000000 --- a/patches/server/0028-Rabbit-naturally-spawn-toast-and-killer.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 31 Aug 2019 17:47:11 -0500 -Subject: [PATCH] Rabbit naturally spawn toast and killer - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -index 9be247def3ab8c74c569f653ec971aaf1b5ed5c9..6d5213d26dbd9d36c5cbe6056091d9e5c85e23c4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -468,10 +468,23 @@ public class Rabbit extends Animal implements VariantHolder { - } - - this.setVariant(entityrabbit_variant); -+ -+ // Purpur start -+ if (entityrabbit_variant != Variant.EVIL && world.getLevel().purpurConfig.rabbitNaturalToast > 0D && random.nextDouble() <= world.getLevel().purpurConfig.rabbitNaturalToast) { -+ setCustomName(Component.translatable("Toast")); -+ } -+ // Purpur end -+ - return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData, entityNbt); - } - - private static Rabbit.Variant getRandomRabbitVariant(LevelAccessor world, BlockPos pos) { -+ // Purpur start -+ Level level = world.getMinecraftWorld(); -+ if (level.purpurConfig.rabbitNaturalKiller > 0D && world.getRandom().nextDouble() <= level.purpurConfig.rabbitNaturalKiller) { -+ return Rabbit.Variant.EVIL; -+ } -+ // Purpur end - Holder holder = world.getBiome(pos); - int i = world.getRandom().nextInt(100); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 607298a7fe2f2f7b2320a028d1343c1575decf31..5974f2ec7f42cf5ea58a5cfa124f0165a5fc5785 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -992,6 +992,8 @@ public class PurpurWorldConfig { - public boolean rabbitRidableInWater = true; - public boolean rabbitControllable = true; - public double rabbitMaxHealth = 3.0D; -+ public double rabbitNaturalToast = 0.0D; -+ public double rabbitNaturalKiller = 0.0D; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -@@ -1002,6 +1004,8 @@ public class PurpurWorldConfig { - set("mobs.rabbit.attributes.max_health", oldValue); - } - rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth); -+ rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast); -+ rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller); - } - - public boolean ravagerRidable = false; diff --git a/patches/server/0029-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/patches/server/0029-Fix-outdated-server-showing-in-ping-before-server-fu.patch deleted file mode 100644 index ac019e68c..000000000 --- a/patches/server/0029-Fix-outdated-server-showing-in-ping-before-server-fu.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 4 Jun 2019 15:50:08 -0500 -Subject: [PATCH] Fix 'outdated server' showing in ping before server fully - boots - - -diff --git a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java -index 2c13147bc063a09bb7907d6f90c3a1e811a09eb1..3edb0c392cec7fdabebad81da1a8b06a700fbfca 100644 ---- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java -@@ -153,6 +153,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene - this.connection.send(new ClientboundStatusResponsePacket(ping)); - // CraftBukkit end - */ -+ if (MinecraftServer.getServer().getStatus().version().isEmpty()) return; // Purpur - do not respond to pings before we know the protocol version - com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(MinecraftServer.getServer(), this.connection); - // Paper end - } diff --git a/patches/server/0030-Tulips-change-fox-type.patch b/patches/server/0030-Tulips-change-fox-type.patch deleted file mode 100644 index 0fec3be3e..000000000 --- a/patches/server/0030-Tulips-change-fox-type.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 13 Jul 2019 15:56:22 -0500 -Subject: [PATCH] Tulips change fox type - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index d6370eb9af9842710052e0e497af52122f265488..cedc4dac776dde310dbf1a22272ce54b14ca2c9e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -35,6 +35,7 @@ import net.minecraft.util.RandomSource; - import net.minecraft.util.StringRepresentable; - import net.minecraft.world.DifficultyInstance; - import net.minecraft.world.InteractionHand; -+import net.minecraft.world.InteractionResult; - import net.minecraft.world.damagesource.DamageSource; - import net.minecraft.world.entity.AgeableMob; - import net.minecraft.world.entity.Entity; -@@ -88,6 +89,7 @@ import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.CaveVines; - import net.minecraft.world.level.block.SweetBerryBushBlock; - import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.gameevent.GameEvent; - import net.minecraft.world.level.pathfinder.BlockPathTypes; - import net.minecraft.world.phys.Vec3; - -@@ -387,6 +389,11 @@ public class Fox extends Animal implements VariantHolder { - } - - private void setTargetGoals() { -+ // Purpur start - do not add duplicate goals -+ this.targetSelector.removeGoal(this.landTargetGoal); -+ this.targetSelector.removeGoal(this.turtleEggTargetGoal); -+ this.targetSelector.removeGoal(this.fishTargetGoal); -+ // Purpur end - if (this.getVariant() == Fox.Type.RED) { - this.targetSelector.addGoal(4, this.landTargetGoal); - this.targetSelector.addGoal(4, this.turtleEggTargetGoal); -@@ -420,6 +427,7 @@ public class Fox extends Animal implements VariantHolder { - - public void setVariant(Fox.Type variant) { - this.entityData.set(Fox.DATA_TYPE_ID, variant.getId()); -+ this.setTargetGoals(); // Purpur - fix API bug not updating pathfinders on type change - } - - List getTrustedUUIDs() { -@@ -756,6 +764,29 @@ public class Fox extends Animal implements VariantHolder { - return this.getTrustedUUIDs().contains(uuid); - } - -+ // Purpur start -+ @Override -+ public InteractionResult mobInteract(Player player, InteractionHand hand) { -+ if (level().purpurConfig.foxTypeChangesWithTulips) { -+ ItemStack itemstack = player.getItemInHand(hand); -+ if (getVariant() == Type.RED && itemstack.getItem() == Items.WHITE_TULIP) { -+ setVariant(Type.SNOW); -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(1); -+ } -+ return InteractionResult.SUCCESS; -+ } else if (getVariant() == Type.SNOW && itemstack.getItem() == Items.ORANGE_TULIP) { -+ setVariant(Type.RED); -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(1); -+ } -+ return InteractionResult.SUCCESS; -+ } -+ } -+ return super.mobInteract(player, hand); -+ } -+ // Purpur end -+ - @Override - // Paper start - Cancellable death event - protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5974f2ec7f42cf5ea58a5cfa124f0165a5fc5785..d0b82fb2d43207f997b85c2a2d6abc3af98c2fbd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -513,6 +513,7 @@ public class PurpurWorldConfig { - public boolean foxRidableInWater = true; - public boolean foxControllable = true; - public double foxMaxHealth = 10.0D; -+ public boolean foxTypeChangesWithTulips = false; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -523,6 +524,7 @@ public class PurpurWorldConfig { - set("mobs.fox.attributes.max_health", oldValue); - } - foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth); -+ foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips); - } - - public boolean frogRidable = false; diff --git a/patches/server/0031-Breedable-Polar-Bears.patch b/patches/server/0031-Breedable-Polar-Bears.patch deleted file mode 100644 index b4d7eb90f..000000000 --- a/patches/server/0031-Breedable-Polar-Bears.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 26 Mar 2020 19:46:44 -0500 -Subject: [PATCH] Breedable Polar Bears - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -index 48ec595e76c09cf719477a543364f1206664afa5..b2cf680e377f849a7cc17136ebca3cf33e34048c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -@@ -99,6 +99,27 @@ public class PolarBear extends Animal implements NeutralMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.polarBearMaxHealth); - } - -+ public boolean canMate(Animal other) { -+ if (other == this) { -+ return false; -+ } else if (this.isStanding()) { -+ return false; -+ } else if (this.getTarget() != null) { -+ return false; -+ } else if (!(other instanceof PolarBear)) { -+ return false; -+ } else { -+ PolarBear bear = (PolarBear) other; -+ if (bear.isStanding()) { -+ return false; -+ } -+ if (bear.getTarget() != null) { -+ return false; -+ } -+ return this.isInLove() && bear.isInLove(); -+ } -+ } -+ - @Nullable - @Override - public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) { -@@ -107,7 +128,7 @@ public class PolarBear extends Animal implements NeutralMob { - - @Override - public boolean isFood(ItemStack stack) { -- return false; -+ return level().purpurConfig.polarBearBreedableItem != null && stack.getItem() == level().purpurConfig.polarBearBreedableItem; // Purpur - } - - @Override -@@ -117,6 +138,12 @@ public class PolarBear extends Animal implements NeutralMob { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal()); - this.goalSelector.addGoal(1, new PolarBear.PolarBearPanicGoal()); -+ // Purpur start -+ if (level().purpurConfig.polarBearBreedableItem != null) { -+ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); -+ this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, net.minecraft.world.item.crafting.Ingredient.of(level().purpurConfig.polarBearBreedableItem), false)); -+ } -+ // Purpur end - this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D)); - this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d0b82fb2d43207f997b85c2a2d6abc3af98c2fbd..f994a8eb776c2ad1029b447ebab0273eb01e302f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -964,6 +964,8 @@ public class PurpurWorldConfig { - public boolean polarBearRidableInWater = true; - public boolean polarBearControllable = true; - public double polarBearMaxHealth = 30.0D; -+ public String polarBearBreedableItemString = ""; -+ public Item polarBearBreedableItem = null; - private void polarBearSettings() { - polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); - polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); -@@ -974,6 +976,9 @@ public class PurpurWorldConfig { - set("mobs.polar_bear.attributes.max_health", oldValue); - } - polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth); -+ polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString); -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(polarBearBreedableItemString)); -+ if (item != Items.AIR) polarBearBreedableItem = item; - } - - public boolean pufferfishRidable = false; diff --git a/patches/server/0032-Chickens-can-retaliate.patch b/patches/server/0032-Chickens-can-retaliate.patch deleted file mode 100644 index dba975b0d..000000000 --- a/patches/server/0032-Chickens-can-retaliate.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 12 Apr 2020 13:19:34 -0500 -Subject: [PATCH] Chickens can retaliate - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index ea404a84a43a02a5614d5142bb78a586edfc69f6..0f49705e3c7adf033cee9d0746319885c830224f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -74,19 +74,30 @@ public class Chicken extends Animal { - @Override - public void initAttributes() { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.chickenMaxHealth); -+ if (level().purpurConfig.chickenRetaliate) { -+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D); -+ } - } - - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -- this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); -+ // this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); // Purpur - moved down - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.0D, Chicken.FOOD_ITEMS, false)); - this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1D)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); -+ // Purpur start -+ if (level().purpurConfig.chickenRetaliate) { -+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false)); -+ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this)); -+ } else { -+ this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); -+ } -+ // Purpur end - } - - @Override -@@ -95,7 +106,7 @@ public class Chicken extends Animal { - } - - public static AttributeSupplier.Builder createAttributes() { -- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D); -+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f994a8eb776c2ad1029b447ebab0273eb01e302f..535fc8b625b49c2451e17f5e73ea44b634adc6d9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -303,6 +303,7 @@ public class PurpurWorldConfig { - public boolean chickenRidableInWater = false; - public boolean chickenControllable = true; - public double chickenMaxHealth = 4.0D; -+ public boolean chickenRetaliate = false; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -@@ -313,6 +314,7 @@ public class PurpurWorldConfig { - set("mobs.chicken.attributes.max_health", oldValue); - } - chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth); -+ chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate); - } - - public boolean codRidable = false; diff --git a/patches/server/0033-Add-option-to-set-armorstand-step-height.patch b/patches/server/0033-Add-option-to-set-armorstand-step-height.patch deleted file mode 100644 index 32ac78889..000000000 --- a/patches/server/0033-Add-option-to-set-armorstand-step-height.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 6 Oct 2019 12:46:35 -0500 -Subject: [PATCH] Add option to set armorstand step height - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 1ecca8fdab2fb12390446fd8e134032f1d003f4a..391d0782245f490152776e3c377a8040ad254322 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -334,7 +334,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public double xOld; - public double yOld; - public double zOld; -- private float maxUpStep; -+ public float maxUpStep; // Purpur - private -> public - public boolean noPhysics; - protected final RandomSource random; - public int tickCount; -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -index 5c6e060a54ffb13c37ff5711992e964bdd59643d..5524a69952130ec38e151509ba7733459146d1b0 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -682,6 +682,7 @@ public class ArmorStand extends LivingEntity { - - @Override - public void tick() { -+ maxUpStep = level().purpurConfig.armorstandStepHeight; - // Paper start - if (!this.canTick) { - if (this.noTickPoseDirty) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 535fc8b625b49c2451e17f5e73ea44b634adc6d9..9ad3efdfdabb1a5a5c2c6a521521a55bf1aea1b2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -90,6 +90,11 @@ public class PurpurWorldConfig { - return value.isEmpty() ? fallback : value; - } - -+ public float armorstandStepHeight = 0.0F; -+ private void armorstandSettings() { -+ armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); -+ } -+ - public boolean idleTimeoutKick = true; - public boolean idleTimeoutTickNearbyEntities = true; - public boolean idleTimeoutCountAsSleeping = false; diff --git a/patches/server/0034-Cat-spawning-options.patch b/patches/server/0034-Cat-spawning-options.patch deleted file mode 100644 index 19bd37d8d..000000000 --- a/patches/server/0034-Cat-spawning-options.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 26 Dec 2019 18:52:55 -0600 -Subject: [PATCH] Cat spawning options - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java -index 5f407535298a31a34cfe114dd863fd6a9b977707..29c7e33fe961020e5a0007287fe9b6631689f1b8 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java -+++ b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java -@@ -30,7 +30,7 @@ public class CatSpawner implements CustomSpawner { - if (this.nextTick > 0) { - return 0; - } else { -- this.nextTick = 1200; -+ this.nextTick = world.purpurConfig.catSpawnDelay; // Purpur - Player player = world.getRandomPlayer(); - if (player == null) { - return 0; -@@ -63,11 +63,15 @@ public class CatSpawner implements CustomSpawner { - } - - private int spawnInVillage(ServerLevel world, BlockPos pos) { -- int i = 48; -+ // Purpur start -+ int range = world.purpurConfig.catSpawnVillageScanRange; -+ if (range <= 0) return 0; -+ - if (world.getPoiManager().getCountInRange((entry) -> { - return entry.is(PoiTypes.HOME); -- }, pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { -- List list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(48.0D, 8.0D, 48.0D)); -+ }, pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { -+ List list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(range, 8.0D, range)); -+ // Purpur end - if (list.size() < 5) { - return this.spawnCat(pos, world); - } -@@ -77,8 +81,11 @@ public class CatSpawner implements CustomSpawner { - } - - private int spawnInHut(ServerLevel world, BlockPos pos) { -- int i = 16; -- List list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(16.0D, 8.0D, 16.0D)); -+ // Purpur start -+ int range = world.purpurConfig.catSpawnSwampHutScanRange; -+ if (range <= 0) return 0; -+ List list = world.getEntitiesOfClass(Cat.class, (new AABB(pos)).inflate(range, 8.0D, range)); -+ // Purpur end - return list.size() < 1 ? this.spawnCat(pos, world) : 0; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9ad3efdfdabb1a5a5c2c6a521521a55bf1aea1b2..524401b0ecdbe8cf67447072aafa58293dc11bf9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -276,6 +276,9 @@ public class PurpurWorldConfig { - public boolean catRidableInWater = true; - public boolean catControllable = true; - public double catMaxHealth = 10.0D; -+ public int catSpawnDelay = 1200; -+ public int catSpawnSwampHutScanRange = 16; -+ public int catSpawnVillageScanRange = 48; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -286,6 +289,9 @@ public class PurpurWorldConfig { - set("mobs.cat.attributes.max_health", oldValue); - } - catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth); -+ catSpawnDelay = getInt("mobs.cat.spawn-delay", catSpawnDelay); -+ catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange); -+ catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange); - } - - public boolean caveSpiderRidable = false; diff --git a/patches/server/0035-Cows-eat-mushrooms.patch b/patches/server/0035-Cows-eat-mushrooms.patch deleted file mode 100644 index b8e231e08..000000000 --- a/patches/server/0035-Cows-eat-mushrooms.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 4 May 2019 01:10:30 -0500 -Subject: [PATCH] Cows eat mushrooms - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 621fe1c08dda65934437eeab679b6e5cd2d11d3d..28c7e240b7dd1957ccb6a45608b1d052a32dc18a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -2,6 +2,7 @@ package net.minecraft.world.entity.animal; - - import javax.annotation.Nullable; - import net.minecraft.core.BlockPos; -+import net.minecraft.core.particles.ParticleTypes; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.sounds.SoundEvents; -@@ -29,6 +30,7 @@ import net.minecraft.world.item.ItemUtils; - import net.minecraft.world.item.Items; - import net.minecraft.world.item.crafting.Ingredient; - import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; - // CraftBukkit start - import org.bukkit.craftbukkit.event.CraftEventFactory; -@@ -70,6 +72,7 @@ public class Cow extends Animal { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); -+ if (level().purpurConfig.cowFeedMushrooms > 0) this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT, Blocks.RED_MUSHROOM.asItem(), Blocks.BROWN_MUSHROOM.asItem()), false)); else // Purpur - this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT), false)); - this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D)); -@@ -125,6 +128,10 @@ public class Cow extends Animal { - - player.setItemInHand(hand, itemstack1); - return InteractionResult.sidedSuccess(this.level().isClientSide); -+ // Purpur start - feed mushroom to change to mooshroom -+ } else if (level().purpurConfig.cowFeedMushrooms > 0 && this.getType() != EntityType.MOOSHROOM && isMushroom(itemstack)) { -+ return this.feedMushroom(player, itemstack); -+ // Purpur end - } else { - return super.mobInteract(player, hand); - } -@@ -140,4 +147,69 @@ public class Cow extends Animal { - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return this.isBaby() ? dimensions.height * 0.95F : 1.3F; - } -+ -+ // Purpur start - feed mushroom to change to mooshroom -+ private int redMushroomsFed = 0; -+ private int brownMushroomsFed = 0; -+ -+ private boolean isMushroom(ItemStack stack) { -+ return stack.getItem() == Blocks.RED_MUSHROOM.asItem() || stack.getItem() == Blocks.BROWN_MUSHROOM.asItem(); -+ } -+ -+ private int incrementFeedCount(ItemStack stack) { -+ if (stack.getItem() == Blocks.RED_MUSHROOM.asItem()) { -+ return ++redMushroomsFed; -+ } else { -+ return ++brownMushroomsFed; -+ } -+ } -+ -+ private InteractionResult feedMushroom(Player player, ItemStack stack) { -+ level().broadcastEntityEvent(this, (byte) 18); // hearts -+ playSound(SoundEvents.COW_MILK, 1.0F, 1.0F); -+ if (incrementFeedCount(stack) < level().purpurConfig.cowFeedMushrooms) { -+ if (!player.getAbilities().instabuild) { -+ stack.shrink(1); -+ } -+ return InteractionResult.CONSUME; // require 5 mushrooms to transform (prevents mushroom duping) -+ } -+ MushroomCow mooshroom = EntityType.MOOSHROOM.create(level()); -+ if (mooshroom == null) { -+ return InteractionResult.PASS; -+ } -+ if (stack.getItem() == Blocks.BROWN_MUSHROOM.asItem()) { -+ mooshroom.setVariant(MushroomCow.MushroomType.BROWN); -+ } else { -+ mooshroom.setVariant(MushroomCow.MushroomType.RED); -+ } -+ mooshroom.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); -+ mooshroom.setHealth(this.getHealth()); -+ mooshroom.setAge(getAge()); -+ mooshroom.copyPosition(this); -+ mooshroom.setYBodyRot(this.yBodyRot); -+ mooshroom.setYHeadRot(this.getYHeadRot()); -+ mooshroom.yRotO = this.yRotO; -+ mooshroom.xRotO = this.xRotO; -+ if (this.hasCustomName()) { -+ mooshroom.setCustomName(this.getCustomName()); -+ } -+ if (CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { -+ return InteractionResult.PASS; -+ } -+ if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), mooshroom.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) { -+ return InteractionResult.PASS; -+ } -+ this.level().addFreshEntity(mooshroom); -+ this.remove(RemovalReason.DISCARDED); -+ if (!player.getAbilities().instabuild) { -+ stack.shrink(1); -+ } -+ for (int i = 0; i < 15; ++i) { -+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER, -+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1, -+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true); -+ } -+ return InteractionResult.SUCCESS; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 524401b0ecdbe8cf67447072aafa58293dc11bf9..e7268c1a19d6d6cf1166c7242f954f55463c6cfa 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -346,6 +346,7 @@ public class PurpurWorldConfig { - public boolean cowRidableInWater = true; - public boolean cowControllable = true; - public double cowMaxHealth = 10.0D; -+ public int cowFeedMushrooms = 0; - private void cowSettings() { - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); -@@ -356,6 +357,7 @@ public class PurpurWorldConfig { - set("mobs.cow.attributes.max_health", oldValue); - } - cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth); -+ cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); - } - - public boolean creeperRidable = false; diff --git a/patches/server/0036-Fix-cow-rotation-when-shearing-mooshroom.patch b/patches/server/0036-Fix-cow-rotation-when-shearing-mooshroom.patch deleted file mode 100644 index d0a54e840..000000000 --- a/patches/server/0036-Fix-cow-rotation-when-shearing-mooshroom.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 3 May 2019 23:53:16 -0500 -Subject: [PATCH] Fix cow rotation when shearing mooshroom - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 780a11339d2492dd607e3ef91867ffbb9062e2cd..b27c0f21354a78025f9d1664c560fb8799bced91 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -202,7 +202,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder -Date: Sun, 12 May 2019 01:14:46 -0500 -Subject: [PATCH] Pigs give saddle back - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Pig.java b/src/main/java/net/minecraft/world/entity/animal/Pig.java -index a22e5b6c13b48b46d16a859531d4231376bc1bfc..ff98a81f7104bbaf67ed85b8ad0946edf2a8c368 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Pig.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java -@@ -174,6 +174,17 @@ public class Pig extends Animal implements ItemSteerable, Saddleable { - public InteractionResult mobInteract(Player player, InteractionHand hand) { - boolean flag = this.isFood(player.getItemInHand(hand)); - -+ if (level().purpurConfig.pigGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) { -+ this.steering.setSaddle(false); -+ if (!player.getAbilities().instabuild) { -+ ItemStack saddle = new ItemStack(Items.SADDLE); -+ if (!player.getInventory().add(saddle)) { -+ player.drop(saddle, false); -+ } -+ } -+ return InteractionResult.SUCCESS; -+ } -+ - if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) { - if (!this.level().isClientSide) { - player.startRiding(this); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e7268c1a19d6d6cf1166c7242f954f55463c6cfa..8194ca9094c9689be094d175a53786a779852b67 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -915,6 +915,7 @@ public class PurpurWorldConfig { - public boolean pigRidableInWater = false; - public boolean pigControllable = true; - public double pigMaxHealth = 10.0D; -+ public boolean pigGiveSaddleBack = false; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -@@ -925,6 +926,7 @@ public class PurpurWorldConfig { - set("mobs.pig.attributes.max_health", oldValue); - } - pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth); -+ pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack); - } - - public boolean piglinRidable = false; diff --git a/patches/server/0038-Snowman-drop-and-put-back-pumpkin.patch b/patches/server/0038-Snowman-drop-and-put-back-pumpkin.patch deleted file mode 100644 index febcf70ca..000000000 --- a/patches/server/0038-Snowman-drop-and-put-back-pumpkin.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 3 May 2019 23:58:44 -0500 -Subject: [PATCH] Snowman drop and put back pumpkin - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index 3ffd9dd16c80399842ba138ed60abd66cd312dcb..bd9dd01e9140c2ad0ab9859b9a455f73ab04a253 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -191,6 +191,14 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - } - - return InteractionResult.sidedSuccess(this.level().isClientSide); -+ // Purpur start -+ } else if (level().purpurConfig.snowGolemPutPumpkinBack && !hasPumpkin() && itemstack.getItem() == Blocks.CARVED_PUMPKIN.asItem()) { -+ setPumpkin(true); -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(1); -+ } -+ return InteractionResult.SUCCESS; -+ // Purpur end - } else { - return tryRide(player, hand); // Purpur - } -@@ -202,6 +210,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - if (!this.level().isClientSide()) { - this.setPumpkin(false); - this.forceDrops = true; // CraftBukkit -+ if (level().purpurConfig.snowGolemDropsPumpkin) // Purpur - this.spawnAtLocation(new ItemStack(Items.CARVED_PUMPKIN), 1.7F); - this.forceDrops = false; // CraftBukkit - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8194ca9094c9689be094d175a53786a779852b67..80d937d3f2ee0fcfa7b955c1ff7252b5d9cd8e2b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1178,6 +1178,8 @@ public class PurpurWorldConfig { - public boolean snowGolemControllable = true; - public boolean snowGolemLeaveTrailWhenRidden = false; - public double snowGolemMaxHealth = 4.0D; -+ public boolean snowGolemDropsPumpkin = true; -+ public boolean snowGolemPutPumpkinBack = false; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1189,6 +1191,8 @@ public class PurpurWorldConfig { - set("mobs.snow_golem.attributes.max_health", oldValue); - } - snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth); -+ snowGolemDropsPumpkin = getBoolean("mobs.snow_golem.drop-pumpkin-when-sheared", snowGolemDropsPumpkin); -+ snowGolemPutPumpkinBack = getBoolean("mobs.snow_golem.pumpkin-can-be-added-back", snowGolemPutPumpkinBack); - } - - public boolean snifferRidable = false; diff --git a/patches/server/0039-Ender-dragon-always-drop-full-exp.patch b/patches/server/0039-Ender-dragon-always-drop-full-exp.patch deleted file mode 100644 index ae6e1f3e9..000000000 --- a/patches/server/0039-Ender-dragon-always-drop-full-exp.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 24 Aug 2019 14:42:54 -0500 -Subject: [PATCH] Ender dragon always drop full exp - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index 9e85b252bedb5d0d532170e7aa57e02f2660965a..3898653acb6127c88a94b5e7111a9cfecb2d136e 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -756,7 +756,7 @@ public class EnderDragon extends Mob implements Enemy { - boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT); - short short0 = 500; - -- if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) { -+ if (this.dragonFight != null && (level().purpurConfig.enderDragonAlwaysDropsFullExp || !this.dragonFight.hasPreviouslyKilledDragon())) { - short0 = 12000; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 80d937d3f2ee0fcfa7b955c1ff7252b5d9cd8e2b..245d658e2b0ac77b097a6572ea7dcbec13a98207 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -459,6 +459,7 @@ public class PurpurWorldConfig { - public boolean enderDragonControllable = true; - public double enderDragonMaxY = 320D; - public double enderDragonMaxHealth = 200.0D; -+ public boolean enderDragonAlwaysDropsFullExp = false; - private void enderDragonSettings() { - enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); - enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); -@@ -474,6 +475,7 @@ public class PurpurWorldConfig { - set("mobs.ender_dragon.attributes.max_health", oldValue); - } - enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth); -+ enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp); - } - - public boolean endermanRidable = false; diff --git a/patches/server/0040-Allow-soil-to-moisten-from-water-directly-under-it.patch b/patches/server/0040-Allow-soil-to-moisten-from-water-directly-under-it.patch deleted file mode 100644 index 880ff4e22..000000000 --- a/patches/server/0040-Allow-soil-to-moisten-from-water-directly-under-it.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 21 Jun 2019 14:37:10 -0500 -Subject: [PATCH] Allow soil to moisten from water directly under it - - -diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -index 5946f06f63b5694034bd027984a4925b0831d439..1776906e886edd511bfbe96b8a345438a3cd66f8 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -162,7 +162,7 @@ public class FarmBlock extends Block { - } - } - -- return false; -+ return ((ServerLevel) world).purpurConfig.farmlandGetsMoistFromBelow && world.getFluidState(pos.relative(Direction.DOWN)).is(FluidTags.WATER); // Purpur; - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 245d658e2b0ac77b097a6572ea7dcbec13a98207..115085ecee8a765921d91288962f9ad8ab2391fa 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -149,6 +149,11 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean farmlandGetsMoistFromBelow = false; -+ private void farmlandSettings() { -+ farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; diff --git a/patches/server/0041-Minecart-settings-and-WASD-controls.patch b/patches/server/0041-Minecart-settings-and-WASD-controls.patch deleted file mode 100644 index 243b0ac5c..000000000 --- a/patches/server/0041-Minecart-settings-and-WASD-controls.patch +++ /dev/null @@ -1,222 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 29 Jun 2019 02:32:40 -0500 -Subject: [PATCH] Minecart settings and WASD controls - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0b7ee1cbe2917dc81d6a740ec09e7aee9ff598cf..e5291d794f2abb719a574afef2b27d4df05d881f 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1113,6 +1113,7 @@ public class ServerPlayer extends Player { - if (this.isInvulnerableTo(source)) { - return false; - } else { -+ if (source.is(DamageTypeTags.IS_FALL) && getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) return false; // Purpur - boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL); - - if (!flag && isSpawnInvulnerable() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -index 9948a28dae4edba877c13ef0156be5ff58df3fa2..5cdcc5792f19766d2d55d16859f8e0f68bd6479b 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -@@ -108,11 +108,13 @@ public abstract class AbstractMinecart extends Entity { - private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision - private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision - public double maxSpeed = 0.4D; -+ public double storedMaxSpeed; // Purpur - // CraftBukkit end - - protected AbstractMinecart(EntityType type, Level world) { - super(type, world); - this.blocksBuilding = true; -+ if (world != null) maxSpeed = storedMaxSpeed = world.purpurConfig.minecartMaxSpeed; // Purpur - } - - protected AbstractMinecart(EntityType type, Level world, double x, double y, double z) { -@@ -335,6 +337,12 @@ public abstract class AbstractMinecart extends Entity { - - @Override - public void tick() { -+ // Purpur start -+ if (storedMaxSpeed != level().purpurConfig.minecartMaxSpeed) { -+ maxSpeed = storedMaxSpeed = level().purpurConfig.minecartMaxSpeed; -+ } -+ // Purpur end -+ - // CraftBukkit start - double prevX = this.getX(); - double prevY = this.getY(); -@@ -499,16 +507,62 @@ public abstract class AbstractMinecart extends Entity { - - public void activateMinecart(int x, int y, int z, boolean powered) {} - -+ // Purpur start -+ private Double lastSpeed; -+ -+ public double getControllableSpeed() { -+ BlockState blockState = level().getBlockState(this.blockPosition()); -+ if (!blockState.isSolid()) { -+ blockState = level().getBlockState(this.blockPosition().relative(Direction.DOWN)); -+ } -+ Double speed = level().purpurConfig.minecartControllableBlockSpeeds.get(blockState.getBlock()); -+ if (!blockState.isSolid()) { -+ speed = lastSpeed; -+ } -+ if (speed == null) { -+ speed = level().purpurConfig.minecartControllableBaseSpeed; -+ } -+ return lastSpeed = speed; -+ } -+ // Purpur end -+ - protected void comeOffTrack() { - double d0 = this.getMaxSpeed(); - Vec3 vec3d = this.getDeltaMovement(); - - this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0)); -+ -+ // Purpur start -+ if (level().purpurConfig.minecartControllable && !isInWater() && !isInLava() && !passengers.isEmpty()) { -+ Entity passenger = passengers.get(0); -+ if (passenger instanceof Player) { -+ Player player = (Player) passenger; -+ if (player.jumping && this.onGround) { -+ setDeltaMovement(new Vec3(getDeltaMovement().x, level().purpurConfig.minecartControllableHopBoost, getDeltaMovement().z)); -+ } -+ if (player.zza != 0.0F) { -+ Vector velocity = player.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed()); -+ if (player.zza < 0.0) { -+ velocity.multiply(-0.5); -+ } -+ setDeltaMovement(new Vec3(velocity.getX(), getDeltaMovement().y, velocity.getZ())); -+ } -+ this.setYRot(passenger.getYRot() - 90); -+ maxUpStep = level().purpurConfig.minecartControllableStepHeight; -+ } else { -+ maxUpStep = 0.0F; -+ } -+ } else { -+ maxUpStep = 0.0F; -+ } -+ // Purpur end -+ - if (this.onGround()) { - // CraftBukkit start - replace magic numbers with our variables - this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ)); - // CraftBukkit end - } -+ else if (level().purpurConfig.minecartControllable) setDeltaMovement(new Vec3(getDeltaMovement().x * derailedX, getDeltaMovement().y, getDeltaMovement().z * derailedZ)); // Purpur - - this.move(MoverType.SELF, this.getDeltaMovement()); - if (!this.onGround()) { -diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java -index c6d2f764efa9b8bec730bbe757d480e365b25ccc..cef98413d25dcc2def82775bbae71f92b096d905 100644 ---- a/src/main/java/net/minecraft/world/item/MinecartItem.java -+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java -@@ -120,8 +120,9 @@ public class MinecartItem extends Item { - BlockState iblockdata = world.getBlockState(blockposition); - - if (!iblockdata.is(BlockTags.RAILS)) { -- return InteractionResult.FAIL; -- } else { -+ if (!world.purpurConfig.minecartPlaceAnywhere) return InteractionResult.FAIL; -+ if (iblockdata.isSolid()) blockposition = blockposition.relative(context.getClickedFace()); -+ } // else { // Purpur - place minecarts anywhere - ItemStack itemstack = context.getItemInHand(); - - if (!world.isClientSide) { -@@ -149,6 +150,6 @@ public class MinecartItem extends Item { - - itemstack.shrink(1); - return InteractionResult.sidedSuccess(world.isClientSide); -- } -+ // } // Purpur - place minecarts anywhere - } - } -diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index de4c1e4701236e7d5ec77339c51ad6a9d8288bb6..5ac102afde62c08f36886b466010ccfedabfa05e 100644 ---- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -89,7 +89,7 @@ public abstract class BlockBehaviour implements FeatureElement { - protected final float jumpFactor; - protected final boolean dynamicShape; - protected final FeatureFlagSet requiredFeatures; -- protected final BlockBehaviour.Properties properties; -+ public final BlockBehaviour.Properties properties; // Purpur - protected -> public - @Nullable - protected ResourceLocation drops; - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 115085ecee8a765921d91288962f9ad8ab2391fa..df1d4a3e6cad6f8e73f28dc2c1da6c9821f6e01f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -95,6 +95,68 @@ public class PurpurWorldConfig { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - } - -+ public double minecartMaxSpeed = 0.4D; -+ public boolean minecartPlaceAnywhere = false; -+ public boolean minecartControllable = false; -+ public float minecartControllableStepHeight = 1.0F; -+ public double minecartControllableHopBoost = 0.5D; -+ public boolean minecartControllableFallDamage = true; -+ public double minecartControllableBaseSpeed = 0.1D; -+ public Map minecartControllableBlockSpeeds = new HashMap<>(); -+ private void minecartSettings() { -+ if (PurpurConfig.version < 12) { -+ boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere); -+ set("gameplay-mechanics.controllable-minecarts.place-anywhere", null); -+ set("gameplay-mechanics.minecart.place-anywhere", oldBool); -+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", minecartControllable); -+ set("gameplay-mechanics.controllable-minecarts.enabled", null); -+ set("gameplay-mechanics.minecart.controllable.enabled", oldBool); -+ double oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.step-height", minecartControllableStepHeight); -+ set("gameplay-mechanics.controllable-minecarts.step-height", null); -+ set("gameplay-mechanics.minecart.controllable.step-height", oldDouble); -+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", minecartControllableHopBoost); -+ set("gameplay-mechanics.controllable-minecarts.hop-boost", null); -+ set("gameplay-mechanics.minecart.controllable.hop-boost", oldDouble); -+ oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", minecartControllableFallDamage); -+ set("gameplay-mechanics.controllable-minecarts.fall-damage", null); -+ set("gameplay-mechanics.minecart.controllable.fall-damage", oldBool); -+ oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", minecartControllableBaseSpeed); -+ set("gameplay-mechanics.controllable-minecarts.base-speed", null); -+ set("gameplay-mechanics.minecart.controllable.base-speed", oldDouble); -+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed"); -+ if (section != null) { -+ for (String key : section.getKeys(false)) { -+ if ("grass-block".equals(key)) key = "grass_block"; // oopsie -+ oldDouble = section.getDouble(key, minecartControllableBaseSpeed); -+ set("gameplay-mechanics.controllable-minecarts.block-speed." + key, null); -+ set("gameplay-mechanics.minecart.controllable.block-speed." + key, oldDouble); -+ } -+ set("gameplay-mechanics.controllable-minecarts.block-speed", null); -+ } -+ set("gameplay-mechanics.controllable-minecarts", null); -+ } -+ -+ minecartMaxSpeed = getDouble("gameplay-mechanics.minecart.max-speed", minecartMaxSpeed); -+ minecartPlaceAnywhere = getBoolean("gameplay-mechanics.minecart.place-anywhere", minecartPlaceAnywhere); -+ minecartControllable = getBoolean("gameplay-mechanics.minecart.controllable.enabled", minecartControllable); -+ minecartControllableStepHeight = (float) getDouble("gameplay-mechanics.minecart.controllable.step-height", minecartControllableStepHeight); -+ minecartControllableHopBoost = getDouble("gameplay-mechanics.minecart.controllable.hop-boost", minecartControllableHopBoost); -+ minecartControllableFallDamage = getBoolean("gameplay-mechanics.minecart.controllable.fall-damage", minecartControllableFallDamage); -+ minecartControllableBaseSpeed = getDouble("gameplay-mechanics.minecart.controllable.base-speed", minecartControllableBaseSpeed); -+ ConfigurationSection section = getConfigurationSection("gameplay-mechanics.minecart.controllable.block-speed"); -+ if (section != null) { -+ for (String key : section.getKeys(false)) { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key)); -+ if (block != Blocks.AIR) { -+ minecartControllableBlockSpeeds.put(block, section.getDouble(key, minecartControllableBaseSpeed)); -+ } -+ } -+ } else { -+ set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D); -+ set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D); -+ } -+ } -+ - public boolean idleTimeoutKick = true; - public boolean idleTimeoutTickNearbyEntities = true; - public boolean idleTimeoutCountAsSleeping = false; diff --git a/patches/server/0042-Disable-loot-drops-on-death-by-cramming.patch b/patches/server/0042-Disable-loot-drops-on-death-by-cramming.patch deleted file mode 100644 index 3031c7f04..000000000 --- a/patches/server/0042-Disable-loot-drops-on-death-by-cramming.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 3 Jul 2019 23:58:31 -0500 -Subject: [PATCH] Disable loot drops on death by cramming - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 92745269118d8a0cb91cbbebdd7700f7737d2b9a..b3699225e2dc77c6d201e361c8d447395c3f3e62 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1801,6 +1801,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - this.dropEquipment(); // CraftBukkit - from below - if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { -+ if (!(source.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level().purpurConfig.disableDropsOnCrammingDeath)) { // Purpur - this.dropFromLootTable(source, flag); - // Paper start - final boolean prev = this.clearEquipmentSlots; -@@ -1809,6 +1810,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - // Paper end - this.dropCustomDeathLoot(source, i, flag); - this.clearEquipmentSlots = prev; // Paper -+ } // Purpur - } - // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment - org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops, () -> { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index df1d4a3e6cad6f8e73f28dc2c1da6c9821f6e01f..a1690b8cc9843b17103fc5906244ffbe72eec574 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -95,6 +95,11 @@ public class PurpurWorldConfig { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - } - -+ public boolean disableDropsOnCrammingDeath = false; -+ private void miscGameplayMechanicsSettings() { -+ disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); -+ } -+ - public double minecartMaxSpeed = 0.4D; - public boolean minecartPlaceAnywhere = false; - public boolean minecartControllable = false; diff --git a/patches/server/0043-Option-to-toggle-milk-curing-bad-omen.patch b/patches/server/0043-Option-to-toggle-milk-curing-bad-omen.patch deleted file mode 100644 index 1f95ecfd8..000000000 --- a/patches/server/0043-Option-to-toggle-milk-curing-bad-omen.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 10 Jul 2019 20:43:05 -0500 -Subject: [PATCH] Option to toggle milk curing bad omen - - -diff --git a/src/main/java/net/minecraft/world/item/MilkBucketItem.java b/src/main/java/net/minecraft/world/item/MilkBucketItem.java -index f33977d95b6db473be4f95075ba99caf90ad0220..56dc04d8875971ee9a5d077a695509af74fe2473 100644 ---- a/src/main/java/net/minecraft/world/item/MilkBucketItem.java -+++ b/src/main/java/net/minecraft/world/item/MilkBucketItem.java -@@ -5,6 +5,8 @@ import net.minecraft.server.level.ServerPlayer; - import net.minecraft.stats.Stats; - import net.minecraft.world.InteractionHand; - import net.minecraft.world.InteractionResultHolder; -+import net.minecraft.world.effect.MobEffectInstance; -+import net.minecraft.world.effect.MobEffects; - import net.minecraft.world.entity.LivingEntity; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.level.Level; -@@ -31,7 +33,9 @@ public class MilkBucketItem extends Item { - } - - if (!world.isClientSide) { -+ MobEffectInstance badOmen = user.getEffect(MobEffects.BAD_OMEN); - user.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.MILK); // CraftBukkit -+ if (!world.purpurConfig.milkCuresBadOmen && badOmen != null) user.addEffect(badOmen); // Purpur - } - - return stack.isEmpty() ? new ItemStack(Items.BUCKET) : stack; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a1690b8cc9843b17103fc5906244ffbe72eec574..0549181f25cc9a31ea6ad9da8170c983667d6bb3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -96,8 +96,10 @@ public class PurpurWorldConfig { - } - - public boolean disableDropsOnCrammingDeath = false; -+ public boolean milkCuresBadOmen = true; - private void miscGameplayMechanicsSettings() { - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); -+ milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0045-Skip-events-if-there-s-no-listeners.patch b/patches/server/0045-Skip-events-if-there-s-no-listeners.patch deleted file mode 100644 index f0df55dd0..000000000 --- a/patches/server/0045-Skip-events-if-there-s-no-listeners.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 4 Apr 2020 03:07:59 -0500 -Subject: [PATCH] Skip events if there's no listeners - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 80c2c8d565f03ae0ea24fbdecdbe2bc5b9aa4b82..147fcbdd784f06fe23138b20aef82f92ba154998 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -456,6 +456,7 @@ public class Commands { - private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootcommandnode) { - // Paper end - Async command map building - new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper -+ if (PlayerCommandSendEvent.getHandlerList().getRegisteredListeners().length > 0) { // Purpur - skip all this crap if there's nothing listening - PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); - event.getPlayer().getServer().getPluginManager().callEvent(event); - -@@ -466,6 +467,7 @@ public class Commands { - } - } - // CraftBukkit end -+ } // Purpur - skip event - player.connection.send(new ClientboundCommandsPacket(rootcommandnode)); - } - diff --git a/patches/server/0046-Add-permission-for-F3-N-debug.patch b/patches/server/0046-Add-permission-for-F3-N-debug.patch deleted file mode 100644 index e994c7408..000000000 --- a/patches/server/0046-Add-permission-for-F3-N-debug.patch +++ /dev/null @@ -1,18 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 28 Dec 2019 04:21:54 -0600 -Subject: [PATCH] Add permission for F3+N debug - - -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 2cac536ebbc21a23a2219c630fe477bf9c00ce65..9cecf7d3e2f860f9de5dd13a352d8d7ed6f95266 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1164,6 +1164,7 @@ public abstract class PlayerList { - } else { - b0 = (byte) (24 + permissionLevel); - } -+ if (b0 < 28 && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b0 = 28; // Purpur - - player.connection.send(new ClientboundEntityEventPacket(player, b0)); - } diff --git a/patches/server/0047-Configurable-TPS-Catchup.patch b/patches/server/0047-Configurable-TPS-Catchup.patch deleted file mode 100644 index b6af7834e..000000000 --- a/patches/server/0047-Configurable-TPS-Catchup.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 26 Mar 2020 19:06:22 -0500 -Subject: [PATCH] Configurable TPS Catchup - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9abd0167f644194367fab679f878c0288758d898..82cd8c65268838f0bad3b15a8a228fb5b9a30b07 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1175,7 +1175,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Thu, 19 Mar 2020 19:39:34 -0500 -Subject: [PATCH] Add option to allow loyalty on tridents to work in the void - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java -index a6bc277b6589dd7104566542733327822d6299a4..dde841cc09ba4a3575a462b03537887551d47ba5 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java -@@ -62,7 +62,7 @@ public class ThrownTrident extends AbstractArrow { - Entity entity = this.getOwner(); - byte b0 = (Byte) this.entityData.get(ThrownTrident.ID_LOYALTY); - -- if (b0 > 0 && (this.dealtDamage || this.isNoPhysics()) && entity != null) { -+ if (b0 > 0 && (this.dealtDamage || this.isNoPhysics() || (level().purpurConfig.tridentLoyaltyVoidReturnHeight < 0.0D && getY() < level().purpurConfig.tridentLoyaltyVoidReturnHeight)) && entity != null) { // Purpur - if (!this.isAcceptibleReturnOwner()) { - if (!this.level().isClientSide && this.pickup == AbstractArrow.Pickup.ALLOWED) { - this.spawnAtLocation(this.getPickupItem(), 0.1F); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0549181f25cc9a31ea6ad9da8170c983667d6bb3..a7eee5311c4a697c1effcbb9a65f45529ab65c49 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -97,9 +97,11 @@ public class PurpurWorldConfig { - - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; -+ public double tridentLoyaltyVoidReturnHeight = 0.0D; - private void miscGameplayMechanicsSettings() { - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); -+ tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0049-Add-enderman-and-creeper-griefing-controls.patch b/patches/server/0049-Add-enderman-and-creeper-griefing-controls.patch deleted file mode 100644 index aa55998a8..000000000 --- a/patches/server/0049-Add-enderman-and-creeper-griefing-controls.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 26 Apr 2020 16:28:38 -0500 -Subject: [PATCH] Add enderman and creeper griefing controls - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index a1e3f29ce49dc41ad70f74ee224250fe9ebea175..5bfeb39a23a72f57523a3c2db1d79c7aef70eb02 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -362,7 +362,7 @@ public class Creeper extends Monster implements PowerableMob { - if (!event.isCancelled()) { - // CraftBukkit end - this.dead = true; -- this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit -+ this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), this.level().getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) && level().purpurConfig.creeperAllowGriefing ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE); // CraftBukkit // Purpur - this.discard(); - this.spawnLingeringCloud(); - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 948d352e01d0f167c25401a0c0ce8c1ef8553a24..74d1a0e8de36aaf11e844cc4f40e4c469d6269b4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -540,6 +540,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean canUse() { -+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - return this.enderman.getCarriedBlock() == null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0); - } - -@@ -585,6 +586,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean canUse() { -+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - return this.enderman.getCarriedBlock() != null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a7eee5311c4a697c1effcbb9a65f45529ab65c49..f929b11d9cdb172a3092a9105cae2361034db21b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -441,6 +441,7 @@ public class PurpurWorldConfig { - public boolean creeperControllable = true; - public double creeperMaxHealth = 20.0D; - public double creeperChargedChance = 0.0D; -+ public boolean creeperAllowGriefing = true; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -452,6 +453,7 @@ public class PurpurWorldConfig { - } - creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth); - creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance); -+ creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing); - } - - public boolean dolphinRidable = false; -@@ -558,6 +560,7 @@ public class PurpurWorldConfig { - public boolean endermanRidableInWater = true; - public boolean endermanControllable = true; - public double endermanMaxHealth = 40.0D; -+ public boolean endermanAllowGriefing = true; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -568,6 +571,7 @@ public class PurpurWorldConfig { - set("mobs.enderman.attributes.max_health", oldValue); - } - endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); -+ endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0050-Villagers-follow-emerald-blocks.patch b/patches/server/0050-Villagers-follow-emerald-blocks.patch deleted file mode 100644 index 4943516ef..000000000 --- a/patches/server/0050-Villagers-follow-emerald-blocks.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 29 Nov 2019 22:10:12 -0600 -Subject: [PATCH] Villagers follow emerald blocks - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java -index 0d9b194781d152e842c9a4b8d6f23d307b2e4452..00cf59524477ec79d4354cc403fc3e75a63b81a0 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java -@@ -62,7 +62,7 @@ public class TemptGoal extends Goal { - } - - private boolean shouldFollow(LivingEntity entity) { -- return this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem()); -+ return (this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem())) && (!(this.mob instanceof net.minecraft.world.entity.npc.Villager villager) || !villager.isSleeping()); // Purpur Fix #512 - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java -index 564908ce0a560c2190fb624e77d227d3b7031024..f2a4e214744227f1df32e3782e71f8a9d5cea261 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java -@@ -43,6 +43,7 @@ import org.bukkit.event.entity.VillagerAcquireTradeEvent; - // CraftBukkit end - - public abstract class AbstractVillager extends AgeableMob implements InventoryCarrier, Npc, Merchant { -+ static final net.minecraft.world.item.crafting.Ingredient TEMPT_ITEMS = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.level.block.Blocks.EMERALD_BLOCK.asItem()); // Purpur - - // CraftBukkit start - private CraftMerchant craftMerchant; -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 773e9ea9036ecfe48cae481484e9f5e64b6cc29b..91a44381a822ce4dc543401a930d32c8d4a5a7eb 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -154,6 +154,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - this.getNavigation().setCanFloat(true); - this.setCanPickUpLoot(true); - this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); -+ if (level().purpurConfig.villagerFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); - } - - // Purpur start -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 33702273c7fabdb8e5bfb3d0e15530f109e318be..509bde6c02d9e74158da7a134fbdda1603cb3ec0 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -108,6 +108,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - this.goalSelector.addGoal(1, new PanicGoal(this, 0.5D)); - this.goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this)); - this.goalSelector.addGoal(2, new WanderingTrader.WanderToPositionGoal(this, 2.0D, 0.35D)); -+ if (level().purpurConfig.wanderingTraderFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); // Purpur - this.goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.35D)); - this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 0.35D)); - this.goalSelector.addGoal(9, new InteractGoal(this, Player.class, 3.0F, 1.0F)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f929b11d9cdb172a3092a9105cae2361034db21b..e2667728ea44292ae05e80f1a27ed3edb36db7b1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1439,6 +1439,7 @@ public class PurpurWorldConfig { - public boolean villagerRidableInWater = true; - public boolean villagerControllable = true; - public double villagerMaxHealth = 20.0D; -+ public boolean villagerFollowEmeraldBlock = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1449,6 +1450,7 @@ public class PurpurWorldConfig { - set("mobs.villager.attributes.max_health", oldValue); - } - villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth); -+ villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock); - } - - public boolean vindicatorRidable = false; -@@ -1471,6 +1473,7 @@ public class PurpurWorldConfig { - public boolean wanderingTraderRidableInWater = true; - public boolean wanderingTraderControllable = true; - public double wanderingTraderMaxHealth = 20.0D; -+ public boolean wanderingTraderFollowEmeraldBlock = false; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -1481,6 +1484,7 @@ public class PurpurWorldConfig { - set("mobs.wandering_trader.attributes.max_health", oldValue); - } - wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth); -+ wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0051-Allow-leashing-villagers.patch b/patches/server/0051-Allow-leashing-villagers.patch deleted file mode 100644 index 06bd585cd..000000000 --- a/patches/server/0051-Allow-leashing-villagers.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 3 Oct 2019 18:08:03 -0500 -Subject: [PATCH] Allow leashing villagers - - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 1f4c41521c9d6fd781f96c7d9552c8e55bbf347b..5241abca7ef49c0891c28e1be6fafb791b8ea347 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1308,6 +1308,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - if (!this.isAlive()) { - return InteractionResult.PASS; - } else if (this.getLeashHolder() == player) { -+ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.AbstractVillager) return InteractionResult.CONSUME; // Purpur - // CraftBukkit start - fire PlayerUnleashEntityEvent - // Paper start - drop leash variable - org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.getAbilities().instabuild); -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 91a44381a822ce4dc543401a930d32c8d4a5a7eb..50925ab7d719ae3323d7456df03a3d2ab3481bfd 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -184,6 +184,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.villagerMaxHealth); - } - -+ @Override -+ public boolean canBeLeashed(Player player) { -+ return level().purpurConfig.villagerCanBeLeashed && !this.isLeashed(); -+ } -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 509bde6c02d9e74158da7a134fbdda1603cb3ec0..7413f90dfcb68476f7d514607f38a8965dfa6fd0 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -88,6 +88,11 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wanderingTraderMaxHealth); - } - -+ @Override -+ public boolean canBeLeashed(Player player) { -+ return level().purpurConfig.wanderingTraderCanBeLeashed && !this.isLeashed(); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e2667728ea44292ae05e80f1a27ed3edb36db7b1..ab42f93a621759a86c7e9eba14cb32b156d4f106 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1440,6 +1440,7 @@ public class PurpurWorldConfig { - public boolean villagerControllable = true; - public double villagerMaxHealth = 20.0D; - public boolean villagerFollowEmeraldBlock = false; -+ public boolean villagerCanBeLeashed = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1451,6 +1452,7 @@ public class PurpurWorldConfig { - } - villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth); - villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock); -+ villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed); - } - - public boolean vindicatorRidable = false; -@@ -1474,6 +1476,7 @@ public class PurpurWorldConfig { - public boolean wanderingTraderControllable = true; - public double wanderingTraderMaxHealth = 20.0D; - public boolean wanderingTraderFollowEmeraldBlock = false; -+ public boolean wanderingTraderCanBeLeashed = false; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -1485,6 +1488,7 @@ public class PurpurWorldConfig { - } - wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth); - wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock); -+ wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0052-Implement-infinite-liquids.patch b/patches/server/0052-Implement-infinite-liquids.patch deleted file mode 100644 index 3a90357a5..000000000 --- a/patches/server/0052-Implement-infinite-liquids.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 23 Nov 2019 17:55:42 -0600 -Subject: [PATCH] Implement infinite liquids - - -diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 7d56693102ee558fe784e3a9b9fdcff4b7ad57b9..485bbec4b52f126393332d5cad2d97c2a220f91a 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -227,7 +227,7 @@ public abstract class FlowingFluid extends Fluid { - } - } - -- if (this.canConvertToSource(world) && j >= 2) { -+ if (this.canConvertToSource(world) && j >= getRequiredSources(world)) { - BlockState iblockdata2 = world.getBlockState(pos.below()); - FluidState fluid1 = iblockdata2.getFluidState(); - -@@ -325,6 +325,12 @@ public abstract class FlowingFluid extends Fluid { - - protected abstract boolean canConvertToSource(Level world); - -+ // Purpur start -+ protected int getRequiredSources(Level level) { -+ return 2; -+ } -+ // Purpur end -+ - protected void spreadTo(LevelAccessor world, BlockPos pos, BlockState state, Direction direction, FluidState fluidState) { - if (state.getBlock() instanceof LiquidBlockContainer) { - ((LiquidBlockContainer) state.getBlock()).placeLiquid(world, pos, state, fluidState); -diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -index 72f8b72c6436ca3b8eaeb39c7d3efe2c1462ae1d..3706ebc551413401b0e6a9a0b1c2e3257d1337c1 100644 ---- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -@@ -198,6 +198,13 @@ public abstract class LavaFluid extends FlowingFluid { - world.levelEvent(1501, pos, 0); - } - -+ // Purpur start -+ @Override -+ protected int getRequiredSources(Level level) { -+ return level.purpurConfig.lavaInfiniteRequiredSources; -+ } -+ // Purpur end -+ - @Override - protected boolean canConvertToSource(Level world) { - return world.getGameRules().getBoolean(GameRules.RULE_LAVA_SOURCE_CONVERSION); -diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java -index 82e85fbbd45244d02df90fa00c9046e7f51275a2..ec6c63075306f9e5389e83641d2c8a82369ddc6b 100644 ---- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java -@@ -64,6 +64,13 @@ public abstract class WaterFluid extends FlowingFluid { - return world.getGameRules().getBoolean(GameRules.RULE_WATER_SOURCE_CONVERSION); - } - -+ // Purpur start -+ @Override -+ protected int getRequiredSources(Level level) { -+ return level.purpurConfig.waterInfiniteRequiredSources; -+ } -+ // Purpur end -+ - // Paper start - @Override - protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ab42f93a621759a86c7e9eba14cb32b156d4f106..e72a61c0e3aeb9758245101621a5aa286f5f5658 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -225,6 +225,11 @@ public class PurpurWorldConfig { - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); - } - -+ public int lavaInfiniteRequiredSources = 2; -+ private void lavaSettings() { -+ lavaInfiniteRequiredSources = getInt("blocks.lava.infinite-required-sources", lavaInfiniteRequiredSources); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; -@@ -234,6 +239,11 @@ public class PurpurWorldConfig { - turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); - } - -+ public int waterInfiniteRequiredSources = 2; -+ private void waterSources() { -+ waterInfiniteRequiredSources = getInt("blocks.water.infinite-required-sources", waterInfiniteRequiredSources); -+ } -+ - public boolean babiesAreRidable = true; - public boolean untamedTamablesAreRidable = true; - public boolean useNightVisionWhenRiding = false; diff --git a/patches/server/0053-Make-lava-flow-speed-configurable.patch b/patches/server/0053-Make-lava-flow-speed-configurable.patch deleted file mode 100644 index 3d008c47e..000000000 --- a/patches/server/0053-Make-lava-flow-speed-configurable.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 2 Jan 2020 11:31:36 -0600 -Subject: [PATCH] Make lava flow speed configurable - - -diff --git a/src/main/java/net/minecraft/world/level/material/LavaFluid.java b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -index 3706ebc551413401b0e6a9a0b1c2e3257d1337c1..b77cdbd8a7395e8442081c6a2b14695d62c9ef03 100644 ---- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -@@ -180,7 +180,7 @@ public abstract class LavaFluid extends FlowingFluid { - - @Override - public int getTickDelay(LevelReader world) { -- return world.dimensionType().ultraWarm() ? 10 : 30; -+ return world.dimensionType().ultraWarm() ? world.getWorldBorder().world.purpurConfig.lavaSpeedNether : world.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e72a61c0e3aeb9758245101621a5aa286f5f5658..bb5877d6feec92e87c13e33abfda48507201d08e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -226,8 +226,12 @@ public class PurpurWorldConfig { - } - - public int lavaInfiniteRequiredSources = 2; -+ public int lavaSpeedNether = 10; -+ public int lavaSpeedNotNether = 30; - private void lavaSettings() { - lavaInfiniteRequiredSources = getInt("blocks.lava.infinite-required-sources", lavaInfiniteRequiredSources); -+ lavaSpeedNether = getInt("blocks.lava.speed.nether", lavaSpeedNether); -+ lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether); - } - - public boolean turtleEggsBreakFromExpOrbs = true; diff --git a/patches/server/0054-Add-player-death-exp-control-options.patch b/patches/server/0054-Add-player-death-exp-control-options.patch deleted file mode 100644 index 35fce6f93..000000000 --- a/patches/server/0054-Add-player-death-exp-control-options.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 26 Dec 2019 22:08:37 -0600 -Subject: [PATCH] Add player death exp control options - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 7b39cb5346925c14f3f144d622ca7e5855420aa6..fa2a0cc24bbe31abd49ce0f3f41bab2aa5d9c81f 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1998,9 +1998,19 @@ public abstract class Player extends LivingEntity { - @Override - public int getExperienceReward() { - if (!this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator()) { -- int i = this.experienceLevel * 7; -- -- return i > 100 ? 100 : i; -+ // Purpur start -+ int toDrop; -+ try { -+ toDrop = Math.round(((Number) scriptEngine.eval("let expLevel = " + experienceLevel + "; " + -+ "let expTotal = " + totalExperience + "; " + -+ "let exp = " + experienceProgress + "; " + -+ level().purpurConfig.playerDeathExpDropEquation)).floatValue()); -+ } catch (javax.script.ScriptException e) { -+ e.printStackTrace(); -+ toDrop = experienceLevel * 7; -+ } -+ return Math.min(toDrop, level().purpurConfig.playerDeathExpDropMax); -+ // Purpur end - } else { - return 0; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index bb5877d6feec92e87c13e33abfda48507201d08e..183a4e0192679d04555215c234dfefbc39dfe02e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -173,6 +173,8 @@ public class PurpurWorldConfig { - public boolean idleTimeoutTargetPlayer = true; - public int playerSpawnInvulnerableTicks = 60; - public boolean playerInvulnerableWhileAcceptingResourcePack = false; -+ public String playerDeathExpDropEquation = "expLevel * 7"; -+ public int playerDeathExpDropMax = 100; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -186,6 +188,8 @@ public class PurpurWorldConfig { - idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer); - playerSpawnInvulnerableTicks = getInt("gameplay-mechanics.player.spawn-invulnerable-ticks", playerSpawnInvulnerableTicks); - playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack); -+ playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation); -+ playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0055-Configurable-void-damage-height-and-damage.patch b/patches/server/0055-Configurable-void-damage-height-and-damage.patch deleted file mode 100644 index f5fffea3b..000000000 --- a/patches/server/0055-Configurable-void-damage-height-and-damage.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 27 Feb 2020 21:42:19 -0600 -Subject: [PATCH] Configurable void damage height and damage - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index acce1d46d6bce9b052126c76bc1f36264df39e71..5b9ddcab26a4c962dae104c44d65d623c036c8fd 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -915,7 +915,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - - public void checkBelowWorld() { - // Paper start - Configurable nether ceiling damage -- if (this.getY() < (double) (this.level.getMinBuildHeight() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER -+ if (this.getY() < (double) (this.level.getMinBuildHeight() + level().purpurConfig.voidDamageHeight) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER // Purpur - && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v) - && (!(this instanceof Player player) || !player.getAbilities().invulnerable))) { - // Paper end -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index b3699225e2dc77c6d201e361c8d447395c3f3e62..b8e6d0bd2ba18d7e0bf7cd78db8f98d4f1467d71 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2496,7 +2496,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - @Override - protected void onBelowWorld() { -- this.hurt(this.damageSources().fellOutOfWorld(), 4.0F); -+ this.hurt(this.damageSources().fellOutOfWorld(), (float) level().purpurConfig.voidDamageDealt); // Purpur - } - - protected void updateSwingTime() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 183a4e0192679d04555215c234dfefbc39dfe02e..a559ab65bc3f2d78d79e22b0e70afb6affd1d6a3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -98,10 +98,14 @@ public class PurpurWorldConfig { - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; -+ public double voidDamageHeight = -64.0D; -+ public double voidDamageDealt = 4.0D; - private void miscGameplayMechanicsSettings() { - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); -+ voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); -+ voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0056-Add-canSaveToDisk-to-Entity.patch b/patches/server/0056-Add-canSaveToDisk-to-Entity.patch deleted file mode 100644 index 7e26b9be2..000000000 --- a/patches/server/0056-Add-canSaveToDisk-to-Entity.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 18 Feb 2020 20:07:08 -0600 -Subject: [PATCH] Add canSaveToDisk to Entity - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 5b9ddcab26a4c962dae104c44d65d623c036c8fd..01a1534ec503e9aaea7befdba86f452128f8863d 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -493,6 +493,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return false; - } - -+ public boolean canSaveToDisk() { -+ return true; -+ } -+ - public final boolean hardCollides() { - return this.hardCollides; - } -diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -index 937f57d8af629c4e913d7ccabf6adab15f91d3d0..f40ab9d5b18c189ba9b572e49243640bd44362d1 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -@@ -115,6 +115,11 @@ public class WitherSkull extends AbstractHurtingProjectile { - return target != this.getRider() && super.canHitEntity(target); - } - -+ @Override -+ public boolean canSaveToDisk() { -+ return false; -+ } -+ - @Override - public boolean isPickable() { - return false; -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java -index 060e064625969610539dbf969ce773b877a7c579..32cd9df202704cdfb8fa06aaf0e738d483054feb 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java -@@ -112,6 +112,7 @@ public class EntityStorage implements EntityPersistentStorage { - ListTag listTag = new ListTag(); - final java.util.Map, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - entities.forEach((entity) -> { // diff here: use entities parameter -+ if (!entity.canSaveToDisk()) return; // Purpur - // Paper start - final EntityType entityType = entity.getType(); - final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); -diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -index af057c1d7fd74f3dd806c5ce7f8b0ad06cab7b8e..ce614ae6b1fa0b31c1ee8dacb69134bb20c949f4 100644 ---- a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -@@ -35,6 +35,13 @@ public class DolphinSpit extends LlamaSpit { - dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F)); - } - -+ // Purpur start -+ @Override -+ public boolean canSaveToDisk() { -+ return false; -+ } -+ // Purpur end -+ - public void tick() { - super_tick(); - -diff --git a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -index 1fd6412e332ea8ee82b19c108e113624e51d764b..ea8b928b6d82689e71bbcc39ab497491072dfba6 100644 ---- a/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -@@ -38,6 +38,13 @@ public class PhantomFlames extends LlamaSpit { - phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * 0.017453292F)); - } - -+ // Purpur start -+ @Override -+ public boolean canSaveToDisk() { -+ return false; -+ } -+ // Purpur end -+ - public void tick() { - super_tick(); - diff --git a/patches/server/0057-Dispenser-curse-of-binding-protection.patch b/patches/server/0057-Dispenser-curse-of-binding-protection.patch deleted file mode 100644 index 725ee2074..000000000 --- a/patches/server/0057-Dispenser-curse-of-binding-protection.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 25 Aug 2019 00:09:52 -0500 -Subject: [PATCH] Dispenser curse of binding protection - - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 5241abca7ef49c0891c28e1be6fafb791b8ea347..ef8d0f594c727f355a8209f36da924ae45017ce5 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -65,6 +65,7 @@ import net.minecraft.world.item.ProjectileWeaponItem; - import net.minecraft.world.item.SpawnEggItem; - import net.minecraft.world.item.SwordItem; - import net.minecraft.world.item.enchantment.EnchantmentHelper; -+import net.minecraft.world.item.enchantment.Enchantments; - import net.minecraft.world.level.GameRules; - import net.minecraft.world.level.ItemLike; - import net.minecraft.world.level.Level; -@@ -1166,6 +1167,12 @@ public abstract class Mob extends LivingEntity implements Targeting { - - } - -+ // Purpur start -+ public static @Nullable EquipmentSlot getSlotForDispenser(ItemStack itemstack) { -+ return EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BINDING_CURSE, itemstack) > 0 ? null : getEquipmentSlotForItem(itemstack); -+ } -+ // Purpur end -+ - @Nullable - public static Item getEquipmentForSlot(EquipmentSlot equipmentSlot, int equipmentLevel) { - switch (equipmentSlot) { -diff --git a/src/main/java/net/minecraft/world/item/ArmorItem.java b/src/main/java/net/minecraft/world/item/ArmorItem.java -index d7a0cbde8f8c99276307502674c71463fbe7e89c..3500c56cb85d8c76b2acd77976d374eaf487b3b3 100644 ---- a/src/main/java/net/minecraft/world/item/ArmorItem.java -+++ b/src/main/java/net/minecraft/world/item/ArmorItem.java -@@ -60,7 +60,7 @@ public class ArmorItem extends Item implements Equipable { - return false; - } else { - LivingEntity entityliving = (LivingEntity) list.get(0); -- EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(armor); -+ EquipmentSlot enumitemslot = pointer.getLevel().purpurConfig.dispenserApplyCursedArmor ? Mob.getEquipmentSlotForItem(armor) : Mob.getSlotForDispenser(armor); if (enumitemslot == null) return false; // Purpur - ItemStack itemstack1 = armor.copyWithCount(1); // Paper - shrink below and single item in event - // CraftBukkit start - Level world = pointer.getLevel(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a559ab65bc3f2d78d79e22b0e70afb6affd1d6a3..a42f39fc79c6ab632ae9df59e76a719665fa4d88 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -228,6 +228,11 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean dispenserApplyCursedArmor = true; -+ private void dispenserSettings() { -+ dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor); -+ } -+ - public boolean farmlandGetsMoistFromBelow = false; - private void farmlandSettings() { - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); diff --git a/patches/server/0058-Add-option-for-boats-to-eject-players-on-land.patch b/patches/server/0058-Add-option-for-boats-to-eject-players-on-land.patch deleted file mode 100644 index f2b897a74..000000000 --- a/patches/server/0058-Add-option-for-boats-to-eject-players-on-land.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 7 Sep 2019 22:47:59 -0500 -Subject: [PATCH] Add option for boats to eject players on land - - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java -index 35aeba4e8430e6419caa9db4a0b931a994228618..cb8443c9bc902741dfe6746baca9129214ab503b 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java -@@ -541,6 +541,7 @@ public class Boat extends Entity implements VariantHolder { - - if (f > 0.0F) { - this.landFriction = f; -+ if (level().purpurConfig.boatEjectPlayersOnLand) ejectPassengers(); // Purpur - return Boat.Status.ON_LAND; - } else { - return Boat.Status.IN_AIR; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a42f39fc79c6ab632ae9df59e76a719665fa4d88..d935bc8e47b31ff8ec66cd3c6a5b19450b4684d5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -95,12 +95,14 @@ public class PurpurWorldConfig { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - } - -+ public boolean boatEjectPlayersOnLand = false; - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; - private void miscGameplayMechanicsSettings() { -+ boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); diff --git a/patches/server/0059-Mending-mends-most-damages-equipment-first.patch b/patches/server/0059-Mending-mends-most-damages-equipment-first.patch deleted file mode 100644 index 8c2079e4b..000000000 --- a/patches/server/0059-Mending-mends-most-damages-equipment-first.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 14 Jul 2019 19:52:47 -0500 -Subject: [PATCH] Mending mends most damages equipment first - - -diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -index eca634792d2a7cc649675e3394e84dbaf1453905..2bd576849403bc2cfae298c2210616192ddc38db 100644 ---- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -@@ -331,7 +331,7 @@ public class ExperienceOrb extends Entity { - } - - private int repairPlayerItems(Player player, int amount) { -- Entry entry = EnchantmentHelper.getRandomItemWith(Enchantments.MENDING, player, ItemStack::isDamaged); -+ Entry entry = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedEquipment(Enchantments.MENDING, player) : EnchantmentHelper.getRandomItemWith(Enchantments.MENDING, player, ItemStack::isDamaged); // Purpur - - if (entry != null) { - ItemStack itemstack = (ItemStack) entry.getValue(); -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 879cc823d56625867eb73bb621db6a13f40ad81c..d8c1c16d3c4b35860e860cf6aef774916822ec9c 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -583,6 +583,16 @@ public final class ItemStack { - return this.isDamageableItem() && this.getDamageValue() > 0; - } - -+ // Purpur start -+ public float getDamagePercent() { -+ if (isDamaged()) { -+ return (float) getDamageValue() / (float) getItem().getMaxDamage(); -+ } else { -+ return 0F; -+ } -+ } -+ // Purpur end -+ - public int getDamageValue() { - return this.tag == null ? 0 : this.tag.getInt("Damage"); - } -diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -index ecf640b00007a386290f8dfe9935a8aa610079fd..2048899f8e4c8211e8dde0d11148d647678009fa 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -@@ -278,6 +278,29 @@ public class EnchantmentHelper { - return getItemEnchantmentLevel(Enchantments.CHANNELING, stack) > 0; - } - -+ // Purpur start -+ @Nullable -+ public static Map.Entry getMostDamagedEquipment(Enchantment enchantment, LivingEntity entity) { -+ Map map = enchantment.getSlotItems(entity); -+ if (map.isEmpty()) { -+ return null; -+ } -+ Map.Entry item = null; -+ float maxPercent = 0F; -+ for (Map.Entry entry : map.entrySet()) { -+ ItemStack itemstack = entry.getValue(); -+ if (!itemstack.isEmpty() && itemstack.isDamaged() && getItemEnchantmentLevel(enchantment, itemstack) > 0) { -+ float percent = itemstack.getDamagePercent(); -+ if (item == null || percent > maxPercent) { -+ item = entry; -+ maxPercent = percent; -+ } -+ } -+ } -+ return item; -+ } -+ // Purpur end -+ - @Nullable - public static Map.Entry getRandomItemWith(Enchantment enchantment, LivingEntity entity) { - return getRandomItemWith(enchantment, entity, (stack) -> { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d935bc8e47b31ff8ec66cd3c6a5b19450b4684d5..9e4428db0ffa0f1b0e0ae11af903d3449c782ee4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -95,6 +95,7 @@ public class PurpurWorldConfig { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - } - -+ public boolean useBetterMending = false; - public boolean boatEjectPlayersOnLand = false; - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; -@@ -102,6 +103,7 @@ public class PurpurWorldConfig { - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; - private void miscGameplayMechanicsSettings() { -+ useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); diff --git a/patches/server/0060-Add-5-second-tps-average-in-tps.patch b/patches/server/0060-Add-5-second-tps-average-in-tps.patch deleted file mode 100644 index f68de9257..000000000 --- a/patches/server/0060-Add-5-second-tps-average-in-tps.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 28 Jul 2019 01:27:37 -0500 -Subject: [PATCH] Add 5 second tps average in /tps - - -diff --git a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java -index fa56cd09102a89692b42f1d14257990508c5c720..f9251183df72ddc56662fd3f02acf21641a2200c 100644 ---- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java -+++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java -@@ -58,7 +58,7 @@ public class RAMDetails extends JList { - GraphData data = RAMGraph.DATA.peekLast(); - Vector vector = new Vector<>(); - -- double[] tps = new double[] {server.tps1.getAverage(), server.tps5.getAverage(), server.tps15.getAverage()}; -+ double[] tps = new double[] {server.tps5s.getAverage(), server.tps1.getAverage(), server.tps5.getAverage(), server.tps15.getAverage()}; // Purpur - String[] tpsAvg = new String[tps.length]; - - for ( int g = 0; g < tps.length; g++) { -@@ -67,7 +67,7 @@ public class RAMDetails extends JList { - vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)"); - vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb"); - vector.add("Avg tick: " + DECIMAL_FORMAT.format(getAverage(server.tickTimes)) + " ms"); -- vector.add("TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg)); -+ vector.add("TPS from last 5s, 1m, 5m, 15m: " + String.join(", ", tpsAvg)); // Purpur - - setListData(vector); - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 680502b60ac73cd59ec6def54e8a9edd611e31d5..477248c492c326c5214d2ceb669366d7992ee52f 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -303,7 +303,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) { diff --git a/patches/server/0061-Implement-elytra-settings.patch b/patches/server/0061-Implement-elytra-settings.patch deleted file mode 100644 index 40ef49338..000000000 --- a/patches/server/0061-Implement-elytra-settings.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 25 Jul 2019 18:07:37 -0500 -Subject: [PATCH] Implement elytra settings - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index b8e6d0bd2ba18d7e0bf7cd78db8f98d4f1467d71..35e3cac34a6a508fbf3971cff9ac07db48ddeaae 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3519,7 +3519,16 @@ public abstract class LivingEntity extends Entity implements Attackable { - int j = i / 10; - - if (j % 2 == 0) { -- itemstack.hurtAndBreak(1, this, (entityliving) -> { -+ // Purpur start -+ int damage = level().purpurConfig.elytraDamagePerSecond; -+ if (level().purpurConfig.elytraDamageMultiplyBySpeed > 0) { -+ double speed = getDeltaMovement().lengthSqr(); -+ if (speed > level().purpurConfig.elytraDamageMultiplyBySpeed) { -+ damage *= (int) speed; -+ } -+ } -+ itemstack.hurtAndBreak(damage, this, (entityliving) -> { -+ // Purpur end - entityliving.broadcastBreakEvent(EquipmentSlot.CHEST); - }); - } -diff --git a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -index 82b0bda3e35ec2157a477e1a17b2b46baadc97d9..0fc45b1048a1c4e0dc2bd1ae0437eecbe113cf96 100644 ---- a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -+++ b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -@@ -15,6 +15,7 @@ import net.minecraft.util.ByIdMap; - import net.minecraft.world.InteractionHand; - import net.minecraft.world.InteractionResult; - import net.minecraft.world.InteractionResultHolder; -+import net.minecraft.world.entity.EquipmentSlot; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.entity.projectile.FireworkRocketEntity; - import net.minecraft.world.item.context.UseOnContext; -@@ -69,6 +70,14 @@ public class FireworkRocketItem extends Item { - com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) fireworkRocketEntity.getBukkitEntity()); - if (event.callEvent() && world.addFreshEntity(fireworkRocketEntity)) { - user.awardStat(Stats.ITEM_USED.get(this)); -+ // Purpur start -+ if (world.purpurConfig.elytraDamagePerFireworkBoost > 0) { -+ ItemStack chestItem = user.getItemBySlot(EquipmentSlot.CHEST); -+ if (chestItem.getItem() == Items.ELYTRA) { -+ chestItem.hurtAndBreak(world.purpurConfig.elytraDamagePerFireworkBoost, user, (entityliving) -> entityliving.broadcastBreakEvent(EquipmentSlot.CHEST)); -+ } -+ } -+ // Purpur end - if (event.shouldConsume() && !user.getAbilities().instabuild) { - itemStack.shrink(1); - } else ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory(); -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index d8c1c16d3c4b35860e860cf6aef774916822ec9c..33548cd10758f3cc891c40d760b9e0a86dc8a2cb 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -612,7 +612,7 @@ public final class ItemStack { - int j; - - if (amount > 0) { -- j = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.UNBREAKING, this); -+ j = (getItem() == Items.ELYTRA && player != null && player.level().purpurConfig.elytraIgnoreUnbreaking) ? 0 : EnchantmentHelper.getItemEnchantmentLevel(Enchantments.UNBREAKING, this); - int k = 0; - - for (int l = 0; j > 0 && l < amount; ++l) { -@@ -667,6 +667,12 @@ public final class ItemStack { - if (this.hurt(amount, entity.getRandom(), entity /*instanceof ServerPlayer ? (ServerPlayer) entity : null*/)) { // Paper - pass LivingEntity for EntityItemDamageEvent - breakCallback.accept(entity); - Item item = this.getItem(); -+ // Purpur start -+ if (item == Items.ELYTRA) { -+ setDamageValue(item.getMaxDamage() - 1); -+ return; -+ } -+ // Purpur end - // CraftBukkit start - Check for item breaking - if (this.count == 1 && entity instanceof net.minecraft.world.entity.player.Player) { - org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((net.minecraft.world.entity.player.Player) entity, this); -diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java -index 8078f127ff4b6e0aafb5804b9c02e237f79445b5..06c2f30b77a2c8aecc65e0c305f643d53798f364 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -130,6 +130,14 @@ public class TridentItem extends Item implements Vanishable { - f2 *= f6 / f5; - f3 *= f6 / f5; - f4 *= f6 / f5; -+ -+ // Purpur start -+ ItemStack chestItem = entityhuman.getItemBySlot(EquipmentSlot.CHEST); -+ if (chestItem.getItem() == Items.ELYTRA && world.purpurConfig.elytraDamagePerTridentBoost > 0) { -+ chestItem.hurtAndBreak(world.purpurConfig.elytraDamagePerTridentBoost, entityhuman, (entity) -> entity.broadcastBreakEvent(EquipmentSlot.CHEST)); -+ } -+ // Purpur end -+ - entityhuman.push((double) f2, (double) f3, (double) f4); - entityhuman.startAutoSpinAttack(20); - if (entityhuman.onGround()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9e4428db0ffa0f1b0e0ae11af903d3449c782ee4..abecd27fab86cb5c16b3d42d5f499caa1976bd45 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -112,6 +112,19 @@ public class PurpurWorldConfig { - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); - } - -+ public int elytraDamagePerSecond = 1; -+ public double elytraDamageMultiplyBySpeed = 0; -+ public boolean elytraIgnoreUnbreaking = false; -+ public int elytraDamagePerFireworkBoost = 0; -+ public int elytraDamagePerTridentBoost = 0; -+ private void elytraSettings() { -+ elytraDamagePerSecond = getInt("gameplay-mechanics.elytra.damage-per-second", elytraDamagePerSecond); -+ elytraDamageMultiplyBySpeed = getDouble("gameplay-mechanics.elytra.damage-multiplied-by-speed", elytraDamageMultiplyBySpeed); -+ elytraIgnoreUnbreaking = getBoolean("gameplay-mechanics.elytra.ignore-unbreaking", elytraIgnoreUnbreaking); -+ elytraDamagePerFireworkBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.firework", elytraDamagePerFireworkBoost); -+ elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost); -+ } -+ - public double minecartMaxSpeed = 0.4D; - public boolean minecartPlaceAnywhere = false; - public boolean minecartControllable = false; diff --git a/patches/server/0062-Item-entity-immunities.patch b/patches/server/0062-Item-entity-immunities.patch deleted file mode 100644 index e17d61bde..000000000 --- a/patches/server/0062-Item-entity-immunities.patch +++ /dev/null @@ -1,170 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 22 Feb 2020 15:54:08 -0600 -Subject: [PATCH] Item entity immunities - - -diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 8ab959dd588b5154b63e133b2e937fa2d0ab8e52..fb11b020d52988360562db23d8568e5ef37e21c8 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -70,7 +70,7 @@ public class ServerEntity { - @Nullable - private List> trackedDataValues; - // CraftBukkit start -- final Set trackedPlayers; // Paper - private -> package -+ public final Set trackedPlayers; // Paper - private -> package // Purpur - package -> public - - public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer> consumer, Set trackedPlayers) { - this.trackedPlayers = trackedPlayers; -diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -index 80fc7918cb294b0d88a293bd6a920441cb55c3ad..d045877bfb0f364a43c8448cad21cc39d0565964 100644 ---- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -56,6 +56,12 @@ public class ItemEntity extends Entity implements TraceableEntity { - public boolean canMobPickup = true; // Paper - private int despawnRate = -1; // Paper - public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper -+ // Purpur start -+ public boolean immuneToCactus = false; -+ public boolean immuneToExplosion = false; -+ public boolean immuneToFire = false; -+ public boolean immuneToLightning = false; -+ // Purpur end - - public ItemEntity(EntityType type, Level world) { - super(type, world); -@@ -358,6 +364,15 @@ public class ItemEntity extends Entity implements TraceableEntity { - return false; - } else if (!this.getItem().getItem().canBeHurtBy(source)) { - return false; -+ // Purpur start -+ } else if ( -+ (immuneToCactus && source.is(net.minecraft.world.damagesource.DamageTypes.CACTUS)) || -+ (immuneToFire && (source.is(DamageTypeTags.IS_FIRE) && source.is(net.minecraft.world.damagesource.DamageTypes.IN_FIRE)) || -+ (immuneToLightning && source.is(net.minecraft.world.damagesource.DamageTypes.LIGHTNING_BOLT)) || -+ (immuneToExplosion && source.is(DamageTypeTags.IS_EXPLOSION))) -+ ) { -+ return false; -+ // Purpur end - } else if (this.level().isClientSide) { - return true; - } else { -@@ -555,6 +570,12 @@ public class ItemEntity extends Entity implements TraceableEntity { - // com.google.common.base.Preconditions.checkArgument(!stack.isEmpty(), "Cannot drop air"); // CraftBukkit // Paper - Remove check - this.getEntityData().set(ItemEntity.DATA_ITEM, stack); - this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper -+ // Purpur start -+ if (level().purpurConfig.itemImmuneToCactus.contains(stack.getItem())) immuneToCactus = true; -+ if (level().purpurConfig.itemImmuneToExplosion.contains(stack.getItem())) immuneToExplosion = true; -+ if (level().purpurConfig.itemImmuneToFire.contains(stack.getItem())) immuneToFire = true; -+ if (level().purpurConfig.itemImmuneToLightning.contains(stack.getItem())) immuneToLightning = true; -+ // level end - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -index 5e83fabb20bc2b0668cbf48530053ca1bb9092f3..8fcee0e426cd598ddfd7e12df4382d57d2016780 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -@@ -154,4 +154,46 @@ public class CraftItem extends CraftEntity implements Item { - public String toString() { - return "CraftItem"; - } -+ -+ // Purpur start -+ @Override -+ public void setImmuneToCactus(boolean immuneToCactus) { -+ item.immuneToCactus = immuneToCactus; -+ } -+ -+ @Override -+ public boolean isImmuneToCactus() { -+ return item.immuneToCactus; -+ } -+ -+ @Override -+ public void setImmuneToExplosion(boolean immuneToExplosion) { -+ item.immuneToExplosion = immuneToExplosion; -+ } -+ -+ @Override -+ public boolean isImmuneToExplosion() { -+ return item.immuneToExplosion; -+ } -+ -+ @Override -+ public void setImmuneToFire(boolean immuneToFire) { -+ item.immuneToFire = immuneToFire; -+ } -+ -+ @Override -+ public boolean isImmuneToFire() { -+ return item.immuneToFire; -+ } -+ -+ @Override -+ public void setImmuneToLightning(boolean immuneToLightning) { -+ item.immuneToLightning = immuneToLightning; -+ } -+ -+ @Override -+ public boolean isImmuneToLightning() { -+ return item.immuneToLightning; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index abecd27fab86cb5c16b3d42d5f499caa1976bd45..9639202024f79c8ed57207445380ef017cfeaea6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -125,6 +125,49 @@ public class PurpurWorldConfig { - elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost); - } - -+ public List itemImmuneToCactus = new ArrayList<>(); -+ public List itemImmuneToExplosion = new ArrayList<>(); -+ public List itemImmuneToFire = new ArrayList<>(); -+ public List itemImmuneToLightning = new ArrayList<>(); -+ private void itemSettings() { -+ itemImmuneToCactus.clear(); -+ getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -+ if (key.toString().equals("*")) { -+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToCactus.add(item)); -+ return; -+ } -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); -+ if (item != Items.AIR) itemImmuneToCactus.add(item); -+ }); -+ itemImmuneToExplosion.clear(); -+ getList("gameplay-mechanics.item.immune.explosion", new ArrayList<>()).forEach(key -> { -+ if (key.toString().equals("*")) { -+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToExplosion.add(item)); -+ return; -+ } -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); -+ if (item != Items.AIR) itemImmuneToExplosion.add(item); -+ }); -+ itemImmuneToFire.clear(); -+ getList("gameplay-mechanics.item.immune.fire", new ArrayList<>()).forEach(key -> { -+ if (key.toString().equals("*")) { -+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToFire.add(item)); -+ return; -+ } -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); -+ if (item != Items.AIR) itemImmuneToFire.add(item); -+ }); -+ itemImmuneToLightning.clear(); -+ getList("gameplay-mechanics.item.immune.lightning", new ArrayList<>()).forEach(key -> { -+ if (key.toString().equals("*")) { -+ BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToLightning.add(item)); -+ return; -+ } -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); -+ if (item != Items.AIR) itemImmuneToLightning.add(item); -+ }); -+ } -+ - public double minecartMaxSpeed = 0.4D; - public boolean minecartPlaceAnywhere = false; - public boolean minecartControllable = false; diff --git a/patches/server/0063-Add-ping-command.patch b/patches/server/0063-Add-ping-command.patch deleted file mode 100644 index 80e22aa9f..000000000 --- a/patches/server/0063-Add-ping-command.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 13 Mar 2020 22:29:10 -0500 -Subject: [PATCH] Add ping command - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 147fcbdd784f06fe23138b20aef82f92ba154998..aadc9fc53b0f495664d90c40f30f9a128c3b4c95 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -224,6 +224,7 @@ public class Commands { - SetPlayerIdleTimeoutCommand.register(this.dispatcher); - StopCommand.register(this.dispatcher); - WhitelistCommand.register(this.dispatcher); -+ org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - } - - if (environment.includeIntegrated) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 4905358e2a1939bfba963bda3fcf914721dfac45..a74932163207506e779c941c819eb663b98bd18c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -179,6 +179,7 @@ public class PurpurConfig { - public static boolean afkBroadcastUseDisplayName = false; - public static String afkTabListPrefix = "[AFK] "; - public static String afkTabListSuffix = ""; -+ public static String pingCommandOutput = "%s's ping is %sms"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -@@ -186,6 +187,7 @@ public class PurpurConfig { - afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName); - afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix))); - afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); -+ pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - } - - public static String serverModName = "Purpur"; -diff --git a/src/main/java/org/purpurmc/purpur/command/PingCommand.java b/src/main/java/org/purpurmc/purpur/command/PingCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..179727c6b3171c040d1aaf069525f61a9a2d54d9 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/PingCommand.java -@@ -0,0 +1,33 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.commands.arguments.EntityArgument; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.PurpurConfig; -+import org.bukkit.craftbukkit.util.CraftChatMessage; -+ -+import java.util.Collection; -+import java.util.Collections; -+ -+public class PingCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("ping") -+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.ping")) -+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) -+ .then(Commands.argument("targets", EntityArgument.players()) -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ping.other")) -+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) -+ ) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender, Collection targets) { -+ for (ServerPlayer player : targets) { -+ String output = String.format(PurpurConfig.pingCommandOutput, player.getGameProfile().getName(), player.latency); -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} diff --git a/patches/server/0064-Add-demo-command.patch b/patches/server/0064-Add-demo-command.patch deleted file mode 100644 index 3941c6443..000000000 --- a/patches/server/0064-Add-demo-command.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 30 Nov 2020 03:12:04 -0600 -Subject: [PATCH] Add demo command - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index aadc9fc53b0f495664d90c40f30f9a128c3b4c95..54f9dd8d666b2b45190681f4362fce457e8e8ef2 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -224,6 +224,7 @@ public class Commands { - SetPlayerIdleTimeoutCommand.register(this.dispatcher); - StopCommand.register(this.dispatcher); - WhitelistCommand.register(this.dispatcher); -+ org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index a74932163207506e779c941c819eb663b98bd18c..235db276a07017bf89e6311a0b84291e8a0af06b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -179,6 +179,7 @@ public class PurpurConfig { - public static boolean afkBroadcastUseDisplayName = false; - public static String afkTabListPrefix = "[AFK] "; - public static String afkTabListSuffix = ""; -+ public static String demoCommandOutput = "%s has been shown the demo screen"; - public static String pingCommandOutput = "%s's ping is %sms"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); -@@ -187,6 +188,7 @@ public class PurpurConfig { - afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName); - afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix))); - afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); -+ demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - } - -diff --git a/src/main/java/org/purpurmc/purpur/command/DemoCommand.java b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..235f3cd89f675b70a6152a00534608c0902f19fd ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/DemoCommand.java -@@ -0,0 +1,35 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.commands.arguments.EntityArgument; -+import net.minecraft.network.protocol.game.ClientboundGameEventPacket; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.PurpurConfig; -+ -+import java.util.Collection; -+import java.util.Collections; -+ -+public class DemoCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("demo") -+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.demo")) -+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) -+ .then(Commands.argument("targets", EntityArgument.players()) -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.demo.other")) -+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) -+ ) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender, Collection targets) { -+ for (ServerPlayer player : targets) { -+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.DEMO_EVENT, 0); -+ player.connection.send(packet); -+ String output = String.format(PurpurConfig.demoCommandOutput, player.getGameProfile().getName()); -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} diff --git a/patches/server/0065-Add-credits-command.patch b/patches/server/0065-Add-credits-command.patch deleted file mode 100644 index edc67a907..000000000 --- a/patches/server/0065-Add-credits-command.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Fri, 30 Apr 2021 14:03:06 -0400 -Subject: [PATCH] Add credits command - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 54f9dd8d666b2b45190681f4362fce457e8e8ef2..e5b1b6ad32c48a4ba13b4930954fad18669677ad 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -224,6 +224,7 @@ public class Commands { - SetPlayerIdleTimeoutCommand.register(this.dispatcher); - StopCommand.register(this.dispatcher); - WhitelistCommand.register(this.dispatcher); -+ org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 235db276a07017bf89e6311a0b84291e8a0af06b..8e99128759e555cdef0efe9cea1b3b36f2c3fda9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -179,6 +179,7 @@ public class PurpurConfig { - public static boolean afkBroadcastUseDisplayName = false; - public static String afkTabListPrefix = "[AFK] "; - public static String afkTabListSuffix = ""; -+ public static String creditsCommandOutput = "%s has been shown the end credits"; - public static String demoCommandOutput = "%s has been shown the demo screen"; - public static String pingCommandOutput = "%s's ping is %sms"; - private static void messages() { -@@ -188,6 +189,7 @@ public class PurpurConfig { - afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName); - afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix))); - afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); -+ creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput); - demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - } -diff --git a/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..40d2fab4a9728ac90c36e30c130f3116b7025d11 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java -@@ -0,0 +1,35 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.commands.arguments.EntityArgument; -+import net.minecraft.network.protocol.game.ClientboundGameEventPacket; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.PurpurConfig; -+ -+import java.util.Collection; -+import java.util.Collections; -+ -+public class CreditsCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("credits") -+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.credits")) -+ .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) -+ .then(Commands.argument("targets", EntityArgument.players()) -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.credits.other")) -+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) -+ ) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender, Collection targets) { -+ for (ServerPlayer player : targets) { -+ ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, 1F); -+ player.connection.send(packet); -+ String output = String.format(PurpurConfig.creditsCommandOutput, player.getGameProfile().getName()); -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} diff --git a/patches/server/0066-Configurable-jockey-options.patch b/patches/server/0066-Configurable-jockey-options.patch deleted file mode 100644 index 3fbf17f0d..000000000 --- a/patches/server/0066-Configurable-jockey-options.patch +++ /dev/null @@ -1,271 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 26 Mar 2020 21:39:32 -0500 -Subject: [PATCH] Configurable jockey options - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 3cde608a222ab2ef2b5fc7f543e97ba71e440ea9..6ec7d0f5e525f9e04b6fa0fb6d4da5fcc22fe653 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -95,6 +95,21 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.drownedSpawnReinforcements); - } - -+ @Override -+ public boolean jockeyOnlyBaby() { -+ return level().purpurConfig.drownedJockeyOnlyBaby; -+ } -+ -+ @Override -+ public double jockeyChance() { -+ return level().purpurConfig.drownedJockeyChance; -+ } -+ -+ @Override -+ public boolean jockeyTryExistingChickens() { -+ return level().purpurConfig.drownedJockeyTryExistingChickens; -+ } -+ - @Override - protected void addBehaviourGoals() { - this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0D)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java -index 5f801b2a7d8a69a38b8e10471a29f813c8d828ad..7e46ebc5c99f018cada50f491200e4fe7b7098d7 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -49,6 +49,21 @@ public class Husk extends Zombie { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.huskSpawnReinforcements); - } - -+ @Override -+ public boolean jockeyOnlyBaby() { -+ return level().purpurConfig.huskJockeyOnlyBaby; -+ } -+ -+ @Override -+ public double jockeyChance() { -+ return level().purpurConfig.huskJockeyChance; -+ } -+ -+ @Override -+ public boolean jockeyTryExistingChickens() { -+ return level().purpurConfig.huskJockeyTryExistingChickens; -+ } -+ - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index b3a31549179760388bc2958b4339848d1f0b293d..b2b04d7b7bbc400fb66cac385fa754d4fb47c7d9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -128,6 +128,18 @@ public class Zombie extends Monster { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieMaxHealth); - } - -+ public boolean jockeyOnlyBaby() { -+ return level().purpurConfig.zombieJockeyOnlyBaby; -+ } -+ -+ public double jockeyChance() { -+ return level().purpurConfig.zombieJockeyChance; -+ } -+ -+ public boolean jockeyTryExistingChickens() { -+ return level().purpurConfig.zombieJockeyTryExistingChickens; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -@@ -551,19 +563,20 @@ public class Zombie extends Monster { - if (object instanceof Zombie.ZombieGroupData) { - Zombie.ZombieGroupData entityzombie_groupdatazombie = (Zombie.ZombieGroupData) object; - -- if (entityzombie_groupdatazombie.isBaby) { -- this.setBaby(true); -+ // Purpur start -+ if (!jockeyOnlyBaby() || entityzombie_groupdatazombie.isBaby) { -+ this.setBaby(entityzombie_groupdatazombie.isBaby); - if (entityzombie_groupdatazombie.canSpawnJockey) { -- if ((double) randomsource.nextFloat() < 0.05D) { -- List list = world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN); -+ if ((double) randomsource.nextFloat() < jockeyChance()) { -+ List list = jockeyTryExistingChickens() ? world.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN) : java.util.Collections.emptyList(); -+ // Purpur end - - if (!list.isEmpty()) { - Chicken entitychicken = (Chicken) list.get(0); - - entitychicken.setChickenJockey(true); - this.startRiding(entitychicken); -- } -- } else if ((double) randomsource.nextFloat() < 0.05D) { -+ } else { // Purpur - Chicken entitychicken1 = (Chicken) EntityType.CHICKEN.create(this.level()); - - if (entitychicken1 != null) { -@@ -573,6 +586,7 @@ public class Zombie extends Monster { - this.startRiding(entitychicken1); - world.addFreshEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit - } -+ } // Purpur - } - } - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index c89680f83275169728f923e70a17cb88d4480f01..6f3667751e430ff6020a9a26f82a25e4ec043ce6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -106,6 +106,21 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieVillagerSpawnReinforcements); - } - -+ @Override -+ public boolean jockeyOnlyBaby() { -+ return level().purpurConfig.zombieVillagerJockeyOnlyBaby; -+ } -+ -+ @Override -+ public double jockeyChance() { -+ return level().purpurConfig.zombieVillagerJockeyChance; -+ } -+ -+ @Override -+ public boolean jockeyTryExistingChickens() { -+ return level().purpurConfig.zombieVillagerJockeyTryExistingChickens; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 8f22bfcde4bb8ad73794f2b98b156113e5a2a6c9..3d61b27c23d90e87fdfc8c170eafc116716ac047 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -85,6 +85,21 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth); - } - -+ @Override -+ public boolean jockeyOnlyBaby() { -+ return level().purpurConfig.zombifiedPiglinJockeyOnlyBaby; -+ } -+ -+ @Override -+ public double jockeyChance() { -+ return level().purpurConfig.zombifiedPiglinJockeyChance; -+ } -+ -+ @Override -+ public boolean jockeyTryExistingChickens() { -+ return level().purpurConfig.zombifiedPiglinJockeyTryExistingChickens; -+ } -+ - @Override - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9639202024f79c8ed57207445380ef017cfeaea6..c74027c71eecf76512d5737b496d98a6cffb5eab 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -592,6 +592,9 @@ public class PurpurWorldConfig { - public boolean drownedControllable = true; - public double drownedMaxHealth = 20.0D; - public double drownedSpawnReinforcements = 0.1D; -+ public boolean drownedJockeyOnlyBaby = true; -+ public double drownedJockeyChance = 0.05D; -+ public boolean drownedJockeyTryExistingChickens = true; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -@@ -603,6 +606,9 @@ public class PurpurWorldConfig { - } - drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth); - drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements); -+ drownedJockeyOnlyBaby = getBoolean("mobs.drowned.jockey.only-babies", drownedJockeyOnlyBaby); -+ drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance); -+ drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens); - } - - public boolean elderGuardianRidable = false; -@@ -851,6 +857,9 @@ public class PurpurWorldConfig { - public boolean huskControllable = true; - public double huskMaxHealth = 20.0D; - public double huskSpawnReinforcements = 0.1D; -+ public boolean huskJockeyOnlyBaby = true; -+ public double huskJockeyChance = 0.05D; -+ public boolean huskJockeyTryExistingChickens = true; - private void huskSettings() { - huskRidable = getBoolean("mobs.husk.ridable", huskRidable); - huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); -@@ -862,6 +871,9 @@ public class PurpurWorldConfig { - } - huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth); - huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements); -+ huskJockeyOnlyBaby = getBoolean("mobs.husk.jockey.only-babies", huskJockeyOnlyBaby); -+ huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance); -+ huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens); - } - - public boolean illusionerRidable = false; -@@ -1678,6 +1690,9 @@ public class PurpurWorldConfig { - public boolean zombieControllable = true; - public double zombieMaxHealth = 20.0D; - public double zombieSpawnReinforcements = 0.1D; -+ public boolean zombieJockeyOnlyBaby = true; -+ public double zombieJockeyChance = 0.05D; -+ public boolean zombieJockeyTryExistingChickens = true; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -1689,6 +1704,9 @@ public class PurpurWorldConfig { - } - zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth); - zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements); -+ zombieJockeyOnlyBaby = getBoolean("mobs.zombie.jockey.only-babies", zombieJockeyOnlyBaby); -+ zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance); -+ zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens); - } - - public boolean zombieHorseRidableInWater = false; -@@ -1723,6 +1741,9 @@ public class PurpurWorldConfig { - public boolean zombieVillagerControllable = true; - public double zombieVillagerMaxHealth = 20.0D; - public double zombieVillagerSpawnReinforcements = 0.1D; -+ public boolean zombieVillagerJockeyOnlyBaby = true; -+ public double zombieVillagerJockeyChance = 0.05D; -+ public boolean zombieVillagerJockeyTryExistingChickens = true; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -1734,6 +1755,9 @@ public class PurpurWorldConfig { - } - zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth); - zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements); -+ zombieVillagerJockeyOnlyBaby = getBoolean("mobs.zombie_villager.jockey.only-babies", zombieVillagerJockeyOnlyBaby); -+ zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance); -+ zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens); - } - - public boolean zombifiedPiglinRidable = false; -@@ -1741,6 +1765,9 @@ public class PurpurWorldConfig { - public boolean zombifiedPiglinControllable = true; - public double zombifiedPiglinMaxHealth = 20.0D; - public double zombifiedPiglinSpawnReinforcements = 0.0D; -+ public boolean zombifiedPiglinJockeyOnlyBaby = true; -+ public double zombifiedPiglinJockeyChance = 0.05D; -+ public boolean zombifiedPiglinJockeyTryExistingChickens = true; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -@@ -1752,5 +1779,8 @@ public class PurpurWorldConfig { - } - zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth); - zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements); -+ zombifiedPiglinJockeyOnlyBaby = getBoolean("mobs.zombified_piglin.jockey.only-babies", zombifiedPiglinJockeyOnlyBaby); -+ zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance); -+ zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens); - } - } diff --git a/patches/server/0068-Add-phantom-spawning-options.patch b/patches/server/0068-Add-phantom-spawning-options.patch deleted file mode 100644 index c246c6ac0..000000000 --- a/patches/server/0068-Add-phantom-spawning-options.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 3 Jul 2020 00:03:52 -0500 -Subject: [PATCH] Add phantom spawning options - - -diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -index dfeb3e336e06ef01f5401a362755030db942bb07..f74c5eda91a3d521763ec7bc33f23e0c62458cc2 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -@@ -49,7 +49,7 @@ public class PhantomSpawner implements CustomSpawner { - int spawnAttemptMaxSeconds = world.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds; - this.nextTick += (spawnAttemptMinSeconds + randomsource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; - // Paper end -- if (world.getSkyDarken() < 5 && world.dimensionType().hasSkyLight()) { -+ if (world.getSkyDarken() < world.purpurConfig.phantomSpawnMinSkyDarkness && world.dimensionType().hasSkyLight()) { // Purpur - return 0; - } else { - int i = 0; -@@ -61,10 +61,10 @@ public class PhantomSpawner implements CustomSpawner { - if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper - BlockPos blockposition = entityplayer.blockPosition(); - -- if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) { -+ if (!world.dimensionType().hasSkyLight() || (!world.purpurConfig.phantomSpawnOnlyAboveSeaLevel || blockposition.getY() >= world.getSeaLevel()) && (!world.purpurConfig.phantomSpawnOnlyWithVisibleSky || world.canSeeSky(blockposition))) { // Purpur - DifficultyInstance difficultydamagescaler = world.getCurrentDifficultyAt(blockposition); - -- if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * 3.0F)) { -+ if (difficultydamagescaler.isHarderThan(randomsource.nextFloat() * (float) world.purpurConfig.phantomSpawnLocalDifficultyChance)) { // Purpur - ServerStatsCounter serverstatisticmanager = entityplayer.getStats(); - int j = Mth.clamp(serverstatisticmanager.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); - boolean flag2 = true; -@@ -76,7 +76,7 @@ public class PhantomSpawner implements CustomSpawner { - - if (NaturalSpawner.isValidEmptySpawnBlock(world, blockposition1, iblockdata, fluid, EntityType.PHANTOM)) { - SpawnGroupData groupdataentity = null; -- int k = 1 + randomsource.nextInt(difficultydamagescaler.getDifficulty().getId() + 1); -+ int k = world.purpurConfig.phantomSpawnMinPerAttempt + world.random.nextInt((world.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? difficultydamagescaler.getDifficulty().getId() : world.purpurConfig.phantomSpawnMaxPerAttempt - world.purpurConfig.phantomSpawnMinPerAttempt) + 1); // Purpur - - for (int l = 0; l < k; ++l) { - // Paper start -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 49af5d8feae2b267a12273a91ee637c7a6e7021e..fb1e819bf55623408a134bf334099c425609765d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1072,6 +1072,12 @@ public class PurpurWorldConfig { - public double phantomAttackedByCrystalRadius = 0.0D; - public float phantomAttackedByCrystalDamage = 1.0F; - public double phantomOrbitCrystalRadius = 0.0D; -+ public int phantomSpawnMinSkyDarkness = 5; -+ public boolean phantomSpawnOnlyAboveSeaLevel = true; -+ public boolean phantomSpawnOnlyWithVisibleSky = true; -+ public double phantomSpawnLocalDifficultyChance = 3.0D; -+ public int phantomSpawnMinPerAttempt = 1; -+ public int phantomSpawnMaxPerAttempt = -1; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -1096,6 +1102,12 @@ public class PurpurWorldConfig { - phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius); - phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage); - phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius); -+ phantomSpawnMinSkyDarkness = getInt("mobs.phantom.spawn.min-sky-darkness", phantomSpawnMinSkyDarkness); -+ phantomSpawnOnlyAboveSeaLevel = getBoolean("mobs.phantom.spawn.only-above-sea-level", phantomSpawnOnlyAboveSeaLevel); -+ phantomSpawnOnlyWithVisibleSky = getBoolean("mobs.phantom.spawn.only-with-visible-sky", phantomSpawnOnlyWithVisibleSky); -+ phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance); -+ phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt); -+ phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt); - } - - public boolean pigRidable = false; diff --git a/patches/server/0069-Implement-bed-explosion-options.patch b/patches/server/0069-Implement-bed-explosion-options.patch deleted file mode 100644 index d73c9a85f..000000000 --- a/patches/server/0069-Implement-bed-explosion-options.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 4 Jul 2020 13:12:43 -0500 -Subject: [PATCH] Implement bed explosion options - - -diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java -index d40500f9a807cab0b2fb6fa9032f33f4fb74c895..e8405a57fb88e63b63baaf00645c417633bdc0f2 100644 ---- a/src/main/java/net/minecraft/world/level/block/BedBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java -@@ -96,7 +96,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock - - Vec3 vec3d = pos.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); -+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur - return InteractionResult.SUCCESS; - } else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) { - if (!BedBlock.canSetSpawn(world)) return this.explodeBed(state, world, pos); // Paper - check explode first -@@ -149,7 +149,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock - - Vec3 vec3d = blockposition.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); -+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur - return InteractionResult.SUCCESS; - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fb1e819bf55623408a134bf334099c425609765d..ab4f4de26a6752b54f35b1aeec657c1d2be3ecdb 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -288,6 +288,27 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean bedExplode = true; -+ public double bedExplosionPower = 5.0D; -+ public boolean bedExplosionFire = true; -+ public net.minecraft.world.level.Level.ExplosionInteraction bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ private void bedSettings() { -+ if (PurpurConfig.version < 31) { -+ if ("DESTROY".equals(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()))) { -+ set("blocks.bed.explosion-effect", "BLOCK"); -+ } -+ } -+ bedExplode = getBoolean("blocks.bed.explode", bedExplode); -+ bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower); -+ bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire); -+ try { -+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.bed.explosion-effect", bedExplosionEffect.name())); -+ } catch (IllegalArgumentException e) { -+ log(Level.SEVERE, "Unknown value for `blocks.bed.explosion-effect`! Using default of `BLOCK`"); -+ bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ } -+ } -+ - public boolean dispenserApplyCursedArmor = true; - private void dispenserSettings() { - dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor); diff --git a/patches/server/0070-Implement-respawn-anchor-explosion-options.patch b/patches/server/0070-Implement-respawn-anchor-explosion-options.patch deleted file mode 100644 index 5f2c71233..000000000 --- a/patches/server/0070-Implement-respawn-anchor-explosion-options.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 4 Jul 2020 13:23:19 -0500 -Subject: [PATCH] Implement respawn anchor explosion options - - -diff --git a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java -index 2ed78cf83c0ae66a6ddba1ff307da89a24b0d0a8..ae17d6a54fad0bd2d71d306f418b5ced2f11b863 100644 ---- a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java -@@ -141,7 +141,7 @@ public class RespawnAnchorBlock extends Block { - }; - Vec3 vec3d = explodedPos.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), explosiondamagecalculator, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // Paper -+ if (world.purpurConfig.respawnAnchorExplode)world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, explodedBlockState), explosiondamagecalculator, vec3d, (float) world.purpurConfig.respawnAnchorExplosionPower, world.purpurConfig.respawnAnchorExplosionFire, world.purpurConfig.respawnAnchorExplosionEffect); // Paper // Purpur - } - - public static boolean canSetSpawn(Level world) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ab4f4de26a6752b54f35b1aeec657c1d2be3ecdb..dc7666906fb04bf8f1df54ac81e04d8ad76a893b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -328,6 +328,27 @@ public class PurpurWorldConfig { - lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether); - } - -+ public boolean respawnAnchorExplode = true; -+ public double respawnAnchorExplosionPower = 5.0D; -+ public boolean respawnAnchorExplosionFire = true; -+ public net.minecraft.world.level.Level.ExplosionInteraction respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ private void respawnAnchorSettings() { -+ if (PurpurConfig.version < 31) { -+ if ("DESTROY".equals(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()))) { -+ set("blocks.respawn_anchor.explosion-effect", "BLOCK"); -+ } -+ } -+ respawnAnchorExplode = getBoolean("blocks.respawn_anchor.explode", respawnAnchorExplode); -+ respawnAnchorExplosionPower = getDouble("blocks.respawn_anchor.explosion-power", respawnAnchorExplosionPower); -+ respawnAnchorExplosionFire = getBoolean("blocks.respawn_anchor.explosion-fire", respawnAnchorExplosionFire); -+ try { -+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name())); -+ } catch (IllegalArgumentException e) { -+ log(Level.SEVERE, "Unknown value for `blocks.respawn_anchor.explosion-effect`! Using default of `BLOCK`"); -+ respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ } -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; diff --git a/patches/server/0071-Add-allow-water-in-end-world-option.patch b/patches/server/0071-Add-allow-water-in-end-world-option.patch deleted file mode 100644 index 816d7f0fd..000000000 --- a/patches/server/0071-Add-allow-water-in-end-world-option.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 5 Jul 2020 23:40:16 -0500 -Subject: [PATCH] Add allow water in end world option - - -diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java -index 578c3db52dda4c169b5ea615a4ce4a79f15a4cad..0bd98b802f246a3f6061f716d470a4797b28d59d 100644 ---- a/src/main/java/net/minecraft/world/item/BucketItem.java -+++ b/src/main/java/net/minecraft/world/item/BucketItem.java -@@ -164,7 +164,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { - // CraftBukkit end - if (!flag1) { - return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit -- } else if (world.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) { -+ } else if ((world.dimensionType().ultraWarm() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur - int i = blockposition.getX(); - int j = blockposition.getY(); - int k = blockposition.getZ(); -@@ -172,7 +172,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { - world.playSound(entityhuman, blockposition, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); - - for (int l = 0; l < 8; ++l) { -- world.addParticle(ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); -+ ((ServerLevel) world).sendParticles(null, ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D, true); // Purpur - } - - return true; -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index dc71cf3c66a0a4390177428688e6f4ee39a981b6..a4c45ee8a5c6e55a6f9abde401cc06f13bd0b018 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1576,4 +1576,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - return null; - } - // Paper end -+ -+ // Purpur start -+ public boolean isNether() { -+ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER; -+ } -+ -+ public boolean isTheEnd() { -+ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java -index 5fbdc96f29e29dfc092b9e84a988032db0fa36ab..1352168db8f3062943cc05396cbd9b535508c461 100644 ---- a/src/main/java/net/minecraft/world/level/block/IceBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java -@@ -33,7 +33,7 @@ public class IceBlock extends HalfTransparentBlock { - public void afterDestroy(Level world, BlockPos pos, ItemStack tool) { - // Paper end - if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) == 0) { -- if (world.dimensionType().ultraWarm()) { -+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - world.removeBlock(pos, false); - return; - } -@@ -61,7 +61,7 @@ public class IceBlock extends HalfTransparentBlock { - return; - } - // CraftBukkit end -- if (world.dimensionType().ultraWarm()) { -+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - world.removeBlock(pos, false); - } else { - world.setBlockAndUpdate(pos, IceBlock.meltsInto()); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 8e99128759e555cdef0efe9cea1b3b36f2c3fda9..8575753af39d19e485f915e3e12e377c325b4751 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -244,6 +244,11 @@ public class PurpurConfig { - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - } - -+ public static boolean allowWaterPlacementInTheEnd = true; -+ private static void allowWaterPlacementInEnd() { -+ allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd); -+ } -+ - public static boolean loggerSuppressInitLegacyMaterialError = false; - public static boolean loggerSuppressIgnoredAdvancementWarnings = false; - public static boolean loggerSuppressUnrecognizedRecipeErrors = false; diff --git a/patches/server/0072-Allow-color-codes-in-books.patch b/patches/server/0072-Allow-color-codes-in-books.patch deleted file mode 100644 index 107244ac1..000000000 --- a/patches/server/0072-Allow-color-codes-in-books.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 3 Nov 2020 01:25:06 -0600 -Subject: [PATCH] Allow color codes in books - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 317342df5d3afe65e09853fd9ffe4aca127152d2..970c711a6631cf3e16ca09704452b32b68672ce9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1359,13 +1359,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - itemstack1.setTag(nbttagcompound.copy()); - } - -+ // Purpur start -+ boolean hasPerm = getCraftPlayer().hasPermission("purpur.book.color.edit") || getCraftPlayer().hasPermission("purpur.book.color.sign"); - itemstack1.addTagElement("author", StringTag.valueOf(this.player.getName().getString())); - if (this.player.isTextFilteringEnabled()) { -- itemstack1.addTagElement("title", StringTag.valueOf(title.filteredOrEmpty())); -+ itemstack1.addTagElement("title", StringTag.valueOf(color(title.filteredOrEmpty(), hasPerm))); - } else { -- itemstack1.addTagElement("filtered_title", StringTag.valueOf(title.filteredOrEmpty())); -- itemstack1.addTagElement("title", StringTag.valueOf(title.raw())); -+ itemstack1.addTagElement("filtered_title", StringTag.valueOf(color(title.filteredOrEmpty(), hasPerm))); -+ itemstack1.addTagElement("title", StringTag.valueOf(color(title.raw(), hasPerm))); - } -+ // Purpur end - - this.updateBookPages(pages, (s) -> { - return Component.Serializer.toJson(Component.literal(s)); -@@ -1377,10 +1380,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - private void updateBookPages(List list, UnaryOperator unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit - ListTag nbttaglist = new ListTag(); - -+ // Purpur start -+ boolean hasPerm = getCraftPlayer().hasPermission("purpur.book.color.edit"); - if (this.player.isTextFilteringEnabled()) { -- Stream stream = list.stream().map((filteredtext) -> { // CraftBukkit - decompile error -- return StringTag.valueOf((String) unaryoperator.apply(filteredtext.filteredOrEmpty())); -+ Stream stream = list.stream().map(s -> color(s.filteredOrEmpty(), hasPerm, false)).map((s) -> { // CraftBukkit - decompile error -+ return StringTag.valueOf((String) unaryoperator.apply(s)); - }); -+ // Purpur end - - Objects.requireNonNull(nbttaglist); - stream.forEach(nbttaglist::add); -@@ -1390,11 +1396,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - for (int j = list.size(); i < j; ++i) { - FilteredText filteredtext = (FilteredText) list.get(i); -- String s = filteredtext.raw(); -+ String s = color(filteredtext.raw(), hasPerm, false); // Purpur - - nbttaglist.add(StringTag.valueOf((String) unaryoperator.apply(s))); - if (filteredtext.isFiltered()) { -- nbttagcompound.putString(String.valueOf(i), (String) unaryoperator.apply(filteredtext.filteredOrEmpty())); -+ nbttagcompound.putString(String.valueOf(i), (String) unaryoperator.apply((String) color(filteredtext.filteredOrEmpty(), hasPerm, false))); // Purpur - } - } - -@@ -1407,6 +1413,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.player.getInventory().setItem(slot, CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) - } - -+ // Purpur start -+ private String color(String str, boolean hasPerm) { -+ return color(str, hasPerm, true); -+ } -+ -+ private String color(String str, boolean hasPerm, boolean parseHex) { -+ return hasPerm ? org.bukkit.ChatColor.color(str, parseHex) : str; -+ } -+ // Purpur end -+ - @Override - public void handleEntityTagQuery(ServerboundEntityTagQuery packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); diff --git a/patches/server/0073-Entity-lifespan.patch b/patches/server/0073-Entity-lifespan.patch deleted file mode 100644 index 4c29af41a..000000000 --- a/patches/server/0073-Entity-lifespan.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 11 Jul 2020 19:41:34 -0500 -Subject: [PATCH] Entity lifespan - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 970c711a6631cf3e16ca09704452b32b68672ce9..8126b1a67e62077c8a78f13deee767236665d7fa 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2922,6 +2922,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - AABB axisalignedbb = entity.getBoundingBox(); - - if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { -+ if (entity instanceof Mob mob) mob.ticksSinceLastInteraction = 0; // Purpur - packet.dispatch(new ServerboundInteractPacket.Handler() { - private void performInteraction(InteractionHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit - ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand); -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index ef8d0f594c727f355a8209f36da924ae45017ce5..64a682ea339809adfb8304a7f14d4a596e5332be 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -133,6 +133,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - private BlockPos restrictCenter; - private float restrictRadius; - -+ public int ticksSinceLastInteraction; // Purpur - public boolean aware = true; // CraftBukkit - - protected Mob(EntityType type, Level world) { -@@ -323,6 +324,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - entityliving = null; - } - } -+ if (entityliving instanceof ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur - this.target = entityliving; - return true; - // CraftBukkit end -@@ -370,8 +372,28 @@ public abstract class Mob extends LivingEntity implements Targeting { - } - - this.level().getProfiler().pop(); -+ incrementTicksSinceLastInteraction(); // Purpur - } - -+ // Purpur start -+ private void incrementTicksSinceLastInteraction() { -+ ++this.ticksSinceLastInteraction; -+ if (getRider() != null) { -+ this.ticksSinceLastInteraction = 0; -+ return; -+ } -+ if (this.level().purpurConfig.entityLifeSpan <= 0) { -+ return; // feature disabled -+ } -+ if (!this.removeWhenFarAway(0) || isPersistenceRequired() || requiresCustomPersistence() || hasCustomName()) { -+ return; // mob persistent -+ } -+ if (this.ticksSinceLastInteraction > this.level().purpurConfig.entityLifeSpan) { -+ this.discard(); -+ } -+ } -+ // Purpur end -+ - @Override - protected void playHurtSound(DamageSource source) { - this.resetAmbientSoundTime(); -@@ -561,6 +583,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - } - - nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit -+ nbt.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur - } - - @Override -@@ -631,6 +654,11 @@ public abstract class Mob extends LivingEntity implements Targeting { - this.aware = nbt.getBoolean("Bukkit.Aware"); - } - // CraftBukkit end -+ // Purpur start -+ if (nbt.contains("Purpur.ticksSinceLastInteraction")) { -+ this.ticksSinceLastInteraction = nbt.getInt("Purpur.ticksSinceLastInteraction"); -+ } -+ // Purpur end - } - - @Override -@@ -1694,6 +1722,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - this.setLastHurtMob(target); - } - -+ if (target instanceof ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur - return flag; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index dc7666906fb04bf8f1df54ac81e04d8ad76a893b..d315a4aac382462b1bd0d44b6b5a88552f663d36 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -125,6 +125,11 @@ public class PurpurWorldConfig { - elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost); - } - -+ public int entityLifeSpan = 0; -+ private void entitySettings() { -+ entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); -+ } -+ - public List itemImmuneToCactus = new ArrayList<>(); - public List itemImmuneToExplosion = new ArrayList<>(); - public List itemImmuneToFire = new ArrayList<>(); diff --git a/patches/server/0074-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch b/patches/server/0074-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch deleted file mode 100644 index 8917f03b5..000000000 --- a/patches/server/0074-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 13 Jul 2020 11:40:00 -0500 -Subject: [PATCH] Add option to teleport to spawn if outside world border - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 03b19d56e4f75d71f592f400f59c9becd39c4d4f..459f8bcf13ed759c40eff1ade5d9011ede8547c3 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2764,4 +2764,26 @@ public class ServerPlayer extends Player { - return (CraftPlayer) super.getBukkitEntity(); - } - // CraftBukkit end -+ -+ // Purpur start -+ public void teleport(Location to) { -+ this.ejectPassengers(); -+ this.stopRiding(true); -+ -+ if (this.isSleeping()) { -+ this.stopSleepInBed(true, false); -+ } -+ -+ if (this.containerMenu != this.inventoryMenu) { -+ this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT); -+ } -+ -+ ServerLevel toLevel = ((CraftWorld) to.getWorld()).getHandle(); -+ if (this.level() == toLevel) { -+ this.connection.internalTeleport(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), java.util.EnumSet.noneOf(net.minecraft.world.entity.RelativeMovement.class)); -+ } else { -+ this.server.getPlayerList().respawn(this, toLevel, true, to, !toLevel.paperConfig().environment.disableTeleportationSuffocationCheck, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 35e3cac34a6a508fbf3971cff9ac07db48ddeaae..8c0ef795691aa17e9522b2deda794bcdf38c64b6 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -427,6 +427,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - double d1 = this.level().getWorldBorder().getDamagePerBlock(); - - if (d1 > 0.0D) { -+ if (level().purpurConfig.teleportIfOutsideBorder && this instanceof ServerPlayer serverPlayer) { serverPlayer.teleport(io.papermc.paper.util.MCUtil.toLocation(level(), ((ServerLevel) level()).getSharedSpawnPos())); return; } // Purpur - this.hurt(this.damageSources().outOfBorder(), (float) Math.max(1, Mth.floor(-d0 * d1))); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d315a4aac382462b1bd0d44b6b5a88552f663d36..8fdbaa0cd95186b3b48aac1eb953e268554726e4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -244,6 +244,7 @@ public class PurpurWorldConfig { - public boolean playerInvulnerableWhileAcceptingResourcePack = false; - public String playerDeathExpDropEquation = "expLevel * 7"; - public int playerDeathExpDropMax = 100; -+ public boolean teleportIfOutsideBorder = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -259,6 +260,7 @@ public class PurpurWorldConfig { - playerInvulnerableWhileAcceptingResourcePack = getBoolean("gameplay-mechanics.player.invulnerable-while-accepting-resource-pack", playerInvulnerableWhileAcceptingResourcePack); - playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation); - playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); -+ teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0075-Squid-EAR-immunity.patch b/patches/server/0075-Squid-EAR-immunity.patch deleted file mode 100644 index c9837884e..000000000 --- a/patches/server/0075-Squid-EAR-immunity.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 13 Jul 2020 13:49:41 -0500 -Subject: [PATCH] Squid EAR immunity - - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8fdbaa0cd95186b3b48aac1eb953e268554726e4..adfb558d98990739b24e10c7bf941287d30737a0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1457,6 +1457,7 @@ public class PurpurWorldConfig { - public boolean squidRidable = false; - public boolean squidControllable = true; - public double squidMaxHealth = 10.0D; -+ public boolean squidImmuneToEAR = true; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -1466,6 +1467,7 @@ public class PurpurWorldConfig { - set("mobs.squid.attributes.max_health", oldValue); - } - squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth); -+ squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR); - } - - public boolean spiderRidable = false; -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 12adaba78ec30e463963f99c0d78e844756143a1..79d027e517ce08443d86b877a55df24cc20d102b 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -15,6 +15,7 @@ import net.minecraft.world.entity.ambient.AmbientCreature; - import net.minecraft.world.entity.animal.Animal; - import net.minecraft.world.entity.animal.Bee; - import net.minecraft.world.entity.animal.Sheep; -+import net.minecraft.world.entity.animal.Squid; - import net.minecraft.world.entity.animal.WaterAnimal; - import net.minecraft.world.entity.animal.horse.Llama; - import net.minecraft.world.entity.boss.EnderDragonPart; -@@ -397,6 +398,7 @@ public class ActivationRange - */ - public static boolean checkIfActive(Entity entity) - { -+ if (entity.level().purpurConfig.squidImmuneToEAR && entity instanceof Squid) return true; // Purpur - // Never safe to skip fireworks or entities not yet added to chunk - if ( entity instanceof FireworkRocketEntity ) { - return true; diff --git a/patches/server/0076-Phantoms-burn-in-light.patch b/patches/server/0076-Phantoms-burn-in-light.patch deleted file mode 100644 index 921e6ca84..000000000 --- a/patches/server/0076-Phantoms-burn-in-light.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: draycia -Date: Sun, 12 Apr 2020 20:41:59 -0700 -Subject: [PATCH] Phantoms burn in light - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 86df67578334a4743909c748213c2e1ed5d19bd9..899a7d3989b51456600787ae09b1736f83bf9a65 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -49,6 +49,7 @@ public class Phantom extends FlyingMob implements Enemy { - Vec3 moveTargetPoint; - public BlockPos anchorPoint; - Phantom.AttackPhase attackPhase; -+ private static final net.minecraft.world.item.crafting.Ingredient TORCH = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.item.Items.TORCH, net.minecraft.world.item.Items.SOUL_TORCH); // Purpur - Vec3 crystalPosition; // Purpur - - public Phantom(EntityType type, Level world) { -@@ -246,7 +247,12 @@ public class Phantom extends FlyingMob implements Enemy { - - @Override - public void aiStep() { -- if (this.isAlive() && shouldBurnInDay && this.isSunBurnTick()) { // Paper - Configurable Burning -+ // Purpur start -+ boolean burnFromDaylight = this.shouldBurnInDay && this.level().purpurConfig.phantomBurnInDaylight; -+ boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; -+ if (this.isAlive() && (burnFromDaylight || burnFromLightSource)) { // Paper - Configurable Burning -+ if (getRider() == null || !this.isControllable()) -+ // Purpur end - if (getRider() == null || !this.isControllable()) // Purpur - this.setSecondsOnFire(8); - } -@@ -656,6 +662,12 @@ public class Phantom extends FlyingMob implements Enemy { - return false; - } else if (!entityliving.isAlive()) { - return false; -+ // Purpur start -+ } else if (level().purpurConfig.phantomBurnInLight > 0 && level().getLightEmission(new BlockPos(Phantom.this)) >= level().purpurConfig.phantomBurnInLight) { -+ return false; -+ } else if (level().purpurConfig.phantomIgnorePlayersWithTorch && (TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)))) { -+ return false; -+ // Purpur end - } else { - if (entityliving instanceof Player) { - Player entityhuman = (Player) entityliving; -@@ -801,6 +813,7 @@ public class Phantom extends FlyingMob implements Enemy { - this.nextScanTick = reducedTickDelay(60); - List list = Phantom.this.level().getNearbyPlayers(this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0D, 64.0D, 16.0D)); - -+ if (level().purpurConfig.phantomIgnorePlayersWithTorch) list.removeIf(human -> TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)));// Purpur - if (!list.isEmpty()) { - list.sort(Comparator.comparing((Entity e) -> { return e.getY(); }).reversed()); // CraftBukkit - decompile error - Iterator iterator = list.iterator(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index adfb558d98990739b24e10c7bf941287d30737a0..d6221af9b94530882a93c178d555dfc363f241ce 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1127,6 +1127,9 @@ public class PurpurWorldConfig { - public double phantomSpawnLocalDifficultyChance = 3.0D; - public int phantomSpawnMinPerAttempt = 1; - public int phantomSpawnMaxPerAttempt = -1; -+ public int phantomBurnInLight = 0; -+ public boolean phantomIgnorePlayersWithTorch = false; -+ public boolean phantomBurnInDaylight = true; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -1157,6 +1160,9 @@ public class PurpurWorldConfig { - phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance); - phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt); - phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt); -+ phantomBurnInLight = getInt("mobs.phantom.burn-in-light", phantomBurnInLight); -+ phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight); -+ phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch); - } - - public boolean pigRidable = false; diff --git a/patches/server/0077-Configurable-villager-breeding.patch b/patches/server/0077-Configurable-villager-breeding.patch deleted file mode 100644 index ed9d97fee..000000000 --- a/patches/server/0077-Configurable-villager-breeding.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: draycia -Date: Tue, 31 Mar 2020 23:48:55 -0700 -Subject: [PATCH] Configurable villager breeding - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 50925ab7d719ae3323d7456df03a3d2ab3481bfd..7d13a5308161d4093023b732bed7d06152f54737 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -785,7 +785,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - - @Override - public boolean canBreed() { -- return this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; -+ return this.level().purpurConfig.villagerCanBreed && this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; // Purpur - } - - private boolean hungry() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d6221af9b94530882a93c178d555dfc363f241ce..b9873e66e91d596e17002a290027b0a9972a54fd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1615,6 +1615,7 @@ public class PurpurWorldConfig { - public double villagerMaxHealth = 20.0D; - public boolean villagerFollowEmeraldBlock = false; - public boolean villagerCanBeLeashed = false; -+ public boolean villagerCanBreed = true; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1627,6 +1628,7 @@ public class PurpurWorldConfig { - villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth); - villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock); - villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed); -+ villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0078-Redstone-deactivates-spawners.patch b/patches/server/0078-Redstone-deactivates-spawners.patch deleted file mode 100644 index 6c7656482..000000000 --- a/patches/server/0078-Redstone-deactivates-spawners.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: draycia -Date: Tue, 14 Apr 2020 00:35:12 -0700 -Subject: [PATCH] Redstone deactivates spawners - - -diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java -index 633500aefd515df5dadda3802b94079f75a03fa0..64d911bee1607880514061c75116d8672df8bb8f 100644 ---- a/src/main/java/net/minecraft/world/level/BaseSpawner.java -+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java -@@ -55,6 +55,7 @@ public abstract class BaseSpawner { - } - - public boolean isNearPlayer(Level world, BlockPos pos) { -+ if (world.purpurConfig.spawnerDeactivateByRedstone && world.hasNeighborSignal(pos)) return false; // Purpur - return world.hasNearbyAlivePlayerThatAffectsSpawning((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange); // Paper - Affects Spawning API - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b9873e66e91d596e17002a290027b0a9972a54fd..10994174be125f8ce819d4d6e851178a72779ba1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -356,6 +356,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean spawnerDeactivateByRedstone = false; -+ private void spawnerSettings() { -+ spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; diff --git a/patches/server/0079-Totems-work-in-inventory.patch b/patches/server/0079-Totems-work-in-inventory.patch deleted file mode 100644 index 165234c56..000000000 --- a/patches/server/0079-Totems-work-in-inventory.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: draycia -Date: Wed, 29 Apr 2020 00:45:58 -0700 -Subject: [PATCH] Totems work in inventory - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 8c0ef795691aa17e9522b2deda794bcdf38c64b6..6edb9c371d77fa52b08f0eaf738d0cb4b0af86ce 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1590,6 +1590,18 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - } - -+ // Purpur start -+ if (level().purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemstack == null || itemstack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) { -+ for (ItemStack item : player.getInventory().items) { -+ if (item.getItem() == Items.TOTEM_OF_UNDYING) { -+ itemstack1 = item; -+ itemstack = item.copy(); -+ break; -+ } -+ } -+ } -+ // Purpur end -+ - org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null; - EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot); - event.setCancelled(itemstack == null); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 10994174be125f8ce819d4d6e851178a72779ba1..9335574d7396da50fe027cb455847cfef6528436 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -245,6 +245,7 @@ public class PurpurWorldConfig { - public String playerDeathExpDropEquation = "expLevel * 7"; - public int playerDeathExpDropMax = 100; - public boolean teleportIfOutsideBorder = false; -+ public boolean totemOfUndyingWorksInInventory = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -261,6 +262,7 @@ public class PurpurWorldConfig { - playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation); - playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); - teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); -+ totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0080-Add-vindicator-johnny-spawn-chance.patch b/patches/server/0080-Add-vindicator-johnny-spawn-chance.patch deleted file mode 100644 index ef007c57a..000000000 --- a/patches/server/0080-Add-vindicator-johnny-spawn-chance.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 24 Jul 2020 19:38:21 -0500 -Subject: [PATCH] Add vindicator johnny spawn chance - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index 40858ac2a9b58108472748d0ef2c2fd5ef5cfd98..2a5b6b94b15bf066b5722e7a4f782bc87f9debf4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -154,6 +154,12 @@ public class Vindicator extends AbstractIllager { - RandomSource randomSource = world.getRandom(); - this.populateDefaultEquipmentSlots(randomSource, difficulty); - this.populateDefaultEquipmentEnchantments(randomSource, difficulty); -+ // Purpur start -+ Level level = world.getMinecraftWorld(); -+ if (level().purpurConfig.vindicatorJohnnySpawnChance > 0D && random.nextDouble() <= level().purpurConfig.vindicatorJohnnySpawnChance) { -+ setCustomName(Component.translatable("Johnny")); -+ } -+ // Purpur end - return spawnGroupData; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9335574d7396da50fe027cb455847cfef6528436..1b7d8a8fc241e096fc4fd5b65d8444779d1deae5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1642,6 +1642,7 @@ public class PurpurWorldConfig { - public boolean vindicatorRidableInWater = true; - public boolean vindicatorControllable = true; - public double vindicatorMaxHealth = 24.0D; -+ public double vindicatorJohnnySpawnChance = 0D; - private void vindicatorSettings() { - vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); - vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); -@@ -1652,6 +1653,7 @@ public class PurpurWorldConfig { - set("mobs.vindicator.attributes.max_health", oldValue); - } - vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth); -+ vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance); - } - - public boolean wanderingTraderRidable = false; diff --git a/patches/server/0081-Dispensers-place-anvils-option.patch b/patches/server/0081-Dispensers-place-anvils-option.patch deleted file mode 100644 index 20435a501..000000000 --- a/patches/server/0081-Dispensers-place-anvils-option.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 4 Aug 2020 21:11:03 -0500 -Subject: [PATCH] Dispensers place anvils option - - -diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java -index 70aade6a8d36f8376cc567800258ea6fabb0607f..d44fde7e44cc862253fe577eb0753524ab7a39dc 100644 ---- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java -+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java -@@ -1186,6 +1186,23 @@ public interface DispenseItemBehavior { - } - } - }); -+ // Purpur start -+ DispenserBlock.registerBehavior(Items.ANVIL, (new OptionalDispenseItemBehavior() { -+ @Override -+ public ItemStack execute(BlockSource dispenser, ItemStack stack) { -+ Level level = dispenser.getLevel(); -+ if (!level.purpurConfig.dispenserPlaceAnvils) return super.execute(dispenser, stack); -+ Direction facing = dispenser.getBlockState().getValue(DispenserBlock.FACING); -+ BlockPos pos = dispenser.getPos().relative(facing); -+ BlockState state = level.getBlockState(pos); -+ if (state.isAir()) { -+ level.setBlockAndUpdate(pos, Blocks.ANVIL.defaultBlockState().setValue(net.minecraft.world.level.block.AnvilBlock.FACING, facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : facing.getClockWise())); -+ stack.shrink(1); -+ } -+ return stack; -+ } -+ })); -+ // Purpur end - } - - static void setEntityPokingOutOfBlock(BlockSource pointer, Entity entity, Direction direction) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1b7d8a8fc241e096fc4fd5b65d8444779d1deae5..0e473df153cfd9090b74b5fa2b5568220d61ae98 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -319,8 +319,10 @@ public class PurpurWorldConfig { - } - - public boolean dispenserApplyCursedArmor = true; -+ public boolean dispenserPlaceAnvils = false; - private void dispenserSettings() { - dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor); -+ dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils); - } - - public boolean farmlandGetsMoistFromBelow = false; diff --git a/patches/server/0082-Allow-anvil-colors.patch b/patches/server/0082-Allow-anvil-colors.patch deleted file mode 100644 index b0fdf81c2..000000000 --- a/patches/server/0082-Allow-anvil-colors.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 4 Aug 2020 22:08:23 -0500 -Subject: [PATCH] Allow anvil colors - - -diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -index b500a04b8135604f0159a741b3d228c9e87b2a46..8a7c30e316db4960b0b62ca0e366c19febead214 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -294,6 +294,54 @@ public class AnvilMenu extends ItemCombinerMenu { - if (!this.itemName.equals(itemstack.getHoverName().getString())) { - b1 = 1; - i += b1; -+ // Purpur start -+ if (this.player != null) { -+ org.bukkit.craftbukkit.entity.CraftHumanEntity player = this.player.getBukkitEntity(); -+ String name = this.itemName; -+ boolean removeItalics = false; -+ if (player.hasPermission("purpur.anvil.remove_italics")) { -+ if (name.startsWith("&r")) { -+ name = name.substring(2); -+ removeItalics = true; -+ } else if (name.startsWith("")) { -+ name = name.substring(3); -+ removeItalics = true; -+ } else if (name.startsWith("")) { -+ name = name.substring(7); -+ removeItalics = true; -+ } -+ } -+ if (this.player.level().purpurConfig.anvilAllowColors) { -+ if (player.hasPermission("purpur.anvil.color")) { -+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([0-9a-fr])").matcher(name); -+ while (matcher.find()) { -+ String match = matcher.group(1); -+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT)); -+ } -+ //name = name.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1"); -+ } -+ if (player.hasPermission("purpur.anvil.format")) { -+ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([k-or])").matcher(name); -+ while (matcher.find()) { -+ String match = matcher.group(1); -+ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT)); -+ } -+ //name = name.replaceAll("(?i)&([l-or])", "\u00a7$1"); -+ } -+ } -+ net.kyori.adventure.text.Component component; -+ if (this.player.level().purpurConfig.anvilColorsUseMiniMessage && player.hasPermission("purpur.anvil.minimessage")) { -+ component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.bukkit.ChatColor.stripColor(name)); -+ } else { -+ component = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(name); -+ } -+ if (removeItalics) { -+ component = component.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); -+ } -+ itemstack1.setHoverName(io.papermc.paper.adventure.PaperAdventure.asVanilla(component)); -+ } -+ else -+ // Purpur end - itemstack1.setHoverName(Component.literal(this.itemName)); - } - } else if (itemstack.hasCustomHoverName()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0e473df153cfd9090b74b5fa2b5568220d61ae98..9344d7492585023f5de6db4e4c0cde41dc6c74cd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -297,6 +297,13 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean anvilAllowColors = false; -+ public boolean anvilColorsUseMiniMessage; -+ private void anvilSettings() { -+ anvilAllowColors = getBoolean("blocks.anvil.allow-colors", anvilAllowColors); -+ anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage); -+ } -+ - public boolean bedExplode = true; - public double bedExplosionPower = 5.0D; - public boolean bedExplosionFire = true; diff --git a/patches/server/0083-Add-option-to-disable-dolphin-treasure-searching.patch b/patches/server/0083-Add-option-to-disable-dolphin-treasure-searching.patch deleted file mode 100644 index ae26ab08e..000000000 --- a/patches/server/0083-Add-option-to-disable-dolphin-treasure-searching.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 8 Aug 2020 16:11:51 -0500 -Subject: [PATCH] Add option to disable dolphin treasure searching - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index 89e3dbfddc739f97fdb6ec9a5714530f03cc7092..079fd78528377ee4236fb2e7189a5f0fc5ec4fb3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -485,6 +485,7 @@ public class Dolphin extends WaterAnimal { - - @Override - public boolean canUse() { -+ if (this.dolphin.level().purpurConfig.dolphinDisableTreasureSearching) return false; // Purpur - return this.dolphin.gotFish() && this.dolphin.getAirSupply() >= 100; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9344d7492585023f5de6db4e4c0cde41dc6c74cd..d230e75bebc1817c171cdd93a1634512d652ceea 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -614,6 +614,7 @@ public class PurpurWorldConfig { - public float dolphinSpitSpeed = 1.0F; - public float dolphinSpitDamage = 2.0F; - public double dolphinMaxHealth = 10.0D; -+ public boolean dolphinDisableTreasureSearching = false; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -626,6 +627,7 @@ public class PurpurWorldConfig { - set("mobs.dolphin.attributes.max_health", oldValue); - } - dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth); -+ dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching); - } - - public boolean donkeyRidableInWater = false; diff --git a/patches/server/0084-Short-enderman-height.patch b/patches/server/0084-Short-enderman-height.patch deleted file mode 100644 index 69cb9329e..000000000 --- a/patches/server/0084-Short-enderman-height.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 10 Aug 2020 21:46:22 -0500 -Subject: [PATCH] Short enderman height - - -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 08beb4c4dfcb0986cdebb4d0cacc25e4e9c17674..e413aa4650297ce2109beb6319f52fb476e291fe 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -308,7 +308,8 @@ public class EntityType implements FeatureElement, EntityTypeT - private Component description; - @Nullable - private ResourceLocation lootTable; -- private final EntityDimensions dimensions; -+ private EntityDimensions dimensions; // Purpur - remove final -+ public void setDimensions(EntityDimensions dimensions) { this.dimensions = dimensions; } // Purpur - private final FeatureFlagSet requiredFeatures; - - private static EntityType register(String id, EntityType.Builder type) { // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 74d1a0e8de36aaf11e844cc4f40e4c469d6269b4..3861f367ac01028139cc6db35fb9a1e20cb39457 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -433,6 +433,7 @@ public class EnderMan extends Monster implements NeutralMob { - if (this.isInvulnerableTo(source)) { - return false; - } else if (getRider() != null && this.isControllable()) { return super.hurt(source, amount); // Purpur - no teleporting on damage -+ } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && source.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height - } else { - boolean flag = source.getDirectEntity() instanceof ThrownPotion; - boolean flag1; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 387da71594b75a43100790e8e8d69d9246598537..e813c87f4cc90fd31a4a48fcfffbe2b4407e5f5d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -244,6 +244,12 @@ public class PurpurConfig { - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - } - -+ public static boolean endermanShortHeight = false; -+ private static void entitySettings() { -+ endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight); -+ if (endermanShortHeight) EntityType.ENDERMAN.setDimensions(EntityDimensions.scalable(0.6F, 1.9F)); -+ } -+ - public static boolean allowWaterPlacementInTheEnd = true; - private static void allowWaterPlacementInEnd() { - allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd); diff --git a/patches/server/0085-Stop-squids-floating-on-top-of-water.patch b/patches/server/0085-Stop-squids-floating-on-top-of-water.patch deleted file mode 100644 index ab06d6f53..000000000 --- a/patches/server/0085-Stop-squids-floating-on-top-of-water.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 13 Aug 2020 04:00:26 -0500 -Subject: [PATCH] Stop squids floating on top of water - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 01a1534ec503e9aaea7befdba86f452128f8863d..6935e327b4c1580dae9fbf2279bde4af165f76ce 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4349,6 +4349,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.yRotO = this.getYRot(); - } - -+ // Purpur start -+ public AABB getAxisForFluidCheck() { -+ return this.getBoundingBox().deflate(0.001D); -+ } -+ // Purpur end -+ - public boolean updateFluidHeightAndDoFluidPushing(TagKey tag, double speed) { - if (false && this.touchingUnloadedChunk()) { // Pufferfish - cost of a lookup here is the same cost as below, so skip - return false; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index 913b66be2111da862e706d4978825c64cfe8b00b..f68c18b6645981126329b58379946308bbb8ccf8 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -75,6 +75,12 @@ public class Squid extends WaterAnimal { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.squidMaxHealth); - } - -+ @Override -+ public net.minecraft.world.phys.AABB getAxisForFluidCheck() { -+ // Stops squids from floating just over the water -+ return super.getAxisForFluidCheck().offsetY(level().purpurConfig.squidOffsetWaterCheck); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java -index ffc76354ead6937daf366c3d87bcb51d3e4c47f5..5b98d42b5d6bc07265fbb017e51a6281c148436a 100644 ---- a/src/main/java/net/minecraft/world/phys/AABB.java -+++ b/src/main/java/net/minecraft/world/phys/AABB.java -@@ -374,4 +374,10 @@ public class AABB { - public static AABB ofSize(Vec3 center, double dx, double dy, double dz) { - return new AABB(center.x - dx / 2.0D, center.y - dy / 2.0D, center.z - dz / 2.0D, center.x + dx / 2.0D, center.y + dy / 2.0D, center.z + dz / 2.0D); - } -+ -+ // Purpur - tuinity added method -+ public final AABB offsetY(double dy) { -+ return new AABB(this.minX, this.minY + dy, this.minZ, this.maxX, this.maxY + dy, this.maxZ); -+ } -+ // Purpur - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d230e75bebc1817c171cdd93a1634512d652ceea..88225ba1bad1a20cbb7bcd56b0cd7424661712c3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1482,6 +1482,7 @@ public class PurpurWorldConfig { - public boolean squidControllable = true; - public double squidMaxHealth = 10.0D; - public boolean squidImmuneToEAR = true; -+ public double squidOffsetWaterCheck = 0.0D; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -1492,6 +1493,7 @@ public class PurpurWorldConfig { - } - squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth); - squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR); -+ squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck); - } - - public boolean spiderRidable = false; diff --git a/patches/server/0086-Crying-obsidian-valid-for-portal-frames.patch b/patches/server/0086-Crying-obsidian-valid-for-portal-frames.patch deleted file mode 100644 index 1ecc6916a..000000000 --- a/patches/server/0086-Crying-obsidian-valid-for-portal-frames.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 17 Aug 2020 17:34:33 -0500 -Subject: [PATCH] Crying obsidian valid for portal frames - - -diff --git a/src/main/java/net/minecraft/world/level/portal/PortalShape.java b/src/main/java/net/minecraft/world/level/portal/PortalShape.java -index c461e0d04047db9c0c5ecc04063cebd38bf96ec2..e7554ec800f321e4e34c926c53f2375a8c3aa979 100644 ---- a/src/main/java/net/minecraft/world/level/portal/PortalShape.java -+++ b/src/main/java/net/minecraft/world/level/portal/PortalShape.java -@@ -34,7 +34,7 @@ public class PortalShape { - private static final int MIN_HEIGHT = 3; - public static final int MAX_HEIGHT = 21; - private static final BlockBehaviour.StatePredicate FRAME = (iblockdata, iblockaccess, blockposition) -> { -- return iblockdata.is(Blocks.OBSIDIAN); -+ return iblockdata.is(Blocks.OBSIDIAN) || (org.purpurmc.purpur.PurpurConfig.cryingObsidianValidForPortalFrame && iblockdata.is(Blocks.CRYING_OBSIDIAN)); // Purpur - }; - private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F; - private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0D; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index e813c87f4cc90fd31a4a48fcfffbe2b4407e5f5d..6470f383b6f044877f0a4d8c91119d0eae451b8f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -212,6 +212,7 @@ public class PurpurConfig { - public static int barrelRows = 3; - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; -+ public static boolean cryingObsidianValidForPortalFrame = false; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -242,6 +243,7 @@ public class PurpurConfig { - enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); - org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); -+ cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); - } - - public static boolean endermanShortHeight = false; diff --git a/patches/server/0087-Entities-can-use-portals-configuration.patch b/patches/server/0087-Entities-can-use-portals-configuration.patch deleted file mode 100644 index a1bdcabad..000000000 --- a/patches/server/0087-Entities-can-use-portals-configuration.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 17 Aug 2020 19:32:05 -0500 -Subject: [PATCH] Entities can use portals configuration - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 6935e327b4c1580dae9fbf2279bde4af165f76ce..105b093cd0353a45197615e9fcdf06e72a0aad00 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3063,7 +3063,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public void handleInsidePortal(BlockPos pos) { - if (this.isOnPortalCooldown()) { - this.setPortalCooldown(); -- } else { -+ } else if (level().purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - if (!this.level().isClientSide && !pos.equals(this.portalEntrancePos)) { - this.portalEntrancePos = pos.immutable(); - } -@@ -3764,7 +3764,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public boolean canChangeDimensions() { -- return !this.isPassenger() && !this.isVehicle() && isAlive() && valid; // Paper -+ return !this.isPassenger() && !this.isVehicle() && isAlive() && valid && (level().purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Paper // Purpur - } - - public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 88225ba1bad1a20cbb7bcd56b0cd7424661712c3..300c3d8a8fc34e95e9ad620aa603e66aa6847ae2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -98,6 +98,7 @@ public class PurpurWorldConfig { - public boolean useBetterMending = false; - public boolean boatEjectPlayersOnLand = false; - public boolean disableDropsOnCrammingDeath = false; -+ public boolean entitiesCanUsePortals = true; - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; -@@ -106,6 +107,7 @@ public class PurpurWorldConfig { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); -+ entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); diff --git a/patches/server/0089-Customizable-wither-health-and-healing.patch b/patches/server/0089-Customizable-wither-health-and-healing.patch deleted file mode 100644 index ccf375a03..000000000 --- a/patches/server/0089-Customizable-wither-health-and-healing.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Thu, 20 Aug 2020 17:38:12 -0700 -Subject: [PATCH] Customizable wither health and healing - -Adds the ability to customize the health of the wither, as well as the amount that it heals, and how often. - -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 6f7ef64612d6229179545d56093efdf58a2d7978..b395d5c85f26ca4252d3f8f886b2ee5a7892af5f 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -518,8 +518,10 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - } - } - -- if (this.tickCount % 20 == 0) { -- this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit -+ // Purpur start - customizable heal rate and amount -+ if (this.tickCount % level().purpurConfig.witherHealthRegenDelay == 0) { -+ this.heal(level().purpurConfig.witherHealthRegenAmount, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit -+ // Purpur end - } - - this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 300c3d8a8fc34e95e9ad620aa603e66aa6847ae2..32b2444c0e96d4ba53693764a4caf0b596ed947f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1721,6 +1721,8 @@ public class PurpurWorldConfig { - public boolean witherControllable = true; - public double witherMaxY = 320D; - public double witherMaxHealth = 300.0D; -+ public float witherHealthRegenAmount = 1.0f; -+ public int witherHealthRegenDelay = 20; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -1736,6 +1738,8 @@ public class PurpurWorldConfig { - set("mobs.wither.attributes.max_health", oldValue); - } - witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth); -+ witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount); -+ witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay); - } - - public boolean witherSkeletonRidable = false; diff --git a/patches/server/0090-Allow-toggling-special-MobSpawners-per-world.patch b/patches/server/0090-Allow-toggling-special-MobSpawners-per-world.patch deleted file mode 100644 index 1252665ad..000000000 --- a/patches/server/0090-Allow-toggling-special-MobSpawners-per-world.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sat, 22 Aug 2020 20:47:11 -0700 -Subject: [PATCH] Allow toggling special MobSpawners per world - -In vanilla, these are all hardcoded on for world type 0 (overworld) and hardcoded off for every other world type. Default config behaviour matches this. - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 11f92c1011a1accaf485e5785d2e9ebc8440406c..af57326c8d461d3c5d84f582d5bc6345c3c9367b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -663,7 +663,24 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.dragonParts = new Int2ObjectOpenHashMap(); - this.tickTime = flag1; - this.server = minecraftserver; -- this.customSpawners = list; -+ // Purpur start - enable/disable MobSpawners per world -+ this.customSpawners = Lists.newArrayList(); -+ if (purpurConfig.phantomSpawning) { -+ customSpawners.add(new net.minecraft.world.level.levelgen.PhantomSpawner()); -+ } -+ if (purpurConfig.patrolSpawning) { -+ customSpawners.add(new net.minecraft.world.level.levelgen.PatrolSpawner()); -+ } -+ if (purpurConfig.catSpawning) { -+ customSpawners.add(new net.minecraft.world.entity.npc.CatSpawner()); -+ } -+ if (purpurConfig.villageSiegeSpawning) { -+ customSpawners.add(new net.minecraft.world.entity.ai.village.VillageSiege()); -+ } -+ if (purpurConfig.villagerTraderSpawning) { -+ customSpawners.add(new net.minecraft.world.entity.npc.WanderingTraderSpawner(iworlddataserver)); -+ } -+ // Purpur end - this.serverLevelData = iworlddataserver; - ChunkGenerator chunkgenerator = worlddimension.generator(); - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java -index 8385eb1d60f377da94e3178ab506feefb43563fd..a5443f92786427c42092aec8350e7ab37704db7a 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java -@@ -159,7 +159,17 @@ public class WanderingTraderSpawner implements CustomSpawner { - int k = pos.getX() + this.random.nextInt(range * 2) - range; - int l = pos.getZ() + this.random.nextInt(range * 2) - range; - int i1 = world.getHeight(Heightmap.Types.WORLD_SURFACE, k, l); -- BlockPos blockposition2 = new BlockPos(k, i1, l); -+ // Purpur start - allow traders to spawn below nether roof -+ BlockPos.MutableBlockPos blockposition2 = new BlockPos.MutableBlockPos(k, i1, l); -+ if (world.dimensionType().hasCeiling()) { -+ do { -+ blockposition2.relative(net.minecraft.core.Direction.DOWN); -+ } while (!world.getBlockState(blockposition2).isAir()); -+ do { -+ blockposition2.relative(net.minecraft.core.Direction.DOWN); -+ } while (world.getBlockState(blockposition2).isAir() && blockposition2.getY() > 0); -+ } -+ // Purpur end - - if (NaturalSpawner.isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, world, blockposition2, EntityType.WANDERING_TRADER)) { - blockposition1 = blockposition2; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 32b2444c0e96d4ba53693764a4caf0b596ed947f..e0447c62f0c5df368a3ea41d63f64e23ccb06271 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -69,6 +69,12 @@ public class PurpurWorldConfig { - return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path)); - } - -+ private boolean getBoolean(String path, Predicate predicate) { -+ String val = getString(path, "default").toLowerCase(); -+ Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default"); -+ return predicate.test(bool); -+ } -+ - private double getDouble(String path, double def) { - PurpurConfig.config.addDefault("world-settings.default." + path, def); - return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path)); -@@ -237,6 +243,21 @@ public class PurpurWorldConfig { - } - } - -+ public boolean catSpawning; -+ public boolean patrolSpawning; -+ public boolean phantomSpawning; -+ public boolean villagerTraderSpawning; -+ public boolean villageSiegeSpawning; -+ private void mobSpawnerSettings() { -+ // values of "default" or null will default to true only if the world environment is normal (aka overworld) -+ Predicate predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL); -+ catSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-cats", predicate); -+ patrolSpawning = getBoolean("gameplay-mechanics.mob-spawning.raid-patrols", predicate); -+ phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate); -+ villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate); -+ villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate); -+ } -+ - public boolean idleTimeoutKick = true; - public boolean idleTimeoutTickNearbyEntities = true; - public boolean idleTimeoutCountAsSleeping = false; diff --git a/patches/server/0091-Raid-cooldown-setting.patch b/patches/server/0091-Raid-cooldown-setting.patch deleted file mode 100644 index bf934694c..000000000 --- a/patches/server/0091-Raid-cooldown-setting.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Thu, 27 Aug 2020 13:48:52 -0700 -Subject: [PATCH] Raid cooldown setting - - -diff --git a/src/main/java/net/minecraft/world/entity/raid/Raids.java b/src/main/java/net/minecraft/world/entity/raid/Raids.java -index 41457c9f27b18fa2734a6cca297ec5186470e82f..94356e0541f8f4da68211fa533347cc97d4f3518 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raids.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raids.java -@@ -28,6 +28,7 @@ import net.minecraft.world.phys.Vec3; - public class Raids extends SavedData { - - private static final String RAID_FILE_ID = "raids"; -+ public final Map playerCooldowns = Maps.newHashMap(); - public final Map raidMap = Maps.newHashMap(); - private final ServerLevel level; - private int nextAvailableID; -@@ -45,6 +46,17 @@ public class Raids extends SavedData { - - public void tick() { - ++this.tick; -+ // Purpur start -+ if (level.purpurConfig.raidCooldownSeconds != 0 && this.tick % 20 == 0) { -+ com.google.common.collect.ImmutableMap.copyOf(playerCooldowns).forEach((uuid, i) -> { -+ if (i < 1) { -+ playerCooldowns.remove(uuid); -+ } else { -+ playerCooldowns.put(uuid, i - 1); -+ } -+ }); -+ } -+ // Purpur end - Iterator iterator = this.raidMap.values().iterator(); - - while (iterator.hasNext()) { -@@ -129,11 +141,13 @@ public class Raids extends SavedData { - } - - if (flag) { -+ if (level.purpurConfig.raidCooldownSeconds != 0 && playerCooldowns.containsKey(player.getUUID())) return null; // Purpur - // CraftBukkit start - if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(raid, player)) { - player.removeEffect(MobEffects.BAD_OMEN); - return null; - } -+ if (level.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), level.purpurConfig.raidCooldownSeconds); // Purpur - - if (!this.raidMap.containsKey(raid.getId())) { - this.raidMap.put(raid.getId(), raid); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e0447c62f0c5df368a3ea41d63f64e23ccb06271..f2db6143daf0ec578b224360273b8dc5ffa3bb43 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -109,6 +109,7 @@ public class PurpurWorldConfig { - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; -+ public int raidCooldownSeconds = 0; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -@@ -118,6 +119,7 @@ public class PurpurWorldConfig { - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); -+ raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds); - } - - public int elytraDamagePerSecond = 1; diff --git a/patches/server/0092-Despawn-rate-config-options-per-projectile-type.patch b/patches/server/0092-Despawn-rate-config-options-per-projectile-type.patch deleted file mode 100644 index 3928e481f..000000000 --- a/patches/server/0092-Despawn-rate-config-options-per-projectile-type.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Mon, 14 Sep 2020 10:09:05 -0700 -Subject: [PATCH] Despawn rate config options per projectile type - -This patch's implementation has been removed in favor of Pufferfish's entity-timeouts. -The config remains for migration purposes. - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f2db6143daf0ec578b224360273b8dc5ffa3bb43..37bea4068461e0b2d3ded80529ca2a8c3e8473e2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -290,6 +290,39 @@ public class PurpurWorldConfig { - totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); - } - -+ private static boolean projectileDespawnRateSettingsMigrated = false; -+ private void projectileDespawnRateSettings() { -+ if (PurpurConfig.version < 28 && !projectileDespawnRateSettingsMigrated) { -+ migrateProjectileDespawnRateSettings(EntityType.DRAGON_FIREBALL); -+ migrateProjectileDespawnRateSettings(EntityType.EGG); -+ migrateProjectileDespawnRateSettings(EntityType.ENDER_PEARL); -+ migrateProjectileDespawnRateSettings(EntityType.EXPERIENCE_BOTTLE); -+ migrateProjectileDespawnRateSettings(EntityType.FIREWORK_ROCKET); -+ migrateProjectileDespawnRateSettings(EntityType.FISHING_BOBBER); -+ migrateProjectileDespawnRateSettings(EntityType.FIREBALL); -+ migrateProjectileDespawnRateSettings(EntityType.LLAMA_SPIT); -+ migrateProjectileDespawnRateSettings(EntityType.POTION); -+ migrateProjectileDespawnRateSettings(EntityType.SHULKER_BULLET); -+ migrateProjectileDespawnRateSettings(EntityType.SMALL_FIREBALL); -+ migrateProjectileDespawnRateSettings(EntityType.SNOWBALL); -+ migrateProjectileDespawnRateSettings(EntityType.WITHER_SKULL); -+ //PufferfishConfig.save(); -+ set("gameplay-mechanics.projectile-despawn-rates", null); -+ // pufferfish's entity_timeout is a global config -+ // we only want to migrate values from the -+ // default world (first world loaded) -+ projectileDespawnRateSettingsMigrated = true; -+ } -+ } -+ private void migrateProjectileDespawnRateSettings(EntityType type) { -+ //String pufferName = "entity_timeouts." + type.id.toUpperCase(Locale.ROOT); -+ //int value = getInt("gameplay-mechanics.projectile-despawn-rates." + type.id, -1); -+ //if (value != -1 && PufferfishConfig.getRawInt(pufferName, -1) == -1) { -+ // PufferfishConfig.setInt(pufferName, value); -+ // type.ttl = value; -+ //} -+ } -+ - public boolean silkTouchEnabled = false; - public String silkTouchSpawnerName = "Monster Spawner"; - public List silkTouchSpawnerLore = new ArrayList<>(); diff --git a/patches/server/0093-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/server/0093-Add-option-to-disable-zombie-aggressiveness-towards-.patch deleted file mode 100644 index ed6de1de1..000000000 --- a/patches/server/0093-Add-option-to-disable-zombie-aggressiveness-towards-.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: nitricspace -Date: Mon, 21 Sep 2020 23:19:43 +0100 -Subject: [PATCH] Add option to disable zombie aggressiveness towards villagers - - -diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java -index a08c00b8c0488d18be5e182f7892e5ab71d12247..338f693d098b6ab507c30f6411c9a952c34ba8e3 100644 ---- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java -+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java -@@ -136,6 +136,10 @@ public class MobGoalHelper { - static { - // TODO these kinda should be checked on each release, in case obfuscation changes - deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee"); -+ // Purpur start -+ deobfuscationMap.put("zombie_1", "zombie_attack_villager"); -+ deobfuscationMap.put("drowned_1", "drowned_attack_villager"); -+ // Purpur end - - ignored.add("goal_selector_1"); - ignored.add("goal_selector_2"); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 6ec7d0f5e525f9e04b6fa0fb6d4da5fcc22fe653..64acb2e81ef65acb0d41db8b5f7c924c2e2a5e00 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -120,7 +120,19 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D)); - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Drowned.class})).setAlertOthers(ZombifiedPiglin.class)); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::okTarget)); -- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper -+ // Purpur start -+ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Spigot -+ @Override -+ public boolean canUse() { -+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse(); -+ } -+ -+ @Override -+ public boolean canContinueToUse() { -+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse(); -+ } -+ }); -+ // Purpur end - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false)); - this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index b2b04d7b7bbc400fb66cac385fa754d4fb47c7d9..bd75aa78a7dd437d6fce79a6cb18298184affe75 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -156,7 +156,19 @@ public class Zombie extends Monster { - this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers(ZombifiedPiglin.class)); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); -- if ( this.level().spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Spigot -+ // Purpur start -+ if ( this.level().spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Spigot -+ @Override -+ public boolean canUse() { -+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse(); -+ } -+ -+ @Override -+ public boolean canContinueToUse() { -+ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse(); -+ } -+ }); -+ // Purpur end - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); - this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 37bea4068461e0b2d3ded80529ca2a8c3e8473e2..6e38987900f04d1aaf7030b3562c8571f12bbbde 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1854,6 +1854,7 @@ public class PurpurWorldConfig { - public boolean zombieJockeyOnlyBaby = true; - public double zombieJockeyChance = 0.05D; - public boolean zombieJockeyTryExistingChickens = true; -+ public boolean zombieAggressiveTowardsVillagerWhenLagging = true; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -1868,6 +1869,7 @@ public class PurpurWorldConfig { - zombieJockeyOnlyBaby = getBoolean("mobs.zombie.jockey.only-babies", zombieJockeyOnlyBaby); - zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance); - zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens); -+ zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging); - } - - public boolean zombieHorseRidableInWater = false; diff --git a/patches/server/0094-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/server/0094-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch deleted file mode 100644 index 224140f0c..000000000 --- a/patches/server/0094-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 3 Oct 2020 17:40:52 -0500 -Subject: [PATCH] Add predicate to recipe's ExactChoice ingredient - - -diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -index cd7ea0c16f9ddcb84b5d7e8a2533e6e84f3879c7..3ca086418ad037c48775db73d2b9c410acf1e326 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -@@ -39,6 +39,7 @@ public final class Ingredient implements Predicate { - @Nullable - private IntList stackingIds; - public boolean exact; // CraftBukkit -+ public Predicate predicate; - - public Ingredient(Stream entries) { - this.values = (Ingredient.Value[]) entries.toArray((i) -> { -@@ -64,6 +65,12 @@ public final class Ingredient implements Predicate { - } else if (this.isEmpty()) { - return itemstack.isEmpty(); - } else { -+ // Purpur start -+ if (predicate != null) { -+ return predicate.test(itemstack.asBukkitCopy()); -+ } -+ // Purpur end -+ - ItemStack[] aitemstack = this.getItems(); - int i = aitemstack.length; - -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..553ecc9b5631ffc0848a798bb3295f16d1722c1f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -@@ -30,6 +30,7 @@ public interface CraftRecipe extends Recipe { - } else if (bukkit instanceof RecipeChoice.ExactChoice) { - stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat)))); - stack.exact = true; -+ stack.predicate = ((RecipeChoice.ExactChoice) bukkit).getPredicate(); // Purpur - } else { - throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); - } diff --git a/patches/server/0095-Flying-squids-Oh-my.patch b/patches/server/0095-Flying-squids-Oh-my.patch deleted file mode 100644 index 3bf9ac553..000000000 --- a/patches/server/0095-Flying-squids-Oh-my.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 4 Oct 2020 12:00:42 -0500 -Subject: [PATCH] Flying squids! Oh my! - - -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index 730958dab7f074930cdccb88a89aa26e2b6a112b..b1ba0f24dd6f1ec4c60208564e4eb84bdcd457f4 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -41,6 +41,11 @@ public class GlowSquid extends Squid { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth); - } - -+ @Override -+ public boolean canFly() { -+ return this.level().purpurConfig.glowSquidsCanFly; -+ } -+ - @Override - protected ParticleOptions getInkParticle() { - return ParticleTypes.GLOW_SQUID_INK; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index f68c18b6645981126329b58379946308bbb8ccf8..80bdc93cba675d6c1286618f14fc33e0344c601f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -81,6 +81,15 @@ public class Squid extends WaterAnimal { - return super.getAxisForFluidCheck().offsetY(level().purpurConfig.squidOffsetWaterCheck); - } - -+ public boolean canFly() { -+ return this.level().purpurConfig.squidsCanFly; -+ } -+ -+ @Override -+ public boolean isInWater() { -+ return this.wasTouchingWater || canFly(); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -@@ -153,6 +162,7 @@ public class Squid extends WaterAnimal { - } - - if (this.isInWaterOrBubble()) { -+ if (canFly()) setNoGravity(!wasTouchingWater); // Purpur - if (this.tentacleMovement < (float)Math.PI) { - float f = this.tentacleMovement / (float)Math.PI; - this.tentacleAngle = Mth.sin(f * f * (float)Math.PI) * (float)Math.PI * 0.25F; -@@ -366,7 +376,7 @@ public class Squid extends WaterAnimal { - int i = this.squid.getNoActionTime(); - if (i > 100) { - this.squid.setMovementVector(0.0F, 0.0F, 0.0F); -- } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) { -+ } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.isInWater() || !this.squid.hasMovementVector()) { // Purpur - float f = this.squid.getRandom().nextFloat() * ((float)Math.PI * 2F); - float g = Mth.cos(f) * 0.2F; - float h = -0.1F + this.squid.getRandom().nextFloat() * 0.2F; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6e38987900f04d1aaf7030b3562c8571f12bbbde..bc8ab431df81d52065b401cf9f5cf0d040c79905 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -906,10 +906,12 @@ public class PurpurWorldConfig { - public boolean glowSquidRidable = false; - public boolean glowSquidControllable = true; - public double glowSquidMaxHealth = 10.0D; -+ public boolean glowSquidsCanFly = false; - private void glowSquidSettings() { - glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); - glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); - glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth); -+ glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly); - } - - public boolean goatRidable = false; -@@ -1541,6 +1543,7 @@ public class PurpurWorldConfig { - public double squidMaxHealth = 10.0D; - public boolean squidImmuneToEAR = true; - public double squidOffsetWaterCheck = 0.0D; -+ public boolean squidsCanFly = false; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -1552,6 +1555,7 @@ public class PurpurWorldConfig { - squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth); - squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR); - squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck); -+ squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly); - } - - public boolean spiderRidable = false; diff --git a/patches/server/0096-Infinity-bow-settings.patch b/patches/server/0096-Infinity-bow-settings.patch deleted file mode 100644 index 99f8da06f..000000000 --- a/patches/server/0096-Infinity-bow-settings.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 4 Oct 2020 19:08:53 -0500 -Subject: [PATCH] Infinity bow settings - - -diff --git a/src/main/java/net/minecraft/world/item/BowItem.java b/src/main/java/net/minecraft/world/item/BowItem.java -index 08d597db1a5345a343777a01427655e6bf2c926b..33df0ca406dc8321b76b393f317bbd1c8ebe6366 100644 ---- a/src/main/java/net/minecraft/world/item/BowItem.java -+++ b/src/main/java/net/minecraft/world/item/BowItem.java -@@ -38,7 +38,7 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable { - float f = BowItem.getPowerForTime(j); - - if ((double) f >= 0.1D) { -- boolean flag1 = flag && itemstack1.is(Items.ARROW); -+ boolean flag1 = flag && ((itemstack1.is(Items.ARROW) && world.purpurConfig.infinityWorksWithNormalArrows) || (itemstack1.is(Items.TIPPED_ARROW) && world.purpurConfig.infinityWorksWithTippedArrows) || (itemstack1.is(Items.SPECTRAL_ARROW) && world.purpurConfig.infinityWorksWithSpectralArrows)); // Purpur if (!world.isClientSide) { - - if (!world.isClientSide) { - ArrowItem itemarrow = (ArrowItem) (itemstack1.getItem() instanceof ArrowItem ? itemstack1.getItem() : Items.ARROW); -@@ -132,7 +132,7 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable { - ItemStack itemstack = user.getItemInHand(hand); - boolean flag = !user.getProjectile(itemstack).isEmpty(); - -- if (!user.getAbilities().instabuild && !flag) { -+ if (!(world.purpurConfig.infinityWorksWithoutArrows && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, itemstack) > 0) && !user.getAbilities().instabuild && !flag) { // Purpur - return InteractionResultHolder.fail(itemstack); - } else { - user.startUsingItem(hand); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index bc8ab431df81d52065b401cf9f5cf0d040c79905..ae3beab9a442477973bc8f9ccd47f479d7ddc9ee 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -140,6 +140,17 @@ public class PurpurWorldConfig { - entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); - } - -+ public boolean infinityWorksWithoutArrows = false; -+ public boolean infinityWorksWithNormalArrows = true; -+ public boolean infinityWorksWithSpectralArrows = false; -+ public boolean infinityWorksWithTippedArrows = false; -+ private void infinityArrowsSettings() { -+ infinityWorksWithoutArrows = getBoolean("gameplay-mechanics.infinity-bow.works-without-arrows", infinityWorksWithoutArrows); -+ infinityWorksWithNormalArrows = getBoolean("gameplay-mechanics.infinity-bow.normal-arrows", infinityWorksWithNormalArrows); -+ infinityWorksWithSpectralArrows = getBoolean("gameplay-mechanics.infinity-bow.spectral-arrows", infinityWorksWithSpectralArrows); -+ infinityWorksWithTippedArrows = getBoolean("gameplay-mechanics.infinity-bow.tipped-arrows", infinityWorksWithTippedArrows); -+ } -+ - public List itemImmuneToCactus = new ArrayList<>(); - public List itemImmuneToExplosion = new ArrayList<>(); - public List itemImmuneToFire = new ArrayList<>(); diff --git a/patches/server/0097-Configurable-daylight-cycle.patch b/patches/server/0097-Configurable-daylight-cycle.patch deleted file mode 100644 index e2baec408..000000000 --- a/patches/server/0097-Configurable-daylight-cycle.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 10 Oct 2020 14:29:55 -0500 -Subject: [PATCH] Configurable daylight cycle - - -diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java -index 9ec6145fe04ec64bbee8ec6a837719caebdbc6f5..358d610ad020cada1bb83e393deeeaaec05a2791 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetTimePacket.java -@@ -5,7 +5,7 @@ import net.minecraft.network.protocol.Packet; - - public class ClientboundSetTimePacket implements Packet { - private final long gameTime; -- private final long dayTime; -+ private long dayTime; public void setDayTime(long dayTime) { this.dayTime = dayTime; } // Purpur - - public ClientboundSetTimePacket(long time, long timeOfDay, boolean doDaylightCycle) { - this.gameTime = time; -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 477248c492c326c5214d2ceb669366d7992ee52f..44e01ac1874d07d5348b53bb1af613b01606fef1 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1533,7 +1533,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Mon, 19 Oct 2020 15:14:01 -0500 -Subject: [PATCH] Furnace uses lava from underneath - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -index 448fa4f4f200430d6ce3051763c7ceb697696146..ca2052804ad829a1528a9c5a0a792275beead113 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -@@ -44,6 +44,7 @@ import net.minecraft.world.level.Level; - import net.minecraft.world.level.block.AbstractFurnaceBlock; - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.material.FluidState; - import net.minecraft.world.phys.Vec3; - // CraftBukkit start - import org.bukkit.craftbukkit.block.CraftBlock; -@@ -324,6 +325,21 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit - } - - ItemStack itemstack = (ItemStack) blockEntity.items.get(1); -+ // Purpur start -+ boolean usedLavaFromUnderneath = false; -+ if (world.purpurConfig.furnaceUseLavaFromUnderneath && !blockEntity.isLit() && itemstack.isEmpty() && !blockEntity.items.get(0).isEmpty() && world.getGameTime() % 20 == 0) { -+ BlockPos below = blockEntity.getBlockPos().below(); -+ BlockState belowState = world.getBlockStateIfLoaded(below); -+ if (belowState != null && belowState.is(Blocks.LAVA)) { -+ FluidState fluidState = belowState.getFluidState(); -+ if (fluidState != null && fluidState.isSource()) { -+ world.setBlock(below, Blocks.AIR.defaultBlockState(), 3); -+ itemstack = Items.LAVA_BUCKET.getDefaultInstance(); -+ usedLavaFromUnderneath = true; -+ } -+ } -+ } -+ // Purpur end - boolean flag2 = !((ItemStack) blockEntity.items.get(0)).isEmpty(); - boolean flag3 = !itemstack.isEmpty(); - -@@ -409,6 +425,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit - setChanged(world, pos, state); - } - -+ if (usedLavaFromUnderneath) blockEntity.items.set(1, ItemStack.EMPTY); // Purpur - } - - private static boolean canBurn(RegistryAccess registryManager, @Nullable Recipe recipe, NonNullList slots, int count) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a721eb79dc528ada6a08265bb1f2895c31f0386e..5c478488da15171d70c1b4c01d7ca33698314032 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -413,6 +413,17 @@ public class PurpurWorldConfig { - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); - } - -+ public boolean furnaceUseLavaFromUnderneath = false; -+ private void furnaceSettings() { -+ if (PurpurConfig.version < 17) { -+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath); -+ boolean oldValue = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath); -+ set("blocks.furnace.infinite-fuel", null); -+ set("blocks.furnace.use-lava-from-underneath", oldValue); -+ } -+ furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath); -+ } -+ - public int lavaInfiniteRequiredSources = 2; - public int lavaSpeedNether = 10; - public int lavaSpeedNotNether = 30; diff --git a/patches/server/0100-Arrows-should-not-reset-despawn-counter.patch b/patches/server/0100-Arrows-should-not-reset-despawn-counter.patch deleted file mode 100644 index e0ad5e034..000000000 --- a/patches/server/0100-Arrows-should-not-reset-despawn-counter.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 4 Nov 2020 13:12:50 -0600 -Subject: [PATCH] Arrows should not reset despawn counter - -This prevents keeping arrows alive indefinitely (such as when the block -the arrow is stuck in gets removed, like a piston head going up/down) - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -index fa885337085348308604e50049ecc5bb52023884..e8773aa2e796ca4e3b9b7cf85c40906d16eb241d 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -312,7 +312,7 @@ public abstract class AbstractArrow extends Projectile { - Vec3 vec3d = this.getDeltaMovement(); - - this.setDeltaMovement(vec3d.multiply((double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F))); -- this.life = 0; -+ if (this.level().purpurConfig.arrowMovementResetsDespawnCounter) this.life = 0; // Purpur - do not reset despawn counter - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5c478488da15171d70c1b4c01d7ca33698314032..b996cafed3083c3d9604005a054f0b4511bbf383 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -101,6 +101,11 @@ public class PurpurWorldConfig { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - } - -+ public boolean arrowMovementResetsDespawnCounter = true; -+ private void arrowSettings() { -+ arrowMovementResetsDespawnCounter = getBoolean("gameplay-mechanics.arrow.movement-resets-despawn-counter", arrowMovementResetsDespawnCounter); -+ } -+ - public boolean useBetterMending = false; - public boolean boatEjectPlayersOnLand = false; - public boolean disableDropsOnCrammingDeath = false; diff --git a/patches/server/0101-Ability-to-re-add-farmland-mechanics-from-Alpha.patch b/patches/server/0101-Ability-to-re-add-farmland-mechanics-from-Alpha.patch deleted file mode 100644 index 0386e338a..000000000 --- a/patches/server/0101-Ability-to-re-add-farmland-mechanics-from-Alpha.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Yive -Date: Sat, 14 Nov 2020 08:06:20 -0800 -Subject: [PATCH] Ability to re-add farmland mechanics from Alpha - - -diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -index 1776906e886edd511bfbe96b8a345438a3cd66f8..7efae60a9a9269703a1fa7cd280b5b96557ec73e 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -115,6 +115,14 @@ public class FarmBlock extends Block { - return; - } - -+ // Purpur start -+ if (world.purpurConfig.farmlandAlpha) { -+ Block block = world.getBlockState(pos.below()).getBlock(); -+ if (block instanceof FenceBlock || block instanceof WallBlock) { -+ return; -+ } -+ } -+ // Purpur end - if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { - return; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b996cafed3083c3d9604005a054f0b4511bbf383..8a43f22c9bcd20d2c1efbc18bce827551af77309 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -414,8 +414,10 @@ public class PurpurWorldConfig { - } - - public boolean farmlandGetsMoistFromBelow = false; -+ public boolean farmlandAlpha = false; - private void farmlandSettings() { - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); -+ farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha); - } - - public boolean furnaceUseLavaFromUnderneath = false; diff --git a/patches/server/0102-Add-adjustable-breeding-cooldown-to-config.patch b/patches/server/0102-Add-adjustable-breeding-cooldown-to-config.patch deleted file mode 100644 index 3e36ffd94..000000000 --- a/patches/server/0102-Add-adjustable-breeding-cooldown-to-config.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: montlikadani -Date: Fri, 13 Nov 2020 17:52:40 +0100 -Subject: [PATCH] Add adjustable breeding cooldown to config - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java -index c2f61ed153260692c96af4f20bc5b7d55cbbc380..43f01ce3064fdc0bb46beab6ea001942f1506200 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Animal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java -@@ -154,7 +154,7 @@ public abstract class Animal extends AgeableMob { - if (this.isFood(itemstack)) { - int i = this.getAge(); - -- if (!this.level().isClientSide && i == 0 && this.canFallInLove()) { -+ if (!this.level().isClientSide && i == 0 && this.canFallInLove() && (this.level().purpurConfig.animalBreedingCooldownSeconds <= 0 || !this.level().hasBreedingCooldown(player.getUUID(), this.getClass()))) { // Purpur - this.usePlayerItem(player, hand, itemstack); - this.setInLove(player); - return InteractionResult.SUCCESS; -@@ -236,12 +236,20 @@ public abstract class Animal extends AgeableMob { - AgeableMob entityageable = this.getBreedOffspring(world, other); - - if (entityageable != null) { -- entityageable.setBaby(true); -- entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); -- // CraftBukkit start - call EntityBreedEvent -+ // Purpur start - ServerPlayer breeder = Optional.ofNullable(this.getLoveCause()).or(() -> { - return Optional.ofNullable(other.getLoveCause()); - }).orElse(null); -+ if (breeder != null && world.purpurConfig.animalBreedingCooldownSeconds > 0) { -+ if (world.hasBreedingCooldown(breeder.getUUID(), this.getClass())) { -+ return; -+ } -+ world.addBreedingCooldown(breeder.getUUID(), this.getClass()); -+ } -+ entityageable.setBaby(true); -+ entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); -+ // CraftBukkit start - call EntityBreedEvent -+ // Purpur end - int experience = this.getRandom().nextInt(7) + 1; - EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(entityageable, this, other, breeder, this.breedItem, experience); - if (entityBreedEvent.isCancelled()) { -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index a4c45ee8a5c6e55a6f9abde401cc06f13bd0b018..84ac5db63a51d1490fcf625d10ed0952d671017e 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -194,6 +194,49 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - // Paper end - fix and optimise world upgrading - -+ // Purpur start -+ private com.google.common.cache.Cache playerBreedingCooldowns; -+ -+ private com.google.common.cache.Cache getNewBreedingCooldownCache() { -+ return com.google.common.cache.CacheBuilder.newBuilder().expireAfterWrite(this.purpurConfig.animalBreedingCooldownSeconds, java.util.concurrent.TimeUnit.SECONDS).build(); -+ } -+ -+ public void resetBreedingCooldowns() { -+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); -+ } -+ -+ public boolean hasBreedingCooldown(java.util.UUID player, Class animalType) { // Purpur -+ return this.playerBreedingCooldowns.getIfPresent(new BreedingCooldownPair(player, animalType)) != null; -+ } -+ -+ public void addBreedingCooldown(java.util.UUID player, Class animalType) { -+ this.playerBreedingCooldowns.put(new BreedingCooldownPair(player, animalType), new Object()); -+ } -+ -+ private static final class BreedingCooldownPair { -+ private final java.util.UUID playerUUID; -+ private final Class animalType; -+ -+ public BreedingCooldownPair(java.util.UUID playerUUID, Class animalType) { -+ this.playerUUID = playerUUID; -+ this.animalType = animalType; -+ } -+ -+ @Override -+ public boolean equals(Object o) { -+ if (this == o) return true; -+ if (o == null || getClass() != o.getClass()) return false; -+ BreedingCooldownPair that = (BreedingCooldownPair) o; -+ return playerUUID.equals(that.playerUUID) && animalType.equals(that.animalType); -+ } -+ -+ @Override -+ public int hashCode() { -+ return java.util.Objects.hash(playerUUID, animalType); -+ } -+ } -+ // Purpur end -+ - public CraftWorld getWorld() { - return this.world; - } -@@ -289,6 +332,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur -+ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - this.generator = gen; - this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8a43f22c9bcd20d2c1efbc18bce827551af77309..7bc980fee2727359a9a2ecc66f31f3569cf8491f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -115,6 +115,7 @@ public class PurpurWorldConfig { - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; - public int raidCooldownSeconds = 0; -+ public int animalBreedingCooldownSeconds = 0; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -@@ -125,6 +126,7 @@ public class PurpurWorldConfig { - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); - raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds); -+ animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds); - } - - public int daytimeTicks = 12000; -diff --git a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java -index afdf04f8b22ad0b7c0b41675e44687b49c2f86d6..2621e54879e9ab0029a875f1d09eee67878b90d5 100644 ---- a/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java -+++ b/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java -@@ -49,6 +49,7 @@ public class PurpurCommand extends Command { - PurpurConfig.init((File) console.options.valueOf("purpur-settings")); - for (ServerLevel level : console.getAllLevels()) { - level.purpurConfig.init(); -+ level.resetBreedingCooldowns(); - } - console.server.reloadCount++; - diff --git a/patches/server/0103-Make-entity-breeding-times-configurable.patch b/patches/server/0103-Make-entity-breeding-times-configurable.patch deleted file mode 100644 index 59af9d228..000000000 --- a/patches/server/0103-Make-entity-breeding-times-configurable.patch +++ /dev/null @@ -1,947 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sun, 15 Nov 2020 02:18:15 -0800 -Subject: [PATCH] Make entity breeding times configurable - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java -index 645d204fe2c727637a6a547aad7d4db1279628ae..8be2f612c9e8a64b0aad6bb54450cdfb12b3c19e 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java -@@ -127,8 +127,10 @@ public class VillagerMakeLove extends Behavior { - return Optional.empty(); - } - // Move age setting down -- parent.setAge(6000); -- partner.setAge(6000); -+ // Purpur start -+ parent.setAge(world.purpurConfig.villagerBreedingTicks); -+ partner.setAge(world.purpurConfig.villagerBreedingTicks); -+ // Purpur end - world.addFreshEntityWithPassengers(entityvillager2, CreatureSpawnEvent.SpawnReason.BREEDING); - // CraftBukkit end - world.broadcastEntityEvent(entityvillager2, (byte) 12); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java -index 43f01ce3064fdc0bb46beab6ea001942f1506200..8773b1072016a3bbf025959e9ab827704ec17fc6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Animal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java -@@ -42,6 +42,7 @@ public abstract class Animal extends AgeableMob { - @Nullable - public UUID loveCause; - public ItemStack breedItem; // CraftBukkit - Add breedItem variable -+ public abstract int getPurpurBreedTime(); // Purpur - - protected Animal(EntityType type, Level world) { - super(type, world); -@@ -277,8 +278,10 @@ public abstract class Animal extends AgeableMob { - entityplayer.awardStat(Stats.ANIMALS_BRED); - CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer, this, entityanimal, entityageable); - } // Paper -- this.setAge(6000); -- entityanimal.setAge(6000); -+ // Purpur start -+ this.setAge(this.getPurpurBreedTime()); -+ entityanimal.setAge(entityanimal.getPurpurBreedTime()); -+ // Purpur end - this.resetLove(); - entityanimal.resetLove(); - worldserver.broadcastEntityEvent(this, (byte) 18); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index e7f95639a442cf602fef0ed7aec0f5f96aa6fd92..fd917f72b4b4d4af1ae36feab38deac2657c95d4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -476,6 +476,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.beeMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.beeBreedingTicks; -+ } -+ - @Override - public int getRemainingPersistentAngerTime() { - return (Integer) this.entityData.get(Bee.DATA_REMAINING_ANGER_TIME); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 49a6ac765e71b311c91cbfd4cf9cde2daa729239..ffa2128b63e5e45c2a41e31a6b8b6e8df0bf9be4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -127,6 +127,11 @@ public class Cat extends TamableAnimal implements VariantHolder { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.catMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.catBreedingTicks; -+ } -+ - public ResourceLocation getResourceLocation() { - return this.getVariant().texture(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index 0f49705e3c7adf033cee9d0746319885c830224f..86b910cbb8d0e19dc9ae53078e730495bf4a3b87 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -79,6 +79,11 @@ public class Chicken extends Animal { - } - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.chickenBreedingTicks; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 28c7e240b7dd1957ccb6a45608b1d052a32dc18a..f9cce30eef4a5bffeb27c48a6c7ee361803560c5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -66,6 +66,11 @@ public class Cow extends Animal { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.cowBreedingTicks; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index cedc4dac776dde310dbf1a22272ce54b14ca2c9e..2cf00af66b73e1891fadcabcf83307873cbed710 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -186,6 +186,11 @@ public class Fox extends Animal implements VariantHolder { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.foxMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.foxBreedingTicks; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -@@ -981,8 +986,10 @@ public class Fox extends Animal implements VariantHolder { - CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer2, this.animal, this.partner, entityfox); - } - -- this.animal.setAge(6000); -- this.partner.setAge(6000); -+ // Purpur start -+ this.animal.setAge(this.animal.getPurpurBreedTime()); -+ this.partner.setAge(this.partner.getPurpurBreedTime()); -+ // Purpur end - this.animal.resetLove(); - this.partner.resetLove(); - worldserver.addFreshEntityWithPassengers(entityfox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index b27c0f21354a78025f9d1664c560fb8799bced91..7fd50f60aab2fa49a8227f313352a2ee251904c8 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -85,6 +85,11 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - public void initAttributes() { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.rabbitMaxHealth); - } -+ -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.rabbitBreedingTicks; -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index 3d212b09258b9777079d4bc7ce950f529cdce69e..c3b79c109e895cb4460571c8816b210455126f1d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -139,6 +139,11 @@ public class Sheep extends Animal implements Shearable { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.sheepMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.sheepBreedingTicks; -+ } -+ - @Override - protected void registerGoals() { - this.eatBlockGoal = new EatBlockGoal(this); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index 6ae82b6293f236fc926f411de2badc496b35399b..4a5cc7eaac2bc093a0f5a457b93b6f4560d739ca 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -105,6 +105,11 @@ public class Turtle extends Animal { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.turtleMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.turtleBreedingTicks; -+ } -+ - public void setHomePos(BlockPos pos) { - this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index 1a27e811169a58515c796344ffa1bb3c46d1014a..d8c73365915e67b38292c96a9601f983f121bdf9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -130,6 +130,11 @@ public class Wolf extends TamableAnimal implements NeutralMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wolfMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.wolfBreedingTicks; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index e1c0c122dde56b8dd797d1278340260150025cf9..acf44bf3d22d7b2ac7a02b3167cf577403942908 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -120,6 +120,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { - return this.modelRotationValues; -diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -index 5ec8815cc69f5abe03a29224650edfb153c9f5d0..8073a3a0df7d75a29419303c85d7dadd9f94be99 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -@@ -89,6 +89,10 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider - public boolean dismountsUnderwater() { - return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater; - } -+ -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.camelBreedingTicks; -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index f189e0493286d58fbe16a30912b3a1dc4cd4c3e5..840b61241c44e92b3053157492f4389f46179e81 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -@@ -136,6 +136,10 @@ public class Frog extends Animal implements VariantHolder { - } - // Purpur end - -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.frogBreedingTicks; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index ec956339b02fc9da53563d67c0eeaa91a3c3a8ee..d7580f06f2f619cb283faa61bdd4e7cc127fc08b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -107,6 +107,11 @@ public class Goat extends Animal { - } - // Purpur end - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.goatBreedingTicks; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -index 365273173e37e74470a1cf511334ea8cb25f8302..6932d0eafbeca9de80728c6b0ae42340d2297ad0 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -@@ -37,6 +37,11 @@ public class Donkey extends AbstractChestedHorse { - return generateSpeed(this.level().purpurConfig.donkeyMovementSpeedMin, this.level().purpurConfig.donkeyMovementSpeedMax); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.donkeyBreedingTicks; -+ } -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.DONKEY_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -index 92339f5a07dcb6bf7eb1bce6d584464ebd8430b5..0251ded48eb5bdf96f0e67f6456aa15909c8a4ff 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -@@ -62,6 +62,11 @@ public class Horse extends AbstractHorse implements VariantHolder { - return generateSpeed(this.level().purpurConfig.horseMovementSpeedMin, this.level().purpurConfig.horseMovementSpeedMax); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.horseBreedingTicks; -+ } -+ - @Override - protected void randomizeAttributes(RandomSource random) { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index 72fe2dd69a9ae51e14ec91a83eac2dd74bd76cca..ad9d061979f5856cba9769b91357b3c75cd9a794 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -138,6 +138,11 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 66f2695403a04c2e9540bf2ec290bf1cc5a377ca..bd72dc99752faca3180ecb165b11406ada1a564e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -89,6 +89,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.hoglinMaxHealth); - } - -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.hoglinBreedingTicks; -+ } -+ - @Override - public boolean canBeLeashed(Player player) { - return !this.isLeashed(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7bc980fee2727359a9a2ecc66f31f3569cf8491f..4df745bede2ecc5e9ee4129b980f9edfd8969d4f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -505,10 +505,12 @@ public class PurpurWorldConfig { - public boolean axolotlRidable = false; - public boolean axolotlControllable = true; - public double axolotlMaxHealth = 14.0D; -+ public int axolotlBreedingTicks = 6000; - private void axolotlSettings() { - axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); - axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); - axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth); -+ axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks); - } - - public boolean batRidable = false; -@@ -548,6 +550,7 @@ public class PurpurWorldConfig { - public boolean beeControllable = true; - public double beeMaxY = 320D; - public double beeMaxHealth = 10.0D; -+ public int beeBreedingTicks = 6000; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -559,6 +562,7 @@ public class PurpurWorldConfig { - set("mobs.bee.attributes.max_health", oldValue); - } - beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth); -+ beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks); - } - - public boolean blazeRidable = false; -@@ -586,6 +590,7 @@ public class PurpurWorldConfig { - public double camelJumpStrengthMax = 0.42D; - public double camelMovementSpeedMin = 0.09D; - public double camelMovementSpeedMax = 0.09D; -+ public int camelBreedingTicks = 6000; - private void camelSettings() { - camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); - camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); -@@ -594,6 +599,7 @@ public class PurpurWorldConfig { - camelJumpStrengthMax = getDouble("mobs.camel.attributes.jump_strength.max", camelJumpStrengthMax); - camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin); - camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax); -+ camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks); - } - - public boolean catRidable = false; -@@ -603,6 +609,7 @@ public class PurpurWorldConfig { - public int catSpawnDelay = 1200; - public int catSpawnSwampHutScanRange = 16; - public int catSpawnVillageScanRange = 48; -+ public int catBreedingTicks = 6000; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -616,6 +623,7 @@ public class PurpurWorldConfig { - catSpawnDelay = getInt("mobs.cat.spawn-delay", catSpawnDelay); - catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange); - catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange); -+ catBreedingTicks = getInt("mobs.cat.breeding-delay-ticks", catBreedingTicks); - } - - public boolean caveSpiderRidable = false; -@@ -639,6 +647,7 @@ public class PurpurWorldConfig { - public boolean chickenControllable = true; - public double chickenMaxHealth = 4.0D; - public boolean chickenRetaliate = false; -+ public int chickenBreedingTicks = 6000; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -@@ -650,6 +659,7 @@ public class PurpurWorldConfig { - } - chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth); - chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate); -+ chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks); - } - - public boolean codRidable = false; -@@ -671,6 +681,7 @@ public class PurpurWorldConfig { - public boolean cowControllable = true; - public double cowMaxHealth = 10.0D; - public int cowFeedMushrooms = 0; -+ public int cowBreedingTicks = 6000; - private void cowSettings() { - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); -@@ -682,6 +693,7 @@ public class PurpurWorldConfig { - } - cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth); - cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); -+ cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks); - } - - public boolean creeperRidable = false; -@@ -733,6 +745,7 @@ public class PurpurWorldConfig { - public double donkeyJumpStrengthMax = 0.5D; - public double donkeyMovementSpeedMin = 0.175D; - public double donkeyMovementSpeedMax = 0.175D; -+ public int donkeyBreedingTicks = 6000; - private void donkeySettings() { - donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); - if (PurpurConfig.version < 10) { -@@ -748,6 +761,7 @@ public class PurpurWorldConfig { - donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax); - donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin); - donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax); -+ donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks); - } - - public boolean drownedRidable = false; -@@ -867,6 +881,7 @@ public class PurpurWorldConfig { - public boolean foxControllable = true; - public double foxMaxHealth = 10.0D; - public boolean foxTypeChangesWithTulips = false; -+ public int foxBreedingTicks = 6000; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -878,17 +893,20 @@ public class PurpurWorldConfig { - } - foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth); - foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips); -+ foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks); - } - - public boolean frogRidable = false; - public boolean frogRidableInWater = true; - public boolean frogControllable = true; - public float frogRidableJumpHeight = 0.65F; -+ public int frogBreedingTicks = 6000; - private void frogSettings() { - frogRidable = getBoolean("mobs.frog.ridable", frogRidable); - frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater); - frogControllable = getBoolean("mobs.frog.controllable", frogControllable); - frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight); -+ frogBreedingTicks = getInt("mobs.frog.breeding-delay-ticks", frogBreedingTicks); - } - - public boolean ghastRidable = false; -@@ -956,11 +974,13 @@ public class PurpurWorldConfig { - public boolean goatRidableInWater = true; - public boolean goatControllable = true; - public double goatMaxHealth = 10.0D; -+ public int goatBreedingTicks = 6000; - private void goatSettings() { - goatRidable = getBoolean("mobs.goat.ridable", goatRidable); - goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); - goatControllable = getBoolean("mobs.goat.controllable", goatControllable); - goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth); -+ goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks); - } - - public boolean guardianRidable = false; -@@ -981,6 +1001,7 @@ public class PurpurWorldConfig { - public boolean hoglinRidableInWater = true; - public boolean hoglinControllable = true; - public double hoglinMaxHealth = 40.0D; -+ public int hoglinBreedingTicks = 6000; - private void hoglinSettings() { - hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); - hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); -@@ -991,6 +1012,7 @@ public class PurpurWorldConfig { - set("mobs.hoglin.attributes.max_health", oldValue); - } - hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth); -+ hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks); - } - - public boolean horseRidableInWater = false; -@@ -1000,6 +1022,7 @@ public class PurpurWorldConfig { - public double horseJumpStrengthMax = 1.0D; - public double horseMovementSpeedMin = 0.1125D; - public double horseMovementSpeedMax = 0.3375D; -+ public int horseBreedingTicks = 6000; - private void horseSettings() { - horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1015,6 +1038,7 @@ public class PurpurWorldConfig { - horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax); - horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin); - horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax); -+ horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks); - } - - public boolean huskRidable = false; -@@ -1092,6 +1116,7 @@ public class PurpurWorldConfig { - public double llamaJumpStrengthMax = 0.5D; - public double llamaMovementSpeedMin = 0.175D; - public double llamaMovementSpeedMax = 0.175D; -+ public int llamaBreedingTicks = 6000; - private void llamaSettings() { - llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); - llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); -@@ -1109,6 +1134,7 @@ public class PurpurWorldConfig { - llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax); - llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin); - llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax); -+ llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks); - } - - public boolean magmaCubeRidable = false; -@@ -1137,6 +1163,7 @@ public class PurpurWorldConfig { - public boolean mooshroomRidableInWater = true; - public boolean mooshroomControllable = true; - public double mooshroomMaxHealth = 10.0D; -+ public int mooshroomBreedingTicks = 6000; - private void mooshroomSettings() { - mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); - mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); -@@ -1147,6 +1174,7 @@ public class PurpurWorldConfig { - set("mobs.mooshroom.attributes.max_health", oldValue); - } - mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth); -+ mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks); - } - - public boolean muleRidableInWater = false; -@@ -1156,6 +1184,7 @@ public class PurpurWorldConfig { - public double muleJumpStrengthMax = 0.5D; - public double muleMovementSpeedMin = 0.175D; - public double muleMovementSpeedMax = 0.175D; -+ public int muleBreedingTicks = 6000; - private void muleSettings() { - muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1171,12 +1200,14 @@ public class PurpurWorldConfig { - muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax); - muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin); - muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax); -+ muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks); - } - - public boolean ocelotRidable = false; - public boolean ocelotRidableInWater = true; - public boolean ocelotControllable = true; - public double ocelotMaxHealth = 10.0D; -+ public int ocelotBreedingTicks = 6000; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -@@ -1187,12 +1218,14 @@ public class PurpurWorldConfig { - set("mobs.ocelot.attributes.max_health", oldValue); - } - ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth); -+ ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks); - } - - public boolean pandaRidable = false; - public boolean pandaRidableInWater = true; - public boolean pandaControllable = true; - public double pandaMaxHealth = 20.0D; -+ public int pandaBreedingTicks = 6000; - private void pandaSettings() { - pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); - pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); -@@ -1203,6 +1236,7 @@ public class PurpurWorldConfig { - set("mobs.panda.attributes.max_health", oldValue); - } - pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth); -+ pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks); - } - - public boolean parrotRidable = false; -@@ -1286,6 +1320,7 @@ public class PurpurWorldConfig { - public boolean pigControllable = true; - public double pigMaxHealth = 10.0D; - public boolean pigGiveSaddleBack = false; -+ public int pigBreedingTicks = 6000; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -@@ -1297,6 +1332,7 @@ public class PurpurWorldConfig { - } - pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth); - pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack); -+ pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks); - } - - public boolean piglinRidable = false; -@@ -1353,6 +1389,7 @@ public class PurpurWorldConfig { - public double polarBearMaxHealth = 30.0D; - public String polarBearBreedableItemString = ""; - public Item polarBearBreedableItem = null; -+ public int polarBearBreedingTicks = 6000; - private void polarBearSettings() { - polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); - polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); -@@ -1366,6 +1403,7 @@ public class PurpurWorldConfig { - polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString); - Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(polarBearBreedableItemString)); - if (item != Items.AIR) polarBearBreedableItem = item; -+ polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks); - } - - public boolean pufferfishRidable = false; -@@ -1388,6 +1426,7 @@ public class PurpurWorldConfig { - public double rabbitMaxHealth = 3.0D; - public double rabbitNaturalToast = 0.0D; - public double rabbitNaturalKiller = 0.0D; -+ public int rabbitBreedingTicks = 6000; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -@@ -1400,6 +1439,7 @@ public class PurpurWorldConfig { - rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth); - rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast); - rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller); -+ rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks); - } - - public boolean ravagerRidable = false; -@@ -1436,6 +1476,7 @@ public class PurpurWorldConfig { - public boolean sheepRidableInWater = true; - public boolean sheepControllable = true; - public double sheepMaxHealth = 8.0D; -+ public int sheepBreedingTicks = 6000; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -1446,6 +1487,7 @@ public class PurpurWorldConfig { - set("mobs.sheep.attributes.max_health", oldValue); - } - sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth); -+ sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks); - } - - public boolean shulkerRidable = false; -@@ -1569,11 +1611,13 @@ public class PurpurWorldConfig { - public boolean snifferRidableInWater = true; - public boolean snifferControllable = true; - public double snifferMaxHealth = 14.0D; -+ public int snifferBreedingTicks = 6000; - private void snifferSettings() { - snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); - snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); - snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable); - snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth); -+ snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", chickenBreedingTicks); - } - - public boolean squidRidable = false; -@@ -1632,6 +1676,7 @@ public class PurpurWorldConfig { - public boolean striderRidableInWater = false; - public boolean striderControllable = true; - public double striderMaxHealth = 20.0D; -+ public int striderBreedingTicks = 6000; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -@@ -1642,6 +1687,7 @@ public class PurpurWorldConfig { - set("mobs.strider.attributes.max_health", oldValue); - } - striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth); -+ striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks); - } - - public boolean tadpoleRidable = false; -@@ -1662,6 +1708,7 @@ public class PurpurWorldConfig { - public double traderLlamaJumpStrengthMax = 0.5D; - public double traderLlamaMovementSpeedMin = 0.175D; - public double traderLlamaMovementSpeedMax = 0.175D; -+ public int traderLlamaBreedingTicks = 6000; - private void traderLlamaSettings() { - traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); - traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); -@@ -1679,6 +1726,7 @@ public class PurpurWorldConfig { - traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax); - traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin); - traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax); -+ traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks); - } - - public boolean tropicalFishRidable = false; -@@ -1699,6 +1747,7 @@ public class PurpurWorldConfig { - public boolean turtleRidableInWater = true; - public boolean turtleControllable = true; - public double turtleMaxHealth = 30.0D; -+ public int turtleBreedingTicks = 6000; - private void turtleSettings() { - turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); - turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); -@@ -1709,6 +1758,7 @@ public class PurpurWorldConfig { - set("mobs.turtle.attributes.max_health", oldValue); - } - turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth); -+ turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks); - } - - public boolean vexRidable = false; -@@ -1736,6 +1786,7 @@ public class PurpurWorldConfig { - public boolean villagerFollowEmeraldBlock = false; - public boolean villagerCanBeLeashed = false; - public boolean villagerCanBreed = true; -+ public int villagerBreedingTicks = 6000; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1749,6 +1800,7 @@ public class PurpurWorldConfig { - villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock); - villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed); - villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed); -+ villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks); - } - - public boolean vindicatorRidable = false; -@@ -1860,6 +1912,7 @@ public class PurpurWorldConfig { - public boolean wolfRidableInWater = true; - public boolean wolfControllable = true; - public double wolfMaxHealth = 8.0D; -+ public int wolfBreedingTicks = 6000; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); - wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); -@@ -1870,6 +1923,7 @@ public class PurpurWorldConfig { - set("mobs.wolf.attributes.max_health", oldValue); - } - wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); -+ wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); - } - - public boolean zoglinRidable = false; diff --git a/patches/server/0104-Apply-display-names-from-item-forms-of-entities-to-e.patch b/patches/server/0104-Apply-display-names-from-item-forms-of-entities-to-e.patch deleted file mode 100644 index ec38e606e..000000000 --- a/patches/server/0104-Apply-display-names-from-item-forms-of-entities-to-e.patch +++ /dev/null @@ -1,168 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Tue, 17 Nov 2020 03:23:48 -0800 -Subject: [PATCH] Apply display names from item forms of entities to entities - and vice versa - - -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -index 5524a69952130ec38e151509ba7733459146d1b0..76222084727c269d376d0df5702204c0bdf59d81 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -606,7 +606,7 @@ public class ArmorStand extends LivingEntity { - private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(DamageSource damageSource) { // Paper - ItemStack itemstack = new ItemStack(Items.ARMOR_STAND); - -- if (this.hasCustomName()) { -+ if (this.level().purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) { // Purpur - itemstack.setHoverName(this.getCustomName()); - } - -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -index 759ecd79534a7706f7d4a63eb9dacbefcfe54674..182faba889dc15a3500c5919cad8a5483a53033a 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -@@ -273,7 +273,13 @@ public class ItemFrame extends HangingEntity { - } - - if (alwaysDrop) { -- this.spawnAtLocation(this.getFrameItemStack()); -+ // Purpur start -+ final ItemStack itemFrame = this.getFrameItemStack(); -+ if (this.level().purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) { -+ itemFrame.setHoverName(this.getCustomName()); -+ } -+ this.spawnAtLocation(itemFrame); -+ // Purpur end - } - - if (!itemstack.isEmpty()) { -diff --git a/src/main/java/net/minecraft/world/entity/decoration/Painting.java b/src/main/java/net/minecraft/world/entity/decoration/Painting.java -index d5784a19cec98eb199a51acd9e1f4de8c6bf7265..4b3a8e4a084585d56dd10a08405463b1a677368b 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/Painting.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/Painting.java -@@ -159,7 +159,13 @@ public class Painting extends HangingEntity implements VariantHolder { - } - - protected void destroy(DamageSource source) { -- this.spawnAtLocation((ItemLike) this.getDropItem()); -+ // Purpur start -+ final ItemStack boat = new ItemStack(this.getDropItem()); -+ if (this.level().purpurConfig.persistentDroppableEntityDisplayNames && this.hasCustomName()) { -+ boat.setHoverName(this.getCustomName()); -+ } -+ this.spawnAtLocation(boat); -+ // Purpur end - } - - @Override -diff --git a/src/main/java/net/minecraft/world/item/ArmorStandItem.java b/src/main/java/net/minecraft/world/item/ArmorStandItem.java -index 7cffc64573008502bdd14ae4906fe51166b12fb3..1feafdbb48cf760cb6ebf95d5be2c32bdb1ad44f 100644 ---- a/src/main/java/net/minecraft/world/item/ArmorStandItem.java -+++ b/src/main/java/net/minecraft/world/item/ArmorStandItem.java -@@ -58,6 +58,14 @@ public class ArmorStandItem extends Item { - return InteractionResult.FAIL; - } - // CraftBukkit end -+ // Purpur start -+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) { -+ entityarmorstand.setCustomName(itemstack.getHoverName()); -+ if (world.purpurConfig.armorstandSetNameVisible) { -+ entityarmorstand.setCustomNameVisible(true); -+ } -+ } -+ // Purpur end - worldserver.addFreshEntityWithPassengers(entityarmorstand); - world.playSound((Player) null, entityarmorstand.getX(), entityarmorstand.getY(), entityarmorstand.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F); - entityarmorstand.gameEvent(GameEvent.ENTITY_PLACE, context.getPlayer()); -diff --git a/src/main/java/net/minecraft/world/item/BoatItem.java b/src/main/java/net/minecraft/world/item/BoatItem.java -index aec7ac31a35b1cc81f40b3fbeb5cf95c0f2c8a6c..cbcd35e60a2c344c83978abf0b94c2120ff53dee 100644 ---- a/src/main/java/net/minecraft/world/item/BoatItem.java -+++ b/src/main/java/net/minecraft/world/item/BoatItem.java -@@ -69,6 +69,11 @@ public class BoatItem extends Item { - - entityboat.setVariant(this.type); - entityboat.setYRot(user.getYRot()); -+ // Purpur start -+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) { -+ entityboat.setCustomName(itemstack.getHoverName()); -+ } -+ // Purpur end - if (!world.noCollision(entityboat, entityboat.getBoundingBox())) { - return InteractionResultHolder.fail(itemstack); - } else { -diff --git a/src/main/java/net/minecraft/world/item/HangingEntityItem.java b/src/main/java/net/minecraft/world/item/HangingEntityItem.java -index b2ad6d230de2c29f371178bccde1111c7532ee70..6667926519a0f1c151e53f59cce36e7417dfc1cd 100644 ---- a/src/main/java/net/minecraft/world/item/HangingEntityItem.java -+++ b/src/main/java/net/minecraft/world/item/HangingEntityItem.java -@@ -48,7 +48,7 @@ public class HangingEntityItem extends Item { - return InteractionResult.FAIL; - } else { - Level world = context.getLevel(); -- Object object; -+ Entity object; // Purpur - - if (this.type == EntityType.PAINTING) { - Optional optional = Painting.create(world, blockposition1, enumdirection); -@@ -72,6 +72,11 @@ public class HangingEntityItem extends Item { - - if (nbttagcompound != null) { - EntityType.updateCustomEntityTag(world, entityhuman, (Entity) object, nbttagcompound); -+ // Purpur start -+ if (world.purpurConfig.persistentDroppableEntityDisplayNames && itemstack.hasCustomHoverName()) { -+ object.setCustomName(itemstack.getHoverName()); -+ } -+ // Purpur end - } - - if (((HangingEntity) object).survives()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 4df745bede2ecc5e9ee4129b980f9edfd8969d4f..f04861a5ca426b1c53b6d3ec4d3514bcaa9789a2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -97,8 +97,10 @@ public class PurpurWorldConfig { - } - - public float armorstandStepHeight = 0.0F; -+ public boolean armorstandSetNameVisible = true; - private void armorstandSettings() { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); -+ armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible); - } - - public boolean arrowMovementResetsDespawnCounter = true; -@@ -111,6 +113,7 @@ public class PurpurWorldConfig { - public boolean disableDropsOnCrammingDeath = false; - public boolean entitiesCanUsePortals = true; - public boolean milkCuresBadOmen = true; -+ public boolean persistentDroppableEntityDisplayNames = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; -@@ -122,6 +125,7 @@ public class PurpurWorldConfig { - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); -+ persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); diff --git a/patches/server/0105-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch b/patches/server/0105-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch deleted file mode 100644 index 908213606..000000000 --- a/patches/server/0105-Set-name-visible-when-using-a-Name-Tag-on-an-Armor-S.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Tue, 17 Nov 2020 13:12:09 -0800 -Subject: [PATCH] Set name visible when using a Name Tag on an Armor Stand - - -diff --git a/src/main/java/net/minecraft/world/item/NameTagItem.java b/src/main/java/net/minecraft/world/item/NameTagItem.java -index 2941c16ef486345b57ab2dfcd26f0272285d3b5a..7cc6812bf6f2ba015f65fd1fc1eaac02dd0f53e2 100644 ---- a/src/main/java/net/minecraft/world/item/NameTagItem.java -+++ b/src/main/java/net/minecraft/world/item/NameTagItem.java -@@ -20,6 +20,7 @@ public class NameTagItem extends Item { - if (!event.callEvent()) return InteractionResult.PASS; - LivingEntity newEntityLiving = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle(); - newEntityLiving.setCustomName(event.getName() != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(event.getName()) : null); -+ if (user.level().purpurConfig.armorstandFixNametags && entity instanceof net.minecraft.world.entity.decoration.ArmorStand) entity.setCustomNameVisible(true); // Purpur - if (event.isPersistent() && newEntityLiving instanceof Mob) { - ((Mob) newEntityLiving).setPersistenceRequired(); - // Paper end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f04861a5ca426b1c53b6d3ec4d3514bcaa9789a2..db678a5ee139920d51ba2fb179f1d01feab3c973 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -98,9 +98,11 @@ public class PurpurWorldConfig { - - public float armorstandStepHeight = 0.0F; - public boolean armorstandSetNameVisible = true; -+ public boolean armorstandFixNametags = false; - private void armorstandSettings() { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible); -+ armorstandFixNametags = getBoolean("gameplay-mechanics.armorstand.fix-nametags", armorstandFixNametags); - } - - public boolean arrowMovementResetsDespawnCounter = true; diff --git a/patches/server/0106-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch b/patches/server/0106-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch deleted file mode 100644 index 737d10cbf..000000000 --- a/patches/server/0106-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sun, 22 Nov 2020 22:17:53 -0800 -Subject: [PATCH] Add config for allowing Endermen to despawn even while - holding a block - -This should help to reduce the amount of dirt, gravel, grass, and etc. -that Endermen like to randomly place all over the world. - -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 3861f367ac01028139cc6db35fb9a1e20cb39457..f49cbb9fcd13afc10aa76ac80128162a902ae345 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -494,7 +494,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean requiresCustomPersistence() { -- return super.requiresCustomPersistence() || this.getCarriedBlock() != null; -+ return super.requiresCustomPersistence() || (!this.level().purpurConfig.endermanDespawnEvenWithBlock && this.getCarriedBlock() != null); // Purpur - } - - private static class EndermanFreezeWhenLookedAt extends Goal { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index db678a5ee139920d51ba2fb179f1d01feab3c973..6fdef95d12988207bb682ba165e135a94e505a58 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -837,6 +837,7 @@ public class PurpurWorldConfig { - public boolean endermanControllable = true; - public double endermanMaxHealth = 40.0D; - public boolean endermanAllowGriefing = true; -+ public boolean endermanDespawnEvenWithBlock = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -848,6 +849,7 @@ public class PurpurWorldConfig { - } - endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); - endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); -+ endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0107-Add-configurable-snowball-damage.patch b/patches/server/0107-Add-configurable-snowball-damage.patch deleted file mode 100644 index 333da4d36..000000000 --- a/patches/server/0107-Add-configurable-snowball-damage.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 24 Nov 2020 05:32:02 -0600 -Subject: [PATCH] Add configurable snowball damage - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -index 718e120c9768cf716b32d3d652f53f1dda925168..b90cedad282e95a067aca176fafa9f72a726f520 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -@@ -53,7 +53,7 @@ public class Snowball extends ThrowableItemProjectile { - protected void onHitEntity(EntityHitResult entityHitResult) { - super.onHitEntity(entityHitResult); - Entity entity = entityHitResult.getEntity(); -- int i = entity instanceof Blaze ? 3 : 0; -+ int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur - entity.hurt(this.damageSources().thrown(this, this.getOwner()), (float)i); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6fdef95d12988207bb682ba165e135a94e505a58..e9778b0e02fb94eeea2e8c6e2680b6564b11d399 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -354,6 +354,11 @@ public class PurpurWorldConfig { - //} - } - -+ public int snowballDamage = -1; -+ private void snowballSettings() { -+ snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage); -+ } -+ - public boolean silkTouchEnabled = false; - public String silkTouchSpawnerName = "Monster Spawner"; - public List silkTouchSpawnerLore = new ArrayList<>(); diff --git a/patches/server/0108-Changeable-Mob-Left-Handed-Chance.patch b/patches/server/0108-Changeable-Mob-Left-Handed-Chance.patch deleted file mode 100644 index d911c21a7..000000000 --- a/patches/server/0108-Changeable-Mob-Left-Handed-Chance.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Mon, 30 Nov 2020 11:40:11 -0500 -Subject: [PATCH] Changeable Mob Left Handed Chance - - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 64a682ea339809adfb8304a7f14d4a596e5332be..4e5a261ccb7fb63b82ac7194598729fd60a20840 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1295,7 +1295,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - RandomSource randomsource = world.getRandom(); - - this.getAttribute(Attributes.FOLLOW_RANGE).addPermanentModifier(new AttributeModifier("Random spawn bonus", randomsource.triangle(0.0D, 0.11485000000000001D), AttributeModifier.Operation.MULTIPLY_BASE)); -- if (randomsource.nextFloat() < 0.05F) { -+ if (randomsource.nextFloat() < world.getLevel().purpurConfig.entityLeftHandedChance) { // Purpur - this.setLeftHanded(true); - } else { - this.setLeftHanded(false); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e9778b0e02fb94eeea2e8c6e2680b6564b11d399..4968d157692e71c54be0fd76069ece3ba0294e5f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -156,8 +156,10 @@ public class PurpurWorldConfig { - } - - public int entityLifeSpan = 0; -+ public float entityLeftHandedChance = 0.05f; - private void entitySettings() { - entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); -+ entityLeftHandedChance = (float) getDouble("gameplay-mechanics.entity-left-handed-chance", entityLeftHandedChance); - } - - public boolean infinityWorksWithoutArrows = false; diff --git a/patches/server/0109-Add-boat-fall-damage-config.patch b/patches/server/0109-Add-boat-fall-damage-config.patch deleted file mode 100644 index a0f47bf0d..000000000 --- a/patches/server/0109-Add-boat-fall-damage-config.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 30 Nov 2020 19:36:35 -0600 -Subject: [PATCH] Add boat fall damage config - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 8ed1a18cd1b33e1776dbf3b2294ab633f243daae..8419775b8c2c6c8d246d0011d81f540cd09cb403 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1113,7 +1113,16 @@ public class ServerPlayer extends Player { - if (this.isInvulnerableTo(source)) { - return false; - } else { -- if (source.is(DamageTypeTags.IS_FALL) && getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) return false; // Purpur -+ // Purpur start -+ if (source.is(DamageTypeTags.IS_FALL)) { // Purpur -+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) { -+ return false; -+ } -+ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.Boat && !level().purpurConfig.boatsDoFallDamage) { -+ return false; -+ } -+ } -+ // Purpur end - boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL); - - if (!flag && isSpawnInvulnerable() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { // Purpur -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 4968d157692e71c54be0fd76069ece3ba0294e5f..e3dbb3d81494ecdcedd06e7e16cc17a2d4e15112 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -112,6 +112,7 @@ public class PurpurWorldConfig { - - public boolean useBetterMending = false; - public boolean boatEjectPlayersOnLand = false; -+ public boolean boatsDoFallDamage = false; - public boolean disableDropsOnCrammingDeath = false; - public boolean entitiesCanUsePortals = true; - public boolean milkCuresBadOmen = true; -@@ -124,6 +125,7 @@ public class PurpurWorldConfig { - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -+ boatsDoFallDamage = getBoolean("gameplay-mechanics.boat.do-fall-damage", boatsDoFallDamage); - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); diff --git a/patches/server/0110-Snow-Golem-rate-of-fire-config.patch b/patches/server/0110-Snow-Golem-rate-of-fire-config.patch deleted file mode 100644 index 4c0235efc..000000000 --- a/patches/server/0110-Snow-Golem-rate-of-fire-config.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Simon Gardling -Date: Tue, 1 Dec 2020 16:50:36 -0500 -Subject: [PATCH] Snow Golem rate of fire config - -The formula used to determine the amount of ticks between shots is: - ((sqrt(distanceToTarget) / snowGolemAttackDistance) / snowGolemSnowBallModifer) * (maxShootIntervalTicks - minShootIntervalTicks) + minShootIntervalTicks - -If min-shoot-interval-ticks and max-shoot-interval-ticks are both set to -0, snow golems won't shoot any snowballs. - -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index bd9dd01e9140c2ad0ab9859b9a455f73ab04a253..d3da7487f911ff791dca5f7b546dccda751fe44c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -79,7 +79,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -- this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25D, 20, 10.0F)); -+ this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur - this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F)); - this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e3dbb3d81494ecdcedd06e7e16cc17a2d4e15112..7518f375450448b2f5aa71a999a2b213f78b7583 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1609,6 +1609,10 @@ public class PurpurWorldConfig { - public double snowGolemMaxHealth = 4.0D; - public boolean snowGolemDropsPumpkin = true; - public boolean snowGolemPutPumpkinBack = false; -+ public int snowGolemSnowBallMin = 20; -+ public int snowGolemSnowBallMax = 20; -+ public float snowGolemSnowBallModifier = 10.0F; -+ public double snowGolemAttackDistance = 1.25D; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1622,6 +1626,10 @@ public class PurpurWorldConfig { - snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth); - snowGolemDropsPumpkin = getBoolean("mobs.snow_golem.drop-pumpkin-when-sheared", snowGolemDropsPumpkin); - snowGolemPutPumpkinBack = getBoolean("mobs.snow_golem.pumpkin-can-be-added-back", snowGolemPutPumpkinBack); -+ snowGolemSnowBallMin = getInt("mobs.snow_golem.min-shoot-interval-ticks", snowGolemSnowBallMin); -+ snowGolemSnowBallMax = getInt("mobs.snow_golem.max-shoot-interval-ticks", snowGolemSnowBallMax); -+ snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier); -+ snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance); - } - - public boolean snifferRidable = false; diff --git a/patches/server/0111-EMC-Configurable-disable-give-dropping.patch b/patches/server/0111-EMC-Configurable-disable-give-dropping.patch deleted file mode 100644 index f9d7076a4..000000000 --- a/patches/server/0111-EMC-Configurable-disable-give-dropping.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Thu, 14 Jan 2016 00:49:14 -0500 -Subject: [PATCH] EMC - Configurable disable give dropping - -Modified version of a patch by Aikar from EMC. Adds a config option in -purpur.yml to disable the /give command from dropping items on the -floor when a player's inventory is full. - -diff --git a/src/main/java/net/minecraft/server/commands/GiveCommand.java b/src/main/java/net/minecraft/server/commands/GiveCommand.java -index d601d287e94a59ff93b8a83a44dac02544d211df..0ff3b06a98b2f4514b2d861b92dd70fe678ae86c 100644 ---- a/src/main/java/net/minecraft/server/commands/GiveCommand.java -+++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java -@@ -59,6 +59,7 @@ public class GiveCommand { - boolean flag = entityplayer.getInventory().add(itemstack1); - ItemEntity entityitem; - -+ if (org.purpurmc.purpur.PurpurConfig.disableGiveCommandDrops) continue; // Purpur - add config option for toggling give command dropping - if (flag && itemstack1.isEmpty()) { - itemstack1.setCount(1); - entityitem = entityplayer.drop(itemstack1, false, false, false); // SPIGOT-2942: Add boolean to call event -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index de1a9b08946005d1235264c7658b51bbdc2c47c2..aaf96399f937fbca30f1ce5d64be4af265a4ed29 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -209,6 +209,11 @@ public class PurpurConfig { - useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); - } - -+ public static boolean disableGiveCommandDrops = false; -+ private static void disableGiveCommandDrops() { -+ disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops); -+ } -+ - public static int barrelRows = 3; - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; diff --git a/patches/server/0112-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch b/patches/server/0112-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch deleted file mode 100644 index 71c3c6a1e..000000000 --- a/patches/server/0112-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch +++ /dev/null @@ -1,197 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sat, 5 Dec 2020 01:20:16 -0800 -Subject: [PATCH] Option for Villager Clerics to farm Nether Wart - -Adds an option so that Villagers with the Cleric profession are able to -farm Nether Wart. Reimplemented based on a feature of the carpet-extra -mod. - -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -index d3a2a6dee2d83b3df0ddc521c080f7d72b709461..66a568e6803b91040f3933d4598c52fa518badab 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -@@ -40,6 +40,7 @@ public class HarvestFarmland extends Behavior { - private long nextOkStartTime; - private int timeWorkedSoFar; - private final List validFarmlandAroundVillager = Lists.newArrayList(); -+ private boolean clericWartFarmer = false; // Purpur - - public HarvestFarmland() { - super(ImmutableMap.of(MemoryModuleType.LOOK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.SECONDARY_JOB_SITE, MemoryStatus.VALUE_PRESENT)); -@@ -48,9 +49,10 @@ public class HarvestFarmland extends Behavior { - protected boolean checkExtraStartConditions(ServerLevel world, Villager entity) { - if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { - return false; -- } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER) { -+ } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER && !(world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC)) { // Purpur - return false; - } else { -+ if (!this.clericWartFarmer && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC) this.clericWartFarmer = true; // Purpur - BlockPos.MutableBlockPos blockposition_mutableblockposition = entity.blockPosition().mutable(); - - this.validFarmlandAroundVillager.clear(); -@@ -81,6 +83,7 @@ public class HarvestFarmland extends Behavior { - Block block = iblockdata.getBlock(); - Block block1 = world.getBlockState(pos.below()).getBlock(); - -+ if (this.clericWartFarmer) return block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3 || iblockdata.isAir() && block1 == Blocks.SOUL_SAND; // Purpur - return block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) || iblockdata.isAir() && block1 instanceof FarmBlock; - } - -@@ -106,20 +109,20 @@ public class HarvestFarmland extends Behavior { - Block block = iblockdata.getBlock(); - Block block1 = world.getBlockState(this.aboveFarmlandPos.below()).getBlock(); - -- if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata)) { -+ if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata) && !this.clericWartFarmer || this.clericWartFarmer && block == Blocks.NETHER_WART && iblockdata.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3) { // Purpur - if (CraftEventFactory.callEntityChangeBlockEvent(entity, this.aboveFarmlandPos, iblockdata.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state - world.destroyBlock(this.aboveFarmlandPos, true, entity); - } // CraftBukkit - } - -- if (iblockdata.isAir() && block1 instanceof FarmBlock && entity.hasFarmSeeds()) { -+ if (iblockdata.isAir() && (block1 instanceof FarmBlock && !this.clericWartFarmer || this.clericWartFarmer && block1 == Blocks.SOUL_SAND) && entity.hasFarmSeeds()) { // Purpur - SimpleContainer inventorysubcontainer = entity.getInventory(); - - for (int j = 0; j < inventorysubcontainer.getContainerSize(); ++j) { - ItemStack itemstack = inventorysubcontainer.getItem(j); - boolean flag = false; - -- if (!itemstack.isEmpty() && itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)) { -+ if (!itemstack.isEmpty() && (itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) || this.clericWartFarmer && itemstack.getItem() == net.minecraft.world.item.Items.NETHER_WART)) { - Item item = itemstack.getItem(); - - if (item instanceof BlockItem) { -@@ -135,7 +138,7 @@ public class HarvestFarmland extends Behavior { - } - - if (flag) { -- world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F); -+ world.playSound((Player) null, (double) this.aboveFarmlandPos.getX(), (double) this.aboveFarmlandPos.getY(), (double) this.aboveFarmlandPos.getZ(), this.clericWartFarmer ? SoundEvents.NETHER_WART_PLANTED : SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - itemstack.shrink(1); - if (itemstack.isEmpty()) { - inventorysubcontainer.setItem(j, ItemStack.EMPTY); -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java -index 3af715e2f3f3949af614a8fcebbc4a835d48ca49..ade1e411ea1f3b4c9a417265e77b0d6861b222f9 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java -@@ -56,6 +56,12 @@ public class TradeWithVillager extends Behavior { - throwHalfStack(entity, ImmutableSet.of(Items.WHEAT), villager); - } - -+ // Purpur start -+ if (world.purpurConfig.villagerClericsFarmWarts && world.purpurConfig.villagerClericFarmersThrowWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC && entity.getInventory().countItem(Items.NETHER_WART) > Items.NETHER_WART.getMaxStackSize() / 2) { -+ throwHalfStack(entity, ImmutableSet.of(Items.NETHER_WART), villager); -+ } -+ // Purpur end -+ - if (!this.trades.isEmpty() && entity.getInventory().hasAnyOf(this.trades)) { - throwHalfStack(entity, this.trades, villager); - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java -index cd7a90ec1073b2b452ca70decefe6a594445003b..47672e48c1cae73cffe532d622b296343fc12ef0 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java -@@ -30,8 +30,13 @@ public class VillagerGoalPackages { - } - - public static ImmutableList>> getWorkPackage(VillagerProfession profession, float speed) { -+ // Purpur start -+ return getWorkPackage(profession, speed, false); -+ } -+ public static ImmutableList>> getWorkPackage(VillagerProfession profession, float speed, boolean clericsFarmWarts) { -+ // Purpur end - WorkAtPoi workAtPoi; -- if (profession == VillagerProfession.FARMER) { -+ if (profession == VillagerProfession.FARMER || (clericsFarmWarts && profession == VillagerProfession.CLERIC)) { // Purpur - workAtPoi = new WorkAtComposter(); - } else { - workAtPoi = new WorkAtPoi(); -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -index cb1d91f9fe98f21c2afbe3894dfd9bca3bdd3ba6..d2703432af207c74ea8d298a784329c3219d2f13 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -@@ -22,6 +22,13 @@ public class SecondaryPoiSensor extends Sensor { - - @Override - protected void doTick(ServerLevel world, Villager entity) { -+ // Purpur start - make sure clerics don't wander to soul sand when the option is off -+ Brain brain = entity.getBrain(); -+ if (!world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == net.minecraft.world.entity.npc.VillagerProfession.CLERIC) { -+ brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); -+ return; -+ } -+ // Purpur end - ResourceKey resourceKey = world.dimension(); - BlockPos blockPos = entity.blockPosition(); - List list = Lists.newArrayList(); -@@ -38,7 +45,7 @@ public class SecondaryPoiSensor extends Sensor { - } - } - -- Brain brain = entity.getBrain(); -+ //Brain brain = entity.getBrain(); // Purpur - moved up - if (!list.isEmpty()) { - brain.setMemory(MemoryModuleType.SECONDARY_JOB_SITE, list); - } else { -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 7d13a5308161d4093023b732bed7d06152f54737..605171aedb4f54dcd16837ad1e36097653a26ab6 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -223,7 +223,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - brain.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(0.5F)); - } else { - brain.setSchedule(Schedule.VILLAGER_DEFAULT); -- brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))); -+ brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F, this.level().purpurConfig.villagerClericsFarmWarts), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))); // Purpur - } - - brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerprofession, 0.5F)); -@@ -978,6 +978,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - - public boolean hasFarmSeeds() { - return this.getInventory().hasAnyMatching((itemstack) -> { -+ // Purpur start -+ if (this.level().purpurConfig.villagerClericsFarmWarts && this.getVillagerData().getProfession() == VillagerProfession.CLERIC) { -+ return itemstack.is(Items.NETHER_WART); -+ } -+ // Purpur end - return itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS); - }); - } -diff --git a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java -index ac70c2c03241e73943bd517a8c69dd05e0873634..0318663a824d2a9515f867a075d148c3fcb1a907 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java -+++ b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java -@@ -26,7 +26,7 @@ public record VillagerProfession(String name, Predicate> heldJob - public static final VillagerProfession ARMORER = register("armorer", PoiTypes.ARMORER, SoundEvents.VILLAGER_WORK_ARMORER); - public static final VillagerProfession BUTCHER = register("butcher", PoiTypes.BUTCHER, SoundEvents.VILLAGER_WORK_BUTCHER); - public static final VillagerProfession CARTOGRAPHER = register("cartographer", PoiTypes.CARTOGRAPHER, SoundEvents.VILLAGER_WORK_CARTOGRAPHER); -- public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, SoundEvents.VILLAGER_WORK_CLERIC); -+ public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, ImmutableSet.of(Items.NETHER_WART), ImmutableSet.of(Blocks.SOUL_SAND), SoundEvents.VILLAGER_WORK_CLERIC); // Purpur - public static final VillagerProfession FARMER = register("farmer", PoiTypes.FARMER, ImmutableSet.of(Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.BONE_MEAL), ImmutableSet.of(Blocks.FARMLAND), SoundEvents.VILLAGER_WORK_FARMER); - public static final VillagerProfession FISHERMAN = register("fisherman", PoiTypes.FISHERMAN, SoundEvents.VILLAGER_WORK_FISHERMAN); - public static final VillagerProfession FLETCHER = register("fletcher", PoiTypes.FLETCHER, SoundEvents.VILLAGER_WORK_FLETCHER); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7518f375450448b2f5aa71a999a2b213f78b7583..ab7a771a80d5288f79dca65fff3f984536d02d9a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1812,6 +1812,8 @@ public class PurpurWorldConfig { - public boolean villagerCanBeLeashed = false; - public boolean villagerCanBreed = true; - public int villagerBreedingTicks = 6000; -+ public boolean villagerClericsFarmWarts = false; -+ public boolean villagerClericFarmersThrowWarts = true; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1826,6 +1828,8 @@ public class PurpurWorldConfig { - villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed); - villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed); - villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks); -+ villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts); -+ villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0113-Toggle-for-Zombified-Piglin-death-always-counting-as.patch b/patches/server/0113-Toggle-for-Zombified-Piglin-death-always-counting-as.patch deleted file mode 100644 index 1665cd684..000000000 --- a/patches/server/0113-Toggle-for-Zombified-Piglin-death-always-counting-as.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sat, 5 Dec 2020 02:34:22 -0800 -Subject: [PATCH] Toggle for Zombified Piglin death always counting as player - kill when angry - -In Vanilla (as of 1.16.4), when Zombified Piglins die while angry, it will -count as a player kill regardless of whether a player has ever hit them, -meaning they will drop XP. This is abused in Zombified Piglin farms where -the player kills the entities through cramming, but they still drop XP due -to the Piglin being angry, even though the player never hit them. - -This patch adds a toggle to disable this behavior. - -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 3d61b27c23d90e87fdfc8c170eafc116716ac047..67fd554cfd6b848ca1f2cf804ad4543ad2c88845 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -152,7 +152,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.maybeAlertOthers(); - } - -- if (this.isAngry()) { -+ if (this.isAngry() && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur - this.lastHurtByPlayerTime = this.tickCount; - } - -@@ -207,7 +207,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.ticksUntilNextAlert = ZombifiedPiglin.ALERT_INTERVAL.sample(this.random); - } - -- if (entityliving instanceof Player) { -+ if (entityliving instanceof Player && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur - this.setLastHurtByPlayer((Player) entityliving); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ab7a771a80d5288f79dca65fff3f984536d02d9a..b1d2c6b15c8d9977e8d8fcfe61485dab67cd9684 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2056,6 +2056,7 @@ public class PurpurWorldConfig { - public boolean zombifiedPiglinJockeyOnlyBaby = true; - public double zombifiedPiglinJockeyChance = 0.05D; - public boolean zombifiedPiglinJockeyTryExistingChickens = true; -+ public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = true; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -@@ -2070,5 +2071,6 @@ public class PurpurWorldConfig { - zombifiedPiglinJockeyOnlyBaby = getBoolean("mobs.zombified_piglin.jockey.only-babies", zombifiedPiglinJockeyOnlyBaby); - zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance); - zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens); -+ zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry); - } - } diff --git a/patches/server/0114-Configurable-chance-for-wolves-to-spawn-rabid.patch b/patches/server/0114-Configurable-chance-for-wolves-to-spawn-rabid.patch deleted file mode 100644 index c0af0a955..000000000 --- a/patches/server/0114-Configurable-chance-for-wolves-to-spawn-rabid.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 8 Dec 2020 17:15:15 -0500 -Subject: [PATCH] Configurable chance for wolves to spawn rabid - -Configurable chance to spawn a wolf that is rabid. -Rabid wolves attack all players, mobs, and animals. - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index d8c73365915e67b38292c96a9601f983f121bdf9..f39b278bc520a4785e9fa472bccd836ad1daac85 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -10,6 +10,7 @@ import net.minecraft.network.syncher.EntityDataAccessor; - import net.minecraft.network.syncher.EntityDataSerializers; - import net.minecraft.network.syncher.SynchedEntityData; - import net.minecraft.server.level.ServerLevel; -+import net.minecraft.server.level.ServerPlayer; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.sounds.SoundEvents; - import net.minecraft.tags.BlockTags; -@@ -17,9 +18,12 @@ import net.minecraft.util.Mth; - import net.minecraft.util.RandomSource; - import net.minecraft.util.TimeUtil; - import net.minecraft.util.valueproviders.UniformInt; -+import net.minecraft.world.DifficultyInstance; - import net.minecraft.world.InteractionHand; - import net.minecraft.world.InteractionResult; - import net.minecraft.world.damagesource.DamageSource; -+import net.minecraft.world.effect.MobEffectInstance; -+import net.minecraft.world.effect.MobEffects; - import net.minecraft.world.entity.AgeableMob; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.EntityDimensions; -@@ -29,6 +33,7 @@ import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.MobSpawnType; - import net.minecraft.world.entity.NeutralMob; - import net.minecraft.world.entity.Pose; -+import net.minecraft.world.entity.SpawnGroupData; - import net.minecraft.world.entity.TamableAnimal; - import net.minecraft.world.entity.ai.attributes.AttributeSupplier; - import net.minecraft.world.entity.ai.attributes.Attributes; -@@ -37,6 +42,7 @@ import net.minecraft.world.entity.ai.goal.BegGoal; - import net.minecraft.world.entity.ai.goal.BreedGoal; - import net.minecraft.world.entity.ai.goal.FloatGoal; - import net.minecraft.world.entity.ai.goal.FollowOwnerGoal; -+import net.minecraft.world.entity.ai.goal.Goal; - import net.minecraft.world.entity.ai.goal.LeapAtTargetGoal; - import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; - import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; -@@ -64,6 +70,7 @@ import net.minecraft.world.item.ItemStack; - import net.minecraft.world.item.Items; - import net.minecraft.world.level.Level; - import net.minecraft.world.level.LevelAccessor; -+import net.minecraft.world.level.ServerLevelAccessor; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.gameevent.GameEvent; - import net.minecraft.world.level.pathfinder.BlockPathTypes; -@@ -84,6 +91,37 @@ public class Wolf extends TamableAnimal implements NeutralMob { - - return entitytypes == EntityType.SHEEP || entitytypes == EntityType.RABBIT || entitytypes == EntityType.FOX; - }; -+ // Purpur start - rabid wolf spawn chance -+ private boolean isRabid = false; -+ private static final Predicate RABID_PREDICATE = entity -> entity instanceof ServerPlayer || entity instanceof Mob; -+ private final Goal PATHFINDER_VANILLA = new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR); -+ private final Goal PATHFINDER_RABID = new NonTameRandomTargetGoal<>(this, LivingEntity.class, false, RABID_PREDICATE); -+ private static final class AvoidRabidWolfGoal extends AvoidEntityGoal { -+ private final Wolf wolf; -+ -+ public AvoidRabidWolfGoal(Wolf wolf, float distance, double minSpeed, double maxSpeed) { -+ super(wolf, Wolf.class, distance, minSpeed, maxSpeed); -+ this.wolf = wolf; -+ } -+ -+ @Override -+ public boolean canUse() { -+ return super.canUse() && !this.wolf.isRabid() && this.toAvoid != null && this.toAvoid.isRabid(); // wolves which are not rabid run away from rabid wolves -+ } -+ -+ @Override -+ public void start() { -+ this.wolf.setTarget(null); -+ super.start(); -+ } -+ -+ @Override -+ public void tick() { -+ this.wolf.setTarget(null); -+ super.tick(); -+ } -+ } -+ // Purpur end - private static final float START_HEALTH = 8.0F; - private static final float TAME_HEALTH = 20.0F; - private float interestedAngle; -@@ -135,6 +173,37 @@ public class Wolf extends TamableAnimal implements NeutralMob { - return this.level().purpurConfig.wolfBreedingTicks; - } - -+ public boolean isRabid() { -+ return this.isRabid; -+ } -+ -+ public void setRabid(boolean isRabid) { -+ this.isRabid = isRabid; -+ updatePathfinders(true); -+ } -+ -+ public void updatePathfinders(boolean modifyEffects) { -+ this.targetSelector.removeGoal(PATHFINDER_VANILLA); -+ this.targetSelector.removeGoal(PATHFINDER_RABID); -+ if (this.isRabid) { -+ setTame(false); -+ setOwnerUUID(null); -+ this.targetSelector.addGoal(5, PATHFINDER_RABID); -+ if (modifyEffects) this.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 1200)); -+ } else { -+ this.targetSelector.addGoal(5, PATHFINDER_VANILLA); -+ this.stopBeingAngry(); -+ if (modifyEffects) this.removeEffect(MobEffects.CONFUSION); -+ } -+ } -+ -+ @Override -+ public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType type, @Nullable SpawnGroupData data, @Nullable CompoundTag nbt) { -+ this.isRabid = world.getLevel().purpurConfig.wolfNaturalRabid > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.wolfNaturalRabid; -+ this.updatePathfinders(false); -+ return super.finalizeSpawn(world, difficulty, type, data, nbt); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -@@ -142,6 +211,7 @@ public class Wolf extends TamableAnimal implements NeutralMob { - this.goalSelector.addGoal(1, new Wolf.WolfPanicGoal(1.5D)); - this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); - this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D)); -+ this.goalSelector.addGoal(3, new AvoidRabidWolfGoal(this, 24.0F, 1.5D, 1.5D)); // Purpur - this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F)); - this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0D, true)); - this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F, false)); -@@ -155,7 +225,7 @@ public class Wolf extends TamableAnimal implements NeutralMob { - this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); - this.targetSelector.addGoal(3, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); - this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); -- this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR)); -+ // this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, Wolf.PREY_SELECTOR)); // Purpur - moved to updatePathfinders() - this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); - this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false)); - this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true)); -@@ -182,6 +252,7 @@ public class Wolf extends TamableAnimal implements NeutralMob { - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putByte("CollarColor", (byte) this.getCollarColor().getId()); -+ nbt.putBoolean("Purpur.IsRabid", this.isRabid); // Purpur - this.addPersistentAngerSaveData(nbt); - } - -@@ -191,6 +262,10 @@ public class Wolf extends TamableAnimal implements NeutralMob { - if (nbt.contains("CollarColor", 99)) { - this.setCollarColor(DyeColor.byId(nbt.getInt("CollarColor"))); - } -+ // Purpur start -+ this.isRabid = nbt.getBoolean("Purpur.IsRabid"); -+ this.updatePathfinders(false); -+ // Purpur end - - this.readPersistentAngerSaveData(this.level(), nbt); - } -@@ -235,6 +310,11 @@ public class Wolf extends TamableAnimal implements NeutralMob { - public void tick() { - super.tick(); - if (this.isAlive()) { -+ // Purpur start -+ if (this.age % 300 == 0 && this.isRabid()) { -+ this.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 400)); -+ } -+ // Purpur end - this.interestedAngleO = this.interestedAngle; - if (this.isInterested()) { - this.interestedAngle += (1.0F - this.interestedAngle) * 0.4F; -@@ -450,6 +530,19 @@ public class Wolf extends TamableAnimal implements NeutralMob { - } - - return InteractionResult.SUCCESS; -+ // Purpur start -+ } else if (this.level().purpurConfig.wolfMilkCuresRabies && itemstack.getItem() == Items.MILK_BUCKET && this.isRabid()) { -+ if (!player.isCreative()) { -+ player.setItemInHand(hand, new ItemStack(Items.BUCKET)); -+ } -+ this.setRabid(false); -+ for (int i = 0; i < 10; ++i) { -+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER, -+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 1.5), getZ() + random.nextFloat(), 1, -+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true); -+ } -+ return InteractionResult.SUCCESS; -+ // Purpur end - } else { - return super.mobInteract(player, hand); - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java -index e16459c9cfcac790edd6d912750d32c68387cbbc..890938ad866e2c588f3f819230ba121b0b436401 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java -@@ -57,4 +57,16 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf { - public void setInterested(boolean flag) { - this.getHandle().setIsInterested(flag); - } -+ -+ // Purpur start -+ @Override -+ public boolean isRabid() { -+ return getHandle().isRabid(); -+ } -+ -+ @Override -+ public void setRabid(boolean isRabid) { -+ getHandle().setRabid(isRabid); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b1d2c6b15c8d9977e8d8fcfe61485dab67cd9684..839f95b52f3ca44ea479d09b37f7b8aaa67f9672 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1941,6 +1941,8 @@ public class PurpurWorldConfig { - public boolean wolfRidableInWater = true; - public boolean wolfControllable = true; - public double wolfMaxHealth = 8.0D; -+ public boolean wolfMilkCuresRabies = true; -+ public double wolfNaturalRabid = 0.0D; - public int wolfBreedingTicks = 6000; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); -@@ -1952,6 +1954,8 @@ public class PurpurWorldConfig { - set("mobs.wolf.attributes.max_health", oldValue); - } - wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); -+ wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies); -+ wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid); - wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); - } - diff --git a/patches/server/0115-Configurable-default-collar-color.patch b/patches/server/0115-Configurable-default-collar-color.patch deleted file mode 100644 index bbb4d5294..000000000 --- a/patches/server/0115-Configurable-default-collar-color.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Thu, 10 Dec 2020 13:43:28 -0500 -Subject: [PATCH] Configurable default collar color - -This allows for the server to set a default collar color when a wolf or cat is tamed. -Resets to RED when the value is invalid. - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index ffa2128b63e5e45c2a41e31a6b8b6e8df0bf9be4..956f125c9ae62537570bb9870be66872f3519f6a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -345,6 +345,14 @@ public class Cat extends TamableAnimal implements VariantHolder { - return Mth.lerp(tickDelta, this.relaxStateOneAmountO, this.relaxStateOneAmount); - } - -+ // Purpur start -+ @Override -+ public void tame(Player player) { -+ setCollarColor(level().purpurConfig.catDefaultCollarColor); -+ super.tame(player); -+ } -+ // Purpur end -+ - @Nullable - @Override - public Cat getBreedOffspring(ServerLevel world, AgeableMob entity) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index f39b278bc520a4785e9fa472bccd836ad1daac85..8d71d0ad4bf8e0df8ded9ebce6f39e66dd632bb5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -204,6 +204,12 @@ public class Wolf extends TamableAnimal implements NeutralMob { - return super.finalizeSpawn(world, difficulty, type, data, nbt); - } - -+ @Override -+ public void tame(Player player) { -+ setCollarColor(level().purpurConfig.wolfDefaultCollarColor); -+ super.tame(player); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 839f95b52f3ca44ea479d09b37f7b8aaa67f9672..5ddb33b3f6811ddea949b08ed72e9355489b9167 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -625,6 +625,7 @@ public class PurpurWorldConfig { - public int catSpawnSwampHutScanRange = 16; - public int catSpawnVillageScanRange = 48; - public int catBreedingTicks = 6000; -+ public DyeColor catDefaultCollarColor = DyeColor.RED; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -639,6 +640,11 @@ public class PurpurWorldConfig { - catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange); - catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange); - catBreedingTicks = getInt("mobs.cat.breeding-delay-ticks", catBreedingTicks); -+ try { -+ catDefaultCollarColor = DyeColor.valueOf(getString("mobs.cat.default-collar-color", catDefaultCollarColor.name())); -+ } catch (IllegalArgumentException ignore) { -+ catDefaultCollarColor = DyeColor.RED; -+ } - } - - public boolean caveSpiderRidable = false; -@@ -1941,6 +1947,7 @@ public class PurpurWorldConfig { - public boolean wolfRidableInWater = true; - public boolean wolfControllable = true; - public double wolfMaxHealth = 8.0D; -+ public DyeColor wolfDefaultCollarColor = DyeColor.RED; - public boolean wolfMilkCuresRabies = true; - public double wolfNaturalRabid = 0.0D; - public int wolfBreedingTicks = 6000; -@@ -1954,6 +1961,11 @@ public class PurpurWorldConfig { - set("mobs.wolf.attributes.max_health", oldValue); - } - wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); -+ try { -+ wolfDefaultCollarColor = DyeColor.valueOf(getString("mobs.wolf.default-collar-color", wolfDefaultCollarColor.name())); -+ } catch (IllegalArgumentException ignore) { -+ wolfDefaultCollarColor = DyeColor.RED; -+ } - wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies); - wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid); - wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); diff --git a/patches/server/0116-Phantom-flames-on-swoop.patch b/patches/server/0116-Phantom-flames-on-swoop.patch deleted file mode 100644 index 3cd4ccdfc..000000000 --- a/patches/server/0116-Phantom-flames-on-swoop.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 12 Dec 2020 09:10:59 -0600 -Subject: [PATCH] Phantom flames on swoop - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 899a7d3989b51456600787ae09b1736f83bf9a65..076a881412d40ce6a8f5e5f83f7598bfa08f199c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -243,6 +243,7 @@ public class Phantom extends FlyingMob implements Enemy { - this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() - (double) f2, this.getY() + (double) f4, this.getZ() - (double) f3, 0.0D, 0.0D, 0.0D); - } - -+ if (level().purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5ddb33b3f6811ddea949b08ed72e9355489b9167..d755e920048324ae17a08fee7546a4b2fb594f7c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1303,6 +1303,7 @@ public class PurpurWorldConfig { - public int phantomBurnInLight = 0; - public boolean phantomIgnorePlayersWithTorch = false; - public boolean phantomBurnInDaylight = true; -+ public boolean phantomFlamesOnSwoop = false; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -1336,6 +1337,7 @@ public class PurpurWorldConfig { - phantomBurnInLight = getInt("mobs.phantom.burn-in-light", phantomBurnInLight); - phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight); - phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch); -+ phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop); - } - - public boolean pigRidable = false; diff --git a/patches/server/0117-Option-for-chests-to-open-even-with-a-solid-block-on.patch b/patches/server/0117-Option-for-chests-to-open-even-with-a-solid-block-on.patch deleted file mode 100644 index e88900d66..000000000 --- a/patches/server/0117-Option-for-chests-to-open-even-with-a-solid-block-on.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sat, 12 Dec 2020 14:34:18 -0800 -Subject: [PATCH] Option for chests to open even with a solid block on top - - -diff --git a/src/main/java/net/minecraft/world/level/block/ChestBlock.java b/src/main/java/net/minecraft/world/level/block/ChestBlock.java -index 5e22d175b1048a58802cdf64ac70a8b56329e915..d81946b400f208c39941128ce823ff7709741c10 100644 ---- a/src/main/java/net/minecraft/world/level/block/ChestBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ChestBlock.java -@@ -355,6 +355,7 @@ public class ChestBlock extends AbstractChestBlock implements - } - - private static boolean isBlockedChestByBlock(BlockGetter world, BlockPos pos) { -+ if (world instanceof Level && ((Level) world).purpurConfig.chestOpenWithBlockOnTop) return false; // Purpur - BlockPos blockposition1 = pos.above(); - - return world.getBlockState(blockposition1).isRedstoneConductor(world, blockposition1); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d755e920048324ae17a08fee7546a4b2fb594f7c..57cb484499525278fe2664ccba83503e9344a874 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -423,6 +423,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean chestOpenWithBlockOnTop = false; -+ private void chestSettings() { -+ chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); -+ } -+ - public boolean dispenserApplyCursedArmor = true; - public boolean dispenserPlaceAnvils = false; - private void dispenserSettings() { diff --git a/patches/server/0118-Implement-TPSBar.patch b/patches/server/0118-Implement-TPSBar.patch deleted file mode 100644 index c994c098e..000000000 --- a/patches/server/0118-Implement-TPSBar.patch +++ /dev/null @@ -1,467 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 12 Dec 2020 21:19:05 -0600 -Subject: [PATCH] Implement TPSBar - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index e5b1b6ad32c48a4ba13b4930954fad18669677ad..4e721dfca7559620d8ce65a6703f2089a839f4a0 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -227,6 +227,7 @@ public class Commands { - org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur -+ org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur - } - - if (environment.includeIntegrated) { -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 44e01ac1874d07d5348b53bb1af613b01606fef1..70c2ccc971600f856b90b2fb1b51cb2f51e21cdd 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1023,6 +1023,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop viewDistances = new java.util.concurrent.atomic.AtomicReference<>(new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.ViewDistances(-1, -1, -1)); - public io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -560,6 +561,7 @@ public class ServerPlayer extends Player { - } - } - -+ if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur - } - - @Override -@@ -626,6 +628,7 @@ public class ServerPlayer extends Player { - } - this.getBukkitEntity().setExtraData(nbt); // CraftBukkit - -+ nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur - } - - // CraftBukkit start - World fallback code, either respawn location or global spawn -@@ -2794,5 +2797,13 @@ public class ServerPlayer extends Player { - this.server.getPlayerList().respawn(this, toLevel, true, to, !toLevel.paperConfig().environment.disableTeleportationSuffocationCheck, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH); - } - } -+ -+ public boolean tpsBar() { -+ return this.tpsBar; -+ } -+ -+ public void tpsBar(boolean tpsBar) { -+ this.tpsBar = tpsBar; -+ } - // Purpur end - } -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 9cecf7d3e2f860f9de5dd13a352d8d7ed6f95266..9a60fa370f37179a7460b316d26e601197b478c7 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -484,6 +484,7 @@ public abstract class PlayerList { - scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); - } - // Paper end -+ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur - // CraftBukkit - Moved from above, added world - PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); - } -@@ -593,6 +594,7 @@ public abstract class PlayerList { - } - public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) { - // Paper end -+ org.purpurmc.purpur.task.BossBarTask.removeFromAll(entityplayer.getBukkitEntity()); // Purpur - ServerLevel worldserver = entityplayer.serverLevel(); - - entityplayer.awardStat(Stats.LEAVE_GAME); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index aaf96399f937fbca30f1ce5d64be4af265a4ed29..61e3bce901bdbf111ffc06521b0e6d5369fce051 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -182,6 +182,7 @@ public class PurpurConfig { - public static String creditsCommandOutput = "%s has been shown the end credits"; - public static String demoCommandOutput = "%s has been shown the demo screen"; - public static String pingCommandOutput = "%s's ping is %sms"; -+ public static String tpsbarCommandOutput = "Tpsbar toggled for "; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -@@ -192,6 +193,7 @@ public class PurpurConfig { - creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput); - demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); -+ tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); - } - - public static String serverModName = "Purpur"; -@@ -214,6 +216,29 @@ public class PurpurConfig { - disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops); - } - -+ public static String commandTPSBarTitle = "TPS: MSPT: Ping: ms"; -+ public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20; -+ public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT; -+ public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN; -+ public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW; -+ public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED; -+ public static String commandTPSBarTextColorGood = ""; -+ public static String commandTPSBarTextColorMedium = ""; -+ public static String commandTPSBarTextColorLow = ""; -+ public static int commandTPSBarTickInterval = 20; -+ private static void commandSettings() { -+ commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); -+ commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); -+ commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name())); -+ commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name())); -+ commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name())); -+ commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name())); -+ commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood); -+ commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium); -+ commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); -+ commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); -+ } -+ - public static int barrelRows = 3; - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; -diff --git a/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d8f9b044107ff7c29a83eb5378aa9f5465ba1995 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java -@@ -0,0 +1,44 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.commands.arguments.EntityArgument; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.PurpurConfig; -+import org.purpurmc.purpur.task.TPSBarTask; -+ -+import java.util.Collection; -+import java.util.Collections; -+ -+public class TPSBarCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("tpsbar") -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar")) -+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) -+ .then(Commands.argument("targets", EntityArgument.players()) -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.tpsbar.other")) -+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) -+ ) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender, Collection targets) { -+ for (ServerPlayer player : targets) { -+ boolean result = TPSBarTask.instance().togglePlayer(player.getBukkitEntity()); -+ player.tpsBar(result); -+ -+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.tpsbarCommandOutput, -+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off") -+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)), -+ Placeholder.parsed("target", player.getGameProfile().getName())); -+ -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d38b3c4a722396cc3b61a9a8ed7e39cea4ae65cb ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -@@ -0,0 +1,109 @@ -+package org.purpurmc.purpur.task; -+ -+import net.kyori.adventure.bossbar.BossBar; -+import net.minecraft.server.level.ServerPlayer; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; -+import org.bukkit.entity.Player; -+import org.bukkit.scheduler.BukkitRunnable; -+ -+import java.util.HashMap; -+import java.util.HashSet; -+import java.util.Iterator; -+import java.util.Map; -+import java.util.UUID; -+ -+public abstract class BossBarTask extends BukkitRunnable { -+ private final Map bossbars = new HashMap<>(); -+ private boolean started; -+ -+ abstract BossBar createBossBar(); -+ -+ abstract void updateBossBar(BossBar bossbar, Player player); -+ -+ @Override -+ public void run() { -+ Iterator> iter = bossbars.entrySet().iterator(); -+ while (iter.hasNext()) { -+ Map.Entry entry = iter.next(); -+ Player player = Bukkit.getPlayer(entry.getKey()); -+ if (player == null) { -+ iter.remove(); -+ continue; -+ } -+ updateBossBar(entry.getValue(), player); -+ } -+ } -+ -+ @Override -+ public void cancel() { -+ super.cancel(); -+ new HashSet<>(this.bossbars.keySet()).forEach(uuid -> { -+ Player player = Bukkit.getPlayer(uuid); -+ if (player != null) { -+ removePlayer(player); -+ } -+ }); -+ this.bossbars.clear(); -+ } -+ -+ public boolean removePlayer(Player player) { -+ BossBar bossbar = this.bossbars.remove(player.getUniqueId()); -+ if (bossbar != null) { -+ player.hideBossBar(bossbar); -+ return true; -+ } -+ return false; -+ } -+ -+ public void addPlayer(Player player) { -+ removePlayer(player); -+ BossBar bossbar = createBossBar(); -+ this.bossbars.put(player.getUniqueId(), bossbar); -+ this.updateBossBar(bossbar, player); -+ player.showBossBar(bossbar); -+ } -+ -+ public boolean hasPlayer(UUID uuid) { -+ return this.bossbars.containsKey(uuid); -+ } -+ -+ public boolean togglePlayer(Player player) { -+ if (removePlayer(player)) { -+ return false; -+ } -+ addPlayer(player); -+ return true; -+ } -+ -+ public void start() { -+ stop(); -+ this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1); -+ started = true; -+ } -+ -+ public void stop() { -+ if (started) { -+ cancel(); -+ } -+ } -+ -+ public static void startAll() { -+ TPSBarTask.instance().start(); -+ } -+ -+ public static void stopAll() { -+ TPSBarTask.instance().stop(); -+ } -+ -+ public static void addToAll(ServerPlayer player) { -+ Player bukkit = player.getBukkitEntity(); -+ if (player.tpsBar()) { -+ TPSBarTask.instance().addPlayer(bukkit); -+ } -+ } -+ -+ public static void removeFromAll(Player player) { -+ TPSBarTask.instance().removePlayer(player); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8769993e7ca59da309087051a3cd38fc562c15d1 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java -@@ -0,0 +1,142 @@ -+package org.purpurmc.purpur.task; -+ -+import net.kyori.adventure.bossbar.BossBar; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import org.purpurmc.purpur.PurpurConfig; -+import org.bukkit.Bukkit; -+import org.bukkit.entity.Player; -+ -+public class TPSBarTask extends BossBarTask { -+ private static TPSBarTask instance; -+ private double tps = 20.0D; -+ private double mspt = 0.0D; -+ private int tick = 0; -+ -+ public static TPSBarTask instance() { -+ if (instance == null) { -+ instance = new TPSBarTask(); -+ } -+ return instance; -+ } -+ -+ @Override -+ BossBar createBossBar() { -+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandTPSBarProgressOverlay); -+ } -+ -+ @Override -+ void updateBossBar(BossBar bossbar, Player player) { -+ bossbar.progress(getBossBarProgress()); -+ bossbar.color(getBossBarColor()); -+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle, -+ Placeholder.component("tps", getTPSColor()), -+ Placeholder.component("mspt", getMSPTColor()), -+ Placeholder.component("ping", getPingColor(player.getPing())) -+ )); -+ } -+ -+ @Override -+ public void run() { -+ if (++tick < PurpurConfig.commandTPSBarTickInterval) { -+ return; -+ } -+ tick = 0; -+ -+ this.tps = Math.max(Math.min(Bukkit.getTPS()[0], 20.0D), 0.0D); -+ this.mspt = Bukkit.getAverageTickTime(); -+ -+ super.run(); -+ } -+ -+ private float getBossBarProgress() { -+ if (PurpurConfig.commandTPSBarProgressFillMode == FillMode.MSPT) { -+ return Math.max(Math.min((float) mspt / 50.0F, 1.0F), 0.0F); -+ } else { -+ return Math.max(Math.min((float) tps / 20.0F, 1.0F), 0.0F); -+ } -+ } -+ -+ private BossBar.Color getBossBarColor() { -+ if (isGood(PurpurConfig.commandTPSBarProgressFillMode)) { -+ return PurpurConfig.commandTPSBarProgressColorGood; -+ } else if (isMedium(PurpurConfig.commandTPSBarProgressFillMode)) { -+ return PurpurConfig.commandTPSBarProgressColorMedium; -+ } else { -+ return PurpurConfig.commandTPSBarProgressColorLow; -+ } -+ } -+ -+ private boolean isGood(FillMode mode) { -+ return isGood(mode, 0); -+ } -+ -+ private boolean isGood(FillMode mode, int ping) { -+ if (mode == FillMode.MSPT) { -+ return mspt < 40; -+ } else if (mode == FillMode.TPS) { -+ return tps >= 19; -+ } else if (mode == FillMode.PING) { -+ return ping < 100; -+ } else { -+ return false; -+ } -+ } -+ -+ private boolean isMedium(FillMode mode) { -+ return isMedium(mode, 0); -+ } -+ -+ private boolean isMedium(FillMode mode, int ping) { -+ if (mode == FillMode.MSPT) { -+ return mspt < 50; -+ } else if (mode == FillMode.TPS) { -+ return tps >= 15; -+ } else if (mode == FillMode.PING) { -+ return ping < 200; -+ } else { -+ return false; -+ } -+ } -+ -+ private Component getTPSColor() { -+ String color; -+ if (isGood(FillMode.TPS)) { -+ color = PurpurConfig.commandTPSBarTextColorGood; -+ } else if (isMedium(FillMode.TPS)) { -+ color = PurpurConfig.commandTPSBarTextColorMedium; -+ } else { -+ color = PurpurConfig.commandTPSBarTextColorLow; -+ } -+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps))); -+ } -+ -+ private Component getMSPTColor() { -+ String color; -+ if (isGood(FillMode.MSPT)) { -+ color = PurpurConfig.commandTPSBarTextColorGood; -+ } else if (isMedium(FillMode.MSPT)) { -+ color = PurpurConfig.commandTPSBarTextColorMedium; -+ } else { -+ color = PurpurConfig.commandTPSBarTextColorLow; -+ } -+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", mspt))); -+ } -+ -+ private Component getPingColor(int ping) { -+ String color; -+ if (isGood(FillMode.PING, ping)) { -+ color = PurpurConfig.commandTPSBarTextColorGood; -+ } else if (isMedium(FillMode.PING, ping)) { -+ color = PurpurConfig.commandTPSBarTextColorMedium; -+ } else { -+ color = PurpurConfig.commandTPSBarTextColorLow; -+ } -+ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping))); -+ } -+ -+ public enum FillMode { -+ TPS, MSPT, PING -+ } -+} diff --git a/patches/server/0119-Striders-give-saddle-back.patch b/patches/server/0119-Striders-give-saddle-back.patch deleted file mode 100644 index 9a4a1f0bb..000000000 --- a/patches/server/0119-Striders-give-saddle-back.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sun, 13 Dec 2020 20:40:57 -0500 -Subject: [PATCH] Striders give saddle back - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index 93dca4758ed995373fe1585930f225604a4153fa..4c077c94b654f54465ece2aa45c5ead0a417ad0d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -486,6 +486,19 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - public InteractionResult mobInteract(Player player, InteractionHand hand) { - boolean flag = this.isFood(player.getItemInHand(hand)); - -+ // Purpur start -+ if (level().purpurConfig.striderGiveSaddleBack && player.isSecondaryUseActive() && !flag && isSaddled() && !isVehicle()) { -+ this.steering.setSaddle(false); -+ if (!player.getAbilities().instabuild) { -+ ItemStack saddle = new ItemStack(Items.SADDLE); -+ if (!player.getInventory().add(saddle)) { -+ player.drop(saddle, false); -+ } -+ } -+ return InteractionResult.SUCCESS; -+ } -+ // Purpur end -+ - if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) { - if (!this.level().isClientSide) { - player.startRiding(this); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 57cb484499525278fe2664ccba83503e9344a874..a0607bcc0da66718928c0ca6e0e02a5a4a56eaf4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1715,6 +1715,7 @@ public class PurpurWorldConfig { - public boolean striderControllable = true; - public double striderMaxHealth = 20.0D; - public int striderBreedingTicks = 6000; -+ public boolean striderGiveSaddleBack = false; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -@@ -1726,6 +1727,7 @@ public class PurpurWorldConfig { - } - striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth); - striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks); -+ striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack); - } - - public boolean tadpoleRidable = false; diff --git a/patches/server/0120-PlayerBookTooLargeEvent.patch b/patches/server/0120-PlayerBookTooLargeEvent.patch deleted file mode 100644 index 0a443fe91..000000000 --- a/patches/server/0120-PlayerBookTooLargeEvent.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 23 Dec 2020 00:43:59 -0600 -Subject: [PATCH] PlayerBookTooLargeEvent - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 8126b1a67e62077c8a78f13deee767236665d7fa..6c5b0d1d8fe7fae29dc1ed449e0412c9f449acf9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1279,10 +1279,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - int maxBookPageSize = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax; - double multiplier = Math.max(0.3D, Math.min(1D, io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier)); - long byteAllowed = maxBookPageSize; -+ ItemStack itemstack = this.player.getInventory().getItem(packet.getSlot()); // Purpur - for (String testString : pageList) { - int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; - if (byteLength > 256 * 4) { - ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send a book with with a page too large!"); -+ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur - server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause - return; - } -@@ -1306,6 +1308,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - if (byteTotal > byteAllowed) { - ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size()); -+ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur - server.scheduleOnMain(() -> this.disconnect("Book too large!", org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION)); // Paper - kick event cause - return; - } diff --git a/patches/server/0121-Full-netherite-armor-grants-fire-resistance.patch b/patches/server/0121-Full-netherite-armor-grants-fire-resistance.patch deleted file mode 100644 index c602cf303..000000000 --- a/patches/server/0121-Full-netherite-armor-grants-fire-resistance.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 24 Dec 2020 11:00:15 -0600 -Subject: [PATCH] Full netherite armor grants fire resistance - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index fa2a0cc24bbe31abd49ce0f3f41bab2aa5d9c81f..04e816f01db7d2de7ffa9bbaaa72daadaab34831 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -366,6 +366,16 @@ public abstract class Player extends LivingEntity { - this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TURTLE_HELMET); // CraftBukkit - } - -+ // Purpur start -+ if (this.level().purpurConfig.playerNetheriteFireResistanceDuration > 0 && this.level().getGameTime() % 20 == 0) { -+ if (itemstack.is(Items.NETHERITE_HELMET) -+ && this.getItemBySlot(EquipmentSlot.CHEST).is(Items.NETHERITE_CHESTPLATE) -+ && this.getItemBySlot(EquipmentSlot.LEGS).is(Items.NETHERITE_LEGGINGS) -+ && this.getItemBySlot(EquipmentSlot.FEET).is(Items.NETHERITE_BOOTS)) { -+ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, this.level().purpurConfig.playerNetheriteFireResistanceDuration, this.level().purpurConfig.playerNetheriteFireResistanceAmplifier, this.level().purpurConfig.playerNetheriteFireResistanceAmbient, this.level().purpurConfig.playerNetheriteFireResistanceShowParticles, this.level().purpurConfig.playerNetheriteFireResistanceShowIcon), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NETHERITE_ARMOR); -+ } -+ } -+ // Purpur end - } - - protected ItemCooldowns createItemCooldowns() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a0607bcc0da66718928c0ca6e0e02a5a4a56eaf4..2606c631d93c8e7b99b0ac1fffffa6b0ce388679 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -295,6 +295,19 @@ public class PurpurWorldConfig { - villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate); - } - -+ public int playerNetheriteFireResistanceDuration = 0; -+ public int playerNetheriteFireResistanceAmplifier = 0; -+ public boolean playerNetheriteFireResistanceAmbient = false; -+ public boolean playerNetheriteFireResistanceShowParticles = false; -+ public boolean playerNetheriteFireResistanceShowIcon = true; -+ private void playerNetheriteFireResistance() { -+ playerNetheriteFireResistanceDuration = getInt("gameplay-mechanics.player.netherite-fire-resistance.duration", playerNetheriteFireResistanceDuration); -+ playerNetheriteFireResistanceAmplifier = getInt("gameplay-mechanics.player.netherite-fire-resistance.amplifier", playerNetheriteFireResistanceAmplifier); -+ playerNetheriteFireResistanceAmbient = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.ambient", playerNetheriteFireResistanceAmbient); -+ playerNetheriteFireResistanceShowParticles = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-particles", playerNetheriteFireResistanceShowParticles); -+ playerNetheriteFireResistanceShowIcon = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-icon", playerNetheriteFireResistanceShowIcon); -+ } -+ - public boolean idleTimeoutKick = true; - public boolean idleTimeoutTickNearbyEntities = true; - public boolean idleTimeoutCountAsSleeping = false; diff --git a/patches/server/0122-Fix-rotating-UP-DOWN-CW-and-CCW.patch b/patches/server/0122-Fix-rotating-UP-DOWN-CW-and-CCW.patch deleted file mode 100644 index 514296a73..000000000 --- a/patches/server/0122-Fix-rotating-UP-DOWN-CW-and-CCW.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 6 Jan 2021 02:19:29 -0600 -Subject: [PATCH] Fix rotating UP/DOWN CW and CCW - - -diff --git a/src/main/java/net/minecraft/core/Direction.java b/src/main/java/net/minecraft/core/Direction.java -index d0a8092bf57a29ab7c00ec0ddf52a9fdb2a33267..defe5951938ce3a7b7f83017d4af36bb49ff5be0 100644 ---- a/src/main/java/net/minecraft/core/Direction.java -+++ b/src/main/java/net/minecraft/core/Direction.java -@@ -238,6 +238,12 @@ public enum Direction implements StringRepresentable { - case EAST: - var10000 = SOUTH; - break; -+ // Purpur start -+ case UP: -+ return UP; -+ case DOWN: -+ return DOWN; -+ // Purpur end - default: - throw new IllegalStateException("Unable to get Y-rotated facing of " + this); - } -@@ -350,6 +356,12 @@ public enum Direction implements StringRepresentable { - case EAST: - var10000 = NORTH; - break; -+ // Purpur start -+ case UP: -+ return UP; -+ case DOWN: -+ return DOWN; -+ // Purpur end - default: - throw new IllegalStateException("Unable to get CCW facing of " + this); - } diff --git a/patches/server/0123-Add-mobGriefing-bypass-to-everything-affected.patch b/patches/server/0123-Add-mobGriefing-bypass-to-everything-affected.patch deleted file mode 100644 index f8e86874a..000000000 --- a/patches/server/0123-Add-mobGriefing-bypass-to-everything-affected.patch +++ /dev/null @@ -1,679 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 5 Jan 2021 22:21:56 -0500 -Subject: [PATCH] Add mobGriefing bypass to everything affected - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 6edb9c371d77fa52b08f0eaf738d0cb4b0af86ce..04b98c7c738682f5c1da27a6873a814eaca5d14b 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1768,7 +1768,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - boolean flag = false; - - if (this.dead && adversary instanceof WitherBoss) { // Paper -- if (this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.level().purpurConfig.witherBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - BlockPos blockposition = this.blockPosition(); - BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState(); - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 4e5a261ccb7fb63b82ac7194598729fd60a20840..d24f94f2c27523af10184cae27157169d5deabae 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -703,7 +703,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - public void aiStep() { - super.aiStep(); - this.level().getProfiler().push("looting"); -- if (!this.level().isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!this.level().isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && (this.level().purpurConfig.entitiesPickUpLootBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { - Vec3i baseblockposition = this.getPickupReach(); - List list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ())); - Iterator iterator = list.iterator(); -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -index 66a568e6803b91040f3933d4598c52fa518badab..a1acc479c9d6a838e3180da3ac8a3a02d22db5c1 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java -@@ -47,7 +47,7 @@ public class HarvestFarmland extends Behavior { - } - - protected boolean checkExtraStartConditions(ServerLevel world, Villager entity) { -- if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!world.purpurConfig.villagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - return false; - } else if (entity.getVillagerData().getProfession() != VillagerProfession.FARMER && !(world.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().getProfession() == VillagerProfession.CLERIC)) { // Purpur - return false; -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java -index a85885ee51df585fa11ae9f8fcd67ff2a71c5a18..d81509e08e70ec5b2f837c9dc66b1254c86854e4 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java -@@ -32,7 +32,7 @@ public class BreakDoorGoal extends DoorInteractGoal { - - @Override - public boolean canUse() { -- return !super.canUse() ? false : (!this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen()); -+ return !super.canUse() ? false : ((!this.mob.level().purpurConfig.zombieBypassMobGriefing && !this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen()); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java -index 93bbda61f0eb2dd52573602b1f9cc7b031d1fc5a..63f9e5e2490e5b2fec6f2395077e21e601804ca5 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java -@@ -74,7 +74,7 @@ public class EatBlockGoal extends Goal { - - final BlockState blockState = this.level.getBlockState(blockposition); // Paper - fix wrong block state - if (EatBlockGoal.IS_TALL_GRASS.test(blockState)) { // Paper - fix wrong block state -- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state -+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !this.level.purpurConfig.sheepBypassMobGriefing && !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state // Purpur - this.level.destroyBlock(blockposition, false); - } - -@@ -83,7 +83,7 @@ public class EatBlockGoal extends Goal { - BlockPos blockposition1 = blockposition.below(); - - if (this.level.getBlockState(blockposition1).is(Blocks.GRASS_BLOCK)) { -- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state -+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !this.level.purpurConfig.sheepBypassMobGriefing && !this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state // Purpur - this.level.levelEvent(2001, blockposition1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); - this.level.setBlock(blockposition1, Blocks.DIRT.defaultBlockState(), 2); - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -index 509317a26c79f453335df1c19dc4c9ec570046af..8e4d673e4f2d7f50ea5ed13794da08b1d20e6665 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -@@ -40,7 +40,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - - @Override - public boolean canUse() { -- if (!this.removerMob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!this.removerMob.level().purpurConfig.zombieBypassMobGriefing && !this.removerMob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - return false; - } else if (this.nextStartTick > 0) { - --this.nextStartTick; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index 2cf00af66b73e1891fadcabcf83307873cbed710..2a6f6753ce9b7ab899256edc181a92c19021822b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -1377,7 +1377,7 @@ public class Fox extends Animal implements VariantHolder { - } - - protected void onReachedTarget() { -- if (Fox.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (Fox.this.level().purpurConfig.foxBypassMobGriefing || Fox.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - BlockState iblockdata = Fox.this.level().getBlockState(this.blockPos); - - if (iblockdata.is(Blocks.SWEET_BERRY_BUSH)) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -index 787391763cac2ae6ed9c178c44616b992849e819..0187af6c3380c1880ae1e3b8982b1543ae9da4a5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -633,7 +633,7 @@ public class Rabbit extends Animal implements VariantHolder { - @Override - public boolean canUse() { - if (this.nextStartTick <= 0) { -- if (!this.rabbit.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!this.rabbit.level().purpurConfig.rabbitBypassMobGriefing && !this.rabbit.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - return false; - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index d3da7487f911ff791dca5f7b546dccda751fe44c..84e0a41620fd52af0cd22dc92fd13ee4efd206fb 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -127,7 +127,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - this.hurt(this.damageSources().melting, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING - } - -- if (!this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!this.level().purpurConfig.snowGolemBypassMobGriefing && !this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - return; - } - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index 3898653acb6127c88a94b5e7111a9cfecb2d136e..8f71c3b94ed17fd72e6bacce7d0a8e3943c37c36 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -612,7 +612,7 @@ public class EnderDragon extends Mob implements Enemy { - BlockState iblockdata = this.level().getBlockState(blockposition); - - if (!iblockdata.isAir() && !iblockdata.is(BlockTags.DRAGON_TRANSPARENT)) { -- if (this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { -+ if ((this.level().purpurConfig.enderDragonBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { // Purpur - // CraftBukkit start - Add blocks to list rather than destroying them - // flag1 = this.level().removeBlock(blockposition, false) || flag1; - flag1 = true; -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index b395d5c85f26ca4252d3f8f886b2ee5a7892af5f..17432b434c899a4d075e4619b4a2bfe3d5761b49 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -485,7 +485,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - - if (this.destroyBlocksTick > 0) { - --this.destroyBlocksTick; -- if (this.destroyBlocksTick == 0 && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.destroyBlocksTick == 0 && (this.level().purpurConfig.witherBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur - i = Mth.floor(this.getY()); - j = Mth.floor(this.getX()); - int i1 = Mth.floor(this.getZ()); -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index f49cbb9fcd13afc10aa76ac80128162a902ae345..65a6073a976ce0924ce3a9c7a34253b7e5a9fcec 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -542,7 +542,15 @@ public class EnderMan extends Monster implements NeutralMob { - @Override - public boolean canUse() { - if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur -- return this.enderman.getCarriedBlock() == null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0); -+ // Purpur start -+ if (this.enderman.getCarriedBlock() == null) { -+ return false; -+ } -+ if (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !this.enderman.level().purpurConfig.endermanBypassMobGriefing) { -+ return false; -+ } -+ return this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; -+ // Purpur end - } - - @Override -@@ -588,7 +596,15 @@ public class EnderMan extends Monster implements NeutralMob { - @Override - public boolean canUse() { - if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur -- return this.enderman.getCarriedBlock() != null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0); -+ // Purpur start -+ if (this.enderman.getCarriedBlock() != null) { -+ return false; -+ } -+ if (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !this.enderman.level().purpurConfig.endermanBypassMobGriefing) { -+ return false; -+ } -+ return this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; -+ // Purpur end - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -index f71a70a72ba0bb01c26291ad9dbdd3b01bbf6ee7..ea2a2c56f94d03b73a5e3a06f91aee7fcb0867f4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -341,7 +341,7 @@ public class Evoker extends SpellcasterIllager { - return false; - } else if (Evoker.this.tickCount < this.nextAttackTickCount) { - return false; -- } else if (!Evoker.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ } else if (!Evoker.this.level().purpurConfig.evokerBypassMobGriefing && !Evoker.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - return false; - } else { - List list = Evoker.this.level().getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0D, 4.0D, 16.0D)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index 92b14a6056dfbf07852fc0ee0e3eaf7b4c1657a4..ab13d20dda5a8af471fd459950e7c3e2215e59db 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -193,7 +193,7 @@ public class Ravager extends Raider { - this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(Mth.lerp(0.1D, d1, d0)); - } - -- if (this.horizontalCollision && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.horizontalCollision && (this.level().purpurConfig.ravagerBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur - boolean flag = false; - AABB axisalignedbb = this.getBoundingBox().inflate(0.2D); - Iterator iterator = BlockPos.betweenClosed(Mth.floor(axisalignedbb.minX), Mth.floor(axisalignedbb.minY), Mth.floor(axisalignedbb.minZ), Mth.floor(axisalignedbb.maxX), Mth.floor(axisalignedbb.maxY), Mth.floor(axisalignedbb.maxZ)).iterator(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -index 43b330e263ebe20b76629c350bf7773220f6583e..8f813469ece9f57b19335a155e97d73ed8ce1478 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -@@ -208,7 +208,7 @@ public class Silverfish extends Monster { - continue; - } - // CraftBukkit end -- if (world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (world.purpurConfig.silverfishBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - world.destroyBlock(blockposition1, true, this.silverfish); - } else { - world.setBlock(blockposition1, ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)), 3); -@@ -246,7 +246,7 @@ public class Silverfish extends Monster { - } else { - RandomSource randomsource = this.mob.getRandom(); - -- if (this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) { -+ if ((this.mob.level().purpurConfig.silverfishBypassMobGriefing || this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && randomsource.nextInt(reducedTickDelay(10)) == 0) { // Purpur - this.selectedDirection = Direction.getRandom(randomsource); - BlockPos blockposition = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ()).relative(this.selectedDirection); - BlockState iblockdata = this.mob.level().getBlockState(blockposition); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index e45c061931c5ca03e204b78e60010a906d3ec945..e2e9545a583deb1e2bbdcb76e459d8bd481d5d77 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -429,7 +429,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - - @Override - public boolean wantsToPickUp(ItemStack stack) { -- return this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); -+ return (this.level().purpurConfig.piglinBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); - } - - protected boolean canReplaceCurrentItem(ItemStack stack) { -diff --git a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java -index 251e6bc87eb02ec4372062ef22b21249ac5164ff..6b60597a0e837054b0d1891c1e6e5e7cd3af9539 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java -@@ -16,20 +16,20 @@ public class LargeFireball extends Fireball { - - public LargeFireball(EntityType type, Level world) { - super(type, world); -- isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit -+ isIncendiary = this.level().purpurConfig.fireballsBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur - } - - public LargeFireball(Level world, LivingEntity owner, double velocityX, double velocityY, double velocityZ, int explosionPower) { - super(EntityType.FIREBALL, owner, velocityX, velocityY, velocityZ, world); - this.explosionPower = explosionPower; -- isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit -+ isIncendiary = this.level().purpurConfig.fireballsBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur - } - - @Override - protected void onHit(HitResult hitResult) { - super.onHit(hitResult); - if (!this.level().isClientSide) { -- boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ boolean flag = this.level().purpurConfig.fireballsBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - - // CraftBukkit start - fire ExplosionPrimeEvent - ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity()); -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -index 459aee61b519a40d9136546c0d9356562f5757c8..134b5758e5c6335a52ffb2703c3fa98dc792948e 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -319,6 +319,6 @@ public abstract class Projectile extends Entity implements TraceableEntity { - public boolean mayInteract(Level world, BlockPos pos) { - Entity entity = this.getOwner(); - -- return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ return entity instanceof Player ? entity.mayInteract(world, pos) : entity == null || world.purpurConfig.projectilesBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); - } - } -diff --git a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java -index 04c2ea1ff44af72ae48e2d6b7b912b1c14285038..7839d856b3f924f0c87b298d9a1e3b15ab0693d6 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java -@@ -24,7 +24,7 @@ public class SmallFireball extends Fireball { - super(EntityType.SMALL_FIREBALL, owner, velocityX, velocityY, velocityZ, world); - // CraftBukkit start - if (this.getOwner() != null && this.getOwner() instanceof Mob) { -- isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ isIncendiary = this.level().purpurConfig.fireballsBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - } - // CraftBukkit end - } -diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java -index 57fdcdaf54fd1c92a6e51a3a81789029096e5abe..822dd4265ce02252afadbc652064450b7dfd7e0d 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raider.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java -@@ -319,7 +319,7 @@ public abstract class Raider extends PatrollingMonster { - - @Override - public boolean canUse() { -- if (!this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items -+ if ((!this.mob.level().purpurConfig.pillagerBypassMobGriefing && !this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur - Raid raid = this.mob.getCurrentRaid(); - - if (this.mob.hasActiveRaid() && !this.mob.getCurrentRaid().isOver() && this.mob.canBeLeader() && !ItemStack.matches(this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getLeaderBannerInstance())) { -diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java -index decb8caa67bc5f4525e4d92fedf465a17171fceb..5ba5a510b30917235e135ccda1b7c98cb7dd93ec 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -168,7 +168,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { - @Override - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { - if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper -- if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit -+ if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), (!world.purpurConfig.ravagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)))) { // CraftBukkit // Purpur - world.destroyBlock(pos, true, entity); - } - -diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -index 7efae60a9a9269703a1fa7cd280b5b96557ec73e..e7b5f54d2fc53878e9466ccca972df21f10a7d67 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -101,7 +101,7 @@ public class FarmBlock extends Block { - @Override - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { - super.fallOn(world, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage. -- if (!world.isClientSide && world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { -+ if (!world.isClientSide && world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || world.purpurConfig.farmlandBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur - // CraftBukkit start - Interact soil - org.bukkit.event.Cancellable cancellable; - if (entity instanceof Player) { -diff --git a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java -index 7e04ecba2a14be0f0d47c917368abd2a2bd64a05..5c944e39e611201c6ae4dad14511a54c451204f6 100644 ---- a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java -@@ -72,7 +72,7 @@ public class PowderSnowBlock extends Block implements BucketPickup { - if (!world.isClientSide) { - // CraftBukkit start - if (entity.isOnFire() && entity.mayInteract(world, pos)) { -- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player))) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !((world.purpurConfig.powderSnowBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || entity instanceof Player))) { - return; - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -index e53294e9c4bb3e832acbdc82bebc0df94523c7e4..86360fad47e4cda64b02ce4c8c0227649ed69a5d 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -211,7 +211,7 @@ public class TurtleEggBlock extends Block { - } - if (entity instanceof Player) return true; - -- return world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ return world.purpurConfig.turtleEggsBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); - // Purpur end - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2606c631d93c8e7b99b0ac1fffffa6b0ce388679..ce4855a70ad684c33420bf84c701fb46ad46e021 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -115,8 +115,11 @@ public class PurpurWorldConfig { - public boolean boatsDoFallDamage = false; - public boolean disableDropsOnCrammingDeath = false; - public boolean entitiesCanUsePortals = true; -+ public boolean entitiesPickUpLootBypassMobGriefing = false; -+ public boolean fireballsBypassMobGriefing = false; - public boolean milkCuresBadOmen = true; - public boolean persistentDroppableEntityDisplayNames = true; -+ public boolean projectilesBypassMobGriefing = false; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; -@@ -128,8 +131,11 @@ public class PurpurWorldConfig { - boatsDoFallDamage = getBoolean("gameplay-mechanics.boat.do-fall-damage", boatsDoFallDamage); - disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); - entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); -+ entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing); -+ fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); -+ projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); -@@ -448,9 +454,11 @@ public class PurpurWorldConfig { - dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils); - } - -+ public boolean farmlandBypassMobGriefing = false; - public boolean farmlandGetsMoistFromBelow = false; - public boolean farmlandAlpha = false; - private void farmlandSettings() { -+ farmlandBypassMobGriefing = getBoolean("blocks.farmland.bypass-mob-griefing", farmlandBypassMobGriefing); - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); - farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha); - } -@@ -475,6 +483,11 @@ public class PurpurWorldConfig { - lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether); - } - -+ public boolean powderSnowBypassMobGriefing = false; -+ private void powderSnowSettings() { -+ powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing); -+ } -+ - public boolean respawnAnchorExplode = true; - public double respawnAnchorExplosionPower = 5.0D; - public boolean respawnAnchorExplosionFire = true; -@@ -504,10 +517,12 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; -+ public boolean turtleEggsBypassMobGriefing = false; - private void turtleEggSettings() { - turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs); - turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems); - turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); -+ turtleEggsBypassMobGriefing = getBoolean("blocks.turtle_egg.bypass-mob-griefing", turtleEggsBypassMobGriefing); - } - - public int waterInfiniteRequiredSources = 2; -@@ -741,6 +756,7 @@ public class PurpurWorldConfig { - public double creeperMaxHealth = 20.0D; - public double creeperChargedChance = 0.0D; - public boolean creeperAllowGriefing = true; -+ public boolean creeperBypassMobGriefing = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -753,6 +769,7 @@ public class PurpurWorldConfig { - creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth); - creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance); - creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing); -+ creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing); - } - - public boolean dolphinRidable = false; -@@ -847,6 +864,7 @@ public class PurpurWorldConfig { - public double enderDragonMaxY = 320D; - public double enderDragonMaxHealth = 200.0D; - public boolean enderDragonAlwaysDropsFullExp = false; -+ public boolean enderDragonBypassMobGriefing = false; - private void enderDragonSettings() { - enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); - enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); -@@ -863,6 +881,7 @@ public class PurpurWorldConfig { - } - enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth); - enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp); -+ enderDragonBypassMobGriefing = getBoolean("mobs.ender_dragon.bypass-mob-griefing", enderDragonBypassMobGriefing); - } - - public boolean endermanRidable = false; -@@ -871,6 +890,7 @@ public class PurpurWorldConfig { - public double endermanMaxHealth = 40.0D; - public boolean endermanAllowGriefing = true; - public boolean endermanDespawnEvenWithBlock = false; -+ public boolean endermanBypassMobGriefing = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -883,6 +903,7 @@ public class PurpurWorldConfig { - endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); - endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); - endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock); -+ endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing); - } - - public boolean endermiteRidable = false; -@@ -905,6 +926,7 @@ public class PurpurWorldConfig { - public boolean evokerRidableInWater = true; - public boolean evokerControllable = true; - public double evokerMaxHealth = 24.0D; -+ public boolean evokerBypassMobGriefing = false; - private void evokerSettings() { - evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); - evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); -@@ -915,6 +937,7 @@ public class PurpurWorldConfig { - set("mobs.evoker.attributes.max_health", oldValue); - } - evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); -+ evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing); - } - - public boolean foxRidable = false; -@@ -923,6 +946,7 @@ public class PurpurWorldConfig { - public double foxMaxHealth = 10.0D; - public boolean foxTypeChangesWithTulips = false; - public int foxBreedingTicks = 6000; -+ public boolean foxBypassMobGriefing = false; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -935,6 +959,7 @@ public class PurpurWorldConfig { - foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth); - foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips); - foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks); -+ foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing); - } - - public boolean frogRidable = false; -@@ -1382,6 +1407,7 @@ public class PurpurWorldConfig { - public boolean piglinRidableInWater = true; - public boolean piglinControllable = true; - public double piglinMaxHealth = 16.0D; -+ public boolean piglinBypassMobGriefing = false; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -1392,6 +1418,7 @@ public class PurpurWorldConfig { - set("mobs.piglin.attributes.max_health", oldValue); - } - piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); -+ piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); - } - - public boolean piglinBruteRidable = false; -@@ -1414,6 +1441,7 @@ public class PurpurWorldConfig { - public boolean pillagerRidableInWater = true; - public boolean pillagerControllable = true; - public double pillagerMaxHealth = 24.0D; -+ public boolean pillagerBypassMobGriefing = false; - private void pillagerSettings() { - pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); - pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); -@@ -1424,6 +1452,7 @@ public class PurpurWorldConfig { - set("mobs.pillager.attributes.max_health", oldValue); - } - pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); -+ pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing); - } - - public boolean polarBearRidable = false; -@@ -1470,6 +1499,7 @@ public class PurpurWorldConfig { - public double rabbitNaturalToast = 0.0D; - public double rabbitNaturalKiller = 0.0D; - public int rabbitBreedingTicks = 6000; -+ public boolean rabbitBypassMobGriefing = false; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -@@ -1483,12 +1513,14 @@ public class PurpurWorldConfig { - rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast); - rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller); - rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks); -+ rabbitBypassMobGriefing = getBoolean("mobs.rabbit.bypass-mob-griefing", rabbitBypassMobGriefing); - } - - public boolean ravagerRidable = false; - public boolean ravagerRidableInWater = false; - public boolean ravagerControllable = true; - public double ravagerMaxHealth = 100.0D; -+ public boolean ravagerBypassMobGriefing = false; - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -@@ -1499,6 +1531,7 @@ public class PurpurWorldConfig { - set("mobs.ravager.attributes.max_health", oldValue); - } - ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); -+ ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing); - } - - public boolean salmonRidable = false; -@@ -1520,6 +1553,7 @@ public class PurpurWorldConfig { - public boolean sheepControllable = true; - public double sheepMaxHealth = 8.0D; - public int sheepBreedingTicks = 6000; -+ public boolean sheepBypassMobGriefing = false; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -1531,6 +1565,7 @@ public class PurpurWorldConfig { - } - sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth); - sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks); -+ sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing); - } - - public boolean shulkerRidable = false; -@@ -1553,6 +1588,7 @@ public class PurpurWorldConfig { - public boolean silverfishRidableInWater = true; - public boolean silverfishControllable = true; - public double silverfishMaxHealth = 8.0D; -+ public boolean silverfishBypassMobGriefing = false; - private void silverfishSettings() { - silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); - silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); -@@ -1563,6 +1599,7 @@ public class PurpurWorldConfig { - set("mobs.silverfish.attributes.max_health", oldValue); - } - silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth); -+ silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing); - } - - public boolean skeletonRidable = false; -@@ -1639,6 +1676,7 @@ public class PurpurWorldConfig { - public int snowGolemSnowBallMax = 20; - public float snowGolemSnowBallModifier = 10.0F; - public double snowGolemAttackDistance = 1.25D; -+ public boolean snowGolemBypassMobGriefing = false; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1656,6 +1694,7 @@ public class PurpurWorldConfig { - snowGolemSnowBallMax = getInt("mobs.snow_golem.max-shoot-interval-ticks", snowGolemSnowBallMax); - snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier); - snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance); -+ snowGolemBypassMobGriefing = getBoolean("mobs.snow_golem.bypass-mob-griefing", snowGolemBypassMobGriefing); - } - - public boolean snifferRidable = false; -@@ -1842,6 +1881,7 @@ public class PurpurWorldConfig { - public int villagerBreedingTicks = 6000; - public boolean villagerClericsFarmWarts = false; - public boolean villagerClericFarmersThrowWarts = true; -+ public boolean villagerBypassMobGriefing = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1858,6 +1898,7 @@ public class PurpurWorldConfig { - villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks); - villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts); - villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts); -+ villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing); - } - - public boolean vindicatorRidable = false; -@@ -1930,6 +1971,7 @@ public class PurpurWorldConfig { - public double witherMaxHealth = 300.0D; - public float witherHealthRegenAmount = 1.0f; - public int witherHealthRegenDelay = 20; -+ public boolean witherBypassMobGriefing = false; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -1947,6 +1989,7 @@ public class PurpurWorldConfig { - witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth); - witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount); - witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay); -+ witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing); - } - - public boolean witherSkeletonRidable = false; -@@ -2018,6 +2061,7 @@ public class PurpurWorldConfig { - public double zombieJockeyChance = 0.05D; - public boolean zombieJockeyTryExistingChickens = true; - public boolean zombieAggressiveTowardsVillagerWhenLagging = true; -+ public boolean zombieBypassMobGriefing = false; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -2033,6 +2077,7 @@ public class PurpurWorldConfig { - zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance); - zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens); - zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging); -+ zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing); - } - - public boolean zombieHorseRidableInWater = false; diff --git a/patches/server/0124-Config-to-allow-Note-Block-sounds-when-blocked.patch b/patches/server/0124-Config-to-allow-Note-Block-sounds-when-blocked.patch deleted file mode 100644 index 5ef3744f3..000000000 --- a/patches/server/0124-Config-to-allow-Note-Block-sounds-when-blocked.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Fri, 8 Jan 2021 16:07:32 -0500 -Subject: [PATCH] Config to allow Note Block sounds when blocked - -Allows for Note Blocks to ignore whether or not there's air above them to play. - -Normally, the sounds will only play when the block directly above is air. -With this patch enabled, players can place any block above the Note Block and it will still work. - -diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java -index e46d84750bdd7c940f400efda226e12a3fdc3848..6343cd0c33cafb30225cfae17ea1cf15859073b1 100644 ---- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java -@@ -87,7 +87,7 @@ public class NoteBlock extends Block { - } - - private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) { -- if (((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) { -+ if (world.purpurConfig.noteBlockIgnoreAbove || ((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) { // Purpur - // CraftBukkit start - // org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE)); - // if (event.isCancelled()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ce4855a70ad684c33420bf84c701fb46ad46e021..028b6aa20c2d83d8a4eb7dabccc4f2451723000e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -118,6 +118,7 @@ public class PurpurWorldConfig { - public boolean entitiesPickUpLootBypassMobGriefing = false; - public boolean fireballsBypassMobGriefing = false; - public boolean milkCuresBadOmen = true; -+ public boolean noteBlockIgnoreAbove = false; - public boolean persistentDroppableEntityDisplayNames = true; - public boolean projectilesBypassMobGriefing = false; - public double tridentLoyaltyVoidReturnHeight = 0.0D; -@@ -134,6 +135,7 @@ public class PurpurWorldConfig { - entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing); - fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); -+ noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); diff --git a/patches/server/0125-Add-EntityTeleportHinderedEvent.patch b/patches/server/0125-Add-EntityTeleportHinderedEvent.patch deleted file mode 100644 index 73ec2dd3d..000000000 --- a/patches/server/0125-Add-EntityTeleportHinderedEvent.patch +++ /dev/null @@ -1,125 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Mariell Hoversholm -Date: Sat, 9 Jan 2021 15:27:46 +0100 -Subject: [PATCH] Add EntityTeleportHinderedEvent - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -diff --git a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -index 41d7cff39fc37955877668337689b4b26cd8c7cf..f80f6da484f4144e743079e5104bf503419074b2 100644 ---- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -@@ -46,6 +46,14 @@ public class EndPortalBlock extends BaseEntityBlock { - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { - if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - if (world instanceof ServerLevel && entity.canChangeDimensions() && Shapes.joinIsNotEmpty(Shapes.create(entity.getBoundingBox().move((double) (-pos.getX()), (double) (-pos.getY()), (double) (-pos.getZ()))), state.getShape(world, pos), BooleanOp.AND)) { -+ // Purpur start -+ if (entity.isPassenger() || entity.isVehicle()) { -+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) { -+ this.entityInside(state, world, pos, entity); -+ } -+ return; -+ } -+ // Purpur end - ResourceKey resourcekey = world.getTypeKey() == LevelStem.END ? Level.OVERWORLD : Level.END; // CraftBukkit - SPIGOT-6152: send back to main overworld in custom ends - ServerLevel worldserver = ((ServerLevel) world).getServer().getLevel(resourcekey); - -diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -index a6ab0d0defc05e56a91084c49897059670a1324b..2c085c4a154cb0f8a1d38453f43474a764398784 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -@@ -84,6 +84,14 @@ public class NetherPortalBlock extends Block { - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { - if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - if (entity.canChangeDimensions()) { -+ // Purpur start -+ if (entity.isPassenger() || entity.isVehicle()) { -+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL).callEvent()) { -+ this.entityInside(state, world, pos, entity); -+ } -+ return; -+ } -+ // Purpur end - // CraftBukkit start - Entity in portal - EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ())); - world.getCraftServer().getPluginManager().callEvent(event); -diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -index 9717b37aef9f487502e696c209ae209ab3b8f000..7291e4056b8e46ab59b71818388ac55fbb12993f 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java -@@ -178,6 +178,14 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { - public static void teleportEntity(Level world, BlockPos pos, BlockState state, Entity entity, TheEndGatewayBlockEntity blockEntity) { - if (world instanceof ServerLevel && !blockEntity.isCoolingDown()) { - if (!entity.canChangeDimensions()) return; // Purpur -+ // Purpur start -+ if (world.purpurConfig.imposeTeleportRestrictionsOnGateways && (entity.isVehicle() || entity.isPassenger())) { -+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), entity.isPassenger() ? org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_PASSENGER : org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) { -+ teleportEntity(world, pos, state, entity, blockEntity); -+ } -+ return; -+ } -+ // Purpur end - ServerLevel worldserver = (ServerLevel) world; - - blockEntity.teleportCooldown = 100; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 912d9e4b3168cf89bd263ba63e049a791beefc90..283836981f5ed94dfc8999c3c5450213a1f2cfc1 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -601,6 +601,10 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - // Paper end - - if ((!ignorePassengers && this.entity.isVehicle()) || this.entity.isRemoved()) { // Paper - Teleport passenger API -+ // Purpur start -+ if (!entity.isRemoved() && new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) -+ return teleport(location, cause); -+ // Purpur end - return false; - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index c6ede1fac5c68f2912a9c401765bd10b09afe7f9..5965b4e91e63a3d3b64e5193399ae3b6d774120b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -1355,6 +1355,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - } - - if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API -+ // Purpur start -+ if (new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) -+ return teleport(location, cause); -+ // Purpur end - return false; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 028b6aa20c2d83d8a4eb7dabccc4f2451723000e..f047edb4f52efaaa89463af0c4741b404459f6c4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -117,6 +117,7 @@ public class PurpurWorldConfig { - public boolean entitiesCanUsePortals = true; - public boolean entitiesPickUpLootBypassMobGriefing = false; - public boolean fireballsBypassMobGriefing = false; -+ public boolean imposeTeleportRestrictionsOnGateways = false; - public boolean milkCuresBadOmen = true; - public boolean noteBlockIgnoreAbove = false; - public boolean persistentDroppableEntityDisplayNames = true; -@@ -134,6 +135,7 @@ public class PurpurWorldConfig { - entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); - entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing); - fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); -+ imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); diff --git a/patches/server/0126-Farmland-trampling-changes.patch b/patches/server/0126-Farmland-trampling-changes.patch deleted file mode 100644 index cd4179100..000000000 --- a/patches/server/0126-Farmland-trampling-changes.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Mariell Hoversholm -Date: Sat, 9 Jan 2021 16:06:40 +0100 -Subject: [PATCH] Farmland trampling changes - -This lets us choose if farmland trampling is fully disabled or only -players may trample farmland. - -This lets us choose if entities can stop trampling if they fall a -distance equal to their feather falling level, plus the extra block -necessary to trample in the first place. Feather Falling 1 requires -you to fall over 3+ blocks to trample. FF 2 requires 4+, etc. - -diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -index e7b5f54d2fc53878e9466ccca972df21f10a7d67..56e66fcd840d0d3681ba671d1ffc51b232631461 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -116,12 +116,20 @@ public class FarmBlock extends Block { - } - - // Purpur start -+ if (world.purpurConfig.farmlandTramplingDisabled) return; -+ if (world.purpurConfig.farmlandTramplingOnlyPlayers && !(entity instanceof Player)) return; - if (world.purpurConfig.farmlandAlpha) { - Block block = world.getBlockState(pos.below()).getBlock(); - if (block instanceof FenceBlock || block instanceof WallBlock) { - return; - } - } -+ if (world.purpurConfig.farmlandTramplingFeatherFalling) { -+ Iterator armor = entity.getArmorSlots().iterator(); -+ if (armor.hasNext() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FALL_PROTECTION, armor.next()) >= (int) entity.fallDistance) { -+ return; -+ } -+ } - // Purpur end - if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { - return; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f047edb4f52efaaa89463af0c4741b404459f6c4..afd1db5d51690191ad3836dfe8de305b1c1a29f0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -461,10 +461,16 @@ public class PurpurWorldConfig { - public boolean farmlandBypassMobGriefing = false; - public boolean farmlandGetsMoistFromBelow = false; - public boolean farmlandAlpha = false; -+ public boolean farmlandTramplingDisabled = false; -+ public boolean farmlandTramplingOnlyPlayers = false; -+ public boolean farmlandTramplingFeatherFalling = false; - private void farmlandSettings() { - farmlandBypassMobGriefing = getBoolean("blocks.farmland.bypass-mob-griefing", farmlandBypassMobGriefing); - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); - farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha); -+ farmlandTramplingDisabled = getBoolean("blocks.farmland.disable-trampling", farmlandTramplingDisabled); -+ farmlandTramplingOnlyPlayers = getBoolean("blocks.farmland.only-players-trample", farmlandTramplingOnlyPlayers); -+ farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling); - } - - public boolean furnaceUseLavaFromUnderneath = false; diff --git a/patches/server/0127-Movement-options-for-armor-stands.patch b/patches/server/0127-Movement-options-for-armor-stands.patch deleted file mode 100644 index 72a3ff6d2..000000000 --- a/patches/server/0127-Movement-options-for-armor-stands.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Mariell Hoversholm -Date: Sat, 9 Jan 2021 22:22:59 +0100 -Subject: [PATCH] Movement options for armor stands - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 105b093cd0353a45197615e9fcdf06e72a0aad00..e45f4570ef8334f319dcee39e2f343b91da62fce 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1876,7 +1876,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return this.isInWater() || flag; - } - -- void updateInWaterStateAndDoWaterCurrentPushing() { -+ public void updateInWaterStateAndDoWaterCurrentPushing() { // Purpur - package-private -> public - Entity entity = this.getVehicle(); - - if (entity instanceof Boat) { -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -index 76222084727c269d376d0df5702204c0bdf59d81..b48a3d318a14889c68a1de5c1997b1090aab4029 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -99,10 +99,12 @@ public class ArmorStand extends LivingEntity { - private boolean noTickPoseDirty = false; - private boolean noTickEquipmentDirty = false; - // Paper end -+ public boolean canMovementTick = true; // Purpur - - public ArmorStand(EntityType type, Level world) { - super(type, world); - if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - armour stand ticking -+ if (world != null) this.canMovementTick = world.purpurConfig.armorstandMovement; // Purpur - this.handItems = NonNullList.withSize(2, ItemStack.EMPTY); - this.armorItems = NonNullList.withSize(4, ItemStack.EMPTY); - this.headPose = ArmorStand.DEFAULT_HEAD_POSE; -@@ -1017,4 +1019,18 @@ public class ArmorStand extends LivingEntity { - } - // Paper end - // Paper end -+ -+ // Purpur start -+ @Override -+ public void updateInWaterStateAndDoWaterCurrentPushing() { -+ if (this.level().purpurConfig.armorstandWaterMovement && -+ (this.level().purpurConfig.armorstandWaterFence || !(level().getBlockState(blockPosition().below()).getBlock() instanceof net.minecraft.world.level.block.FenceBlock))) -+ super.updateInWaterStateAndDoWaterCurrentPushing(); -+ } -+ -+ @Override -+ public void aiStep() { -+ if (this.canMovementTick && this.canMove) super.aiStep(); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index afd1db5d51690191ad3836dfe8de305b1c1a29f0..14e708b579fc8cbb648fc01d96439a05a6abe239 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -99,10 +99,16 @@ public class PurpurWorldConfig { - public float armorstandStepHeight = 0.0F; - public boolean armorstandSetNameVisible = true; - public boolean armorstandFixNametags = false; -+ public boolean armorstandMovement = true; -+ public boolean armorstandWaterMovement = true; -+ public boolean armorstandWaterFence = true; - private void armorstandSettings() { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible); - armorstandFixNametags = getBoolean("gameplay-mechanics.armorstand.fix-nametags", armorstandFixNametags); -+ armorstandMovement = getBoolean("gameplay-mechanics.armorstand.can-movement-tick", armorstandMovement); -+ armorstandWaterMovement = getBoolean("gameplay-mechanics.armorstand.can-move-in-water", armorstandWaterMovement); -+ armorstandWaterFence = getBoolean("gameplay-mechanics.armorstand.can-move-in-water-over-fence", armorstandWaterFence); - } - - public boolean arrowMovementResetsDespawnCounter = true; diff --git a/patches/server/0128-Fix-stuck-in-portals.patch b/patches/server/0128-Fix-stuck-in-portals.patch deleted file mode 100644 index b8817852b..000000000 --- a/patches/server/0128-Fix-stuck-in-portals.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 14 Jan 2021 16:48:10 -0600 -Subject: [PATCH] Fix stuck in portals - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index de7c199c0df87a1c1a0157328fa52351f74a0d3d..540c7b650deefa4a1a0e4f86650d9f3c56091566 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1272,6 +1272,7 @@ public class ServerPlayer extends Player { - playerlist.sendPlayerPermissionLevel(this); - worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); - this.unsetRemoved(); -+ this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur - - // CraftBukkit end - this.setServerLevel(worldserver); -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e45f4570ef8334f319dcee39e2f343b91da62fce..36e9b635f81f2799cd210075bced9e976a90a351 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3060,12 +3060,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return Vec3.directionFromRotation(this.getRotationVector()); - } - -+ public BlockPos portalPos = BlockPos.ZERO; // Purpur - public void handleInsidePortal(BlockPos pos) { - if (this.isOnPortalCooldown()) { -+ if (!(level().purpurConfig.playerFixStuckPortal && this instanceof Player && !pos.equals(portalPos))) // Purpur - this.setPortalCooldown(); - } else if (level().purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - if (!this.level().isClientSide && !pos.equals(this.portalEntrancePos)) { - this.portalEntrancePos = pos.immutable(); -+ portalPos = BlockPos.ZERO; // Purpur - } - - this.isInsidePortal = true; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 14e708b579fc8cbb648fc01d96439a05a6abe239..c72af60345ac9704118ff9d19295d1a663a1e985 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -335,6 +335,7 @@ public class PurpurWorldConfig { - public int playerDeathExpDropMax = 100; - public boolean teleportIfOutsideBorder = false; - public boolean totemOfUndyingWorksInInventory = false; -+ public boolean playerFixStuckPortal = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -352,6 +353,7 @@ public class PurpurWorldConfig { - playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); - teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); - totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); -+ playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0129-Toggle-for-water-sensitive-mob-damage.patch b/patches/server/0129-Toggle-for-water-sensitive-mob-damage.patch deleted file mode 100644 index 771daf822..000000000 --- a/patches/server/0129-Toggle-for-water-sensitive-mob-damage.patch +++ /dev/null @@ -1,2342 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Fri, 5 Feb 2021 01:11:22 +0100 -Subject: [PATCH] Toggle for water sensitive mob damage - - -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index b1ba0f24dd6f1ec4c60208564e4eb84bdcd457f4..1a67f46b57e398d23fbc495ee81ae62e0d84d3dc 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -46,6 +46,11 @@ public class GlowSquid extends Squid { - return this.level().purpurConfig.glowSquidsCanFly; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.glowSquidTakeDamageFromWater; -+ } -+ - @Override - protected ParticleOptions getInkParticle() { - return ParticleTypes.GLOW_SQUID_INK; -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index c283900e6c43fda62428a6e6d8b70e512458e779..c3d1d77a5703db4e1e3eb38fdd0b8903f691bf25 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -276,6 +276,11 @@ public class Bat extends AmbientCreature { - this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level().purpurConfig.batAttackKnockback); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.batTakeDamageFromWater; -+ } -+ - @Override - public void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index fd917f72b4b4d4af1ae36feab38deac2657c95d4..bfafbc90c710514e1a5699f76381f593c8790c3b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -180,7 +180,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - // Paper end - this.lookControl = new Bee.BeeLookControl(this); - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F); -- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur - this.setPathfindingMalus(BlockPathTypes.WATER_BORDER, 16.0F); - this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F); - this.setPathfindingMalus(BlockPathTypes.FENCE, -1.0F); -@@ -481,6 +481,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return this.level().purpurConfig.beeBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.beeTakeDamageFromWater; -+ } -+ - @Override - public int getRemainingPersistentAngerTime() { - return (Integer) this.entityData.get(Bee.DATA_REMAINING_ANGER_TIME); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 956f125c9ae62537570bb9870be66872f3519f6a..1554e4a0e068e06ec121adf56421f8332132f062 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -132,6 +132,11 @@ public class Cat extends TamableAnimal implements VariantHolder { - return this.level().purpurConfig.catBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.catTakeDamageFromWater; -+ } -+ - public ResourceLocation getResourceLocation() { - return this.getVariant().texture(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index 86b910cbb8d0e19dc9ae53078e730495bf4a3b87..6fe3e62ba4534e821937baa14d728186f8c6439e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -84,6 +84,11 @@ public class Chicken extends Animal { - return this.level().purpurConfig.chickenBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.chickenTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index 3d61c2d5da103de68242c16d85c703813979d179..2343325fa9a771de7b9445cda24a2bcd7a7c1761 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -30,6 +30,11 @@ public class Cod extends AbstractSchoolingFish { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.codMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.codTakeDamageFromWater; -+ } -+ - @Override - public ItemStack getBucketItemStack() { - return new ItemStack(Items.COD_BUCKET); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index f9cce30eef4a5bffeb27c48a6c7ee361803560c5..8d714c029cf3b62af1ff8140a82ed3ae463e0e18 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -71,6 +71,11 @@ public class Cow extends Animal { - return this.level().purpurConfig.cowBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.cowTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index 079fd78528377ee4236fb2e7189a5f0fc5ec4fb3..77746eeffdc612793a6c907f222753bce5cd0ed4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -163,6 +163,11 @@ public class Dolphin extends WaterAnimal { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.dolphinMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.dolphinTakeDamageFromWater; -+ } -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index 2a6f6753ce9b7ab899256edc181a92c19021822b..ed7d057bc12ca0e3d808320a008c902ebb27e1ca 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -191,6 +191,11 @@ public class Fox extends Animal implements VariantHolder { - return this.level().purpurConfig.foxBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.foxTakeDamageFromWater; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 95ff109511c97d603aeaf9e73c49397a841fcbce..58539d29b8e2a8c0676cc574f64e7d61613e1451 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -88,6 +88,11 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - } - // Purpur end - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.ironGolemTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 7fd50f60aab2fa49a8227f313352a2ee251904c8..56fb35206af7e04a78eba489d444135176188a20 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -90,6 +90,11 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - public int getPurpurBreedTime() { - return this.level().purpurConfig.rabbitBreedingTicks; - } -+ -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.rabbitTakeDamageFromWater; -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 742805994f29a18af444912b10af631d2c60cacf..e101c3bf425902908c43ffa18867fb83a5e1f16e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -30,6 +30,11 @@ public class Salmon extends AbstractSchoolingFish { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.salmonMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.salmonTakeDamageFromWater; -+ } -+ - @Override - public int getMaxSchoolSize() { - return 5; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index c3b79c109e895cb4460571c8816b210455126f1d..35decea07efa8ca9f7ed896be9b8f3eb5afbf082 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -144,6 +144,11 @@ public class Sheep extends Animal implements Shearable { - return this.level().purpurConfig.sheepBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.sheepTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.eatBlockGoal = new EatBlockGoal(this); -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index 84e0a41620fd52af0cd22dc92fd13ee4efd206fb..a1efde792eaa3f80cd0c7c48e38bbd07e1eb5c9d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -116,7 +116,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - - @Override - public boolean isSensitiveToWater() { -- return true; -+ return this.level().purpurConfig.snowGolemTakeDamageFromWater; // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index 80bdc93cba675d6c1286618f14fc33e0344c601f..57b37ac40b4ffe6c5e27548755aaa15da89a0f0b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -90,6 +90,11 @@ public class Squid extends WaterAnimal { - return this.wasTouchingWater || canFly(); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.squidTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -index 7d02e552a27632939bb9c40a62f4e0df7bd60bbc..b16d075581a352714f86f1b87805f24c9e336aa3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -@@ -59,6 +59,11 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.tropicalFishMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.tropicalFishTakeDamageFromWater; -+ } -+ - public static String getPredefinedName(int variant) { - return "entity.minecraft.tropical_fish.predefined." + variant; - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index 4a5cc7eaac2bc093a0f5a457b93b6f4560d739ca..df9a7975959393ab2e81fa7c3878afd034ef7f90 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -110,6 +110,11 @@ public class Turtle extends Animal { - return this.level().purpurConfig.turtleBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.turtleTakeDamageFromWater; -+ } -+ - public void setHomePos(BlockPos pos) { - this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index 8d71d0ad4bf8e0df8ded9ebce6f39e66dd632bb5..f3c3db958c359b3b032bd54c7732f16fce108ec3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -210,6 +210,11 @@ public class Wolf extends TamableAnimal implements NeutralMob { - super.tame(player); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.wolfTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index acf44bf3d22d7b2ac7a02b3167cf577403942908..6aaf20501558f1710be0adca9c14441448698541 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -125,6 +125,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { - return this.modelRotationValues; -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index d7580f06f2f619cb283faa61bdd4e7cc127fc08b..83a3a87caba237b382e4409339e98b6b99a005b7 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -112,6 +112,11 @@ public class Goat extends Animal { - return this.level().purpurConfig.goatBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.goatTakeDamageFromWater; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -index 6932d0eafbeca9de80728c6b0ae42340d2297ad0..af4bac165cbc39fb6959983a1116a6fb65af0ecb 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -@@ -42,6 +42,11 @@ public class Donkey extends AbstractChestedHorse { - return this.level().purpurConfig.donkeyBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.donkeyTakeDamageFromWater; -+ } -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.DONKEY_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -index 0251ded48eb5bdf96f0e67f6456aa15909c8a4ff..f8dbea402f723bf38d7ab3f2468d1b02b7124560 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -@@ -67,6 +67,11 @@ public class Horse extends AbstractHorse implements VariantHolder { - return this.level().purpurConfig.horseBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.horseTakeDamageFromWater; -+ } -+ - @Override - protected void randomizeAttributes(RandomSource random) { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index ad9d061979f5856cba9769b91357b3c75cd9a794..b7450d182ecf10ef17ac9eb3fce04e4334013c91 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -143,6 +143,11 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, Level world) { - super(type, world); - this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur -- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur - this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F); - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F); - this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F); -@@ -143,7 +143,7 @@ public class Blaze extends Monster { - - @Override - public boolean isSensitiveToWater() { -- return true; -+ return this.level().purpurConfig.blazeTakeDamageFromWater; // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -index 2f7328286399a556f5a3862d71f2c6931f82d9d1..aed77678edfeeeb646804a1c3b32c4ae8672074d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -@@ -50,6 +50,11 @@ public class CaveSpider extends Spider { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.caveSpiderMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.caveSpiderTakeDamageFromWater; -+ } -+ - @Override - public boolean doHurtTarget(Entity target) { - if (super.doHurtTarget(target)) { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index 5bfeb39a23a72f57523a3c2db1d79c7aef70eb02..e8191f0df3420d5a531cf226547a177732ff65ea 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -263,6 +263,11 @@ public class Creeper extends Monster implements PowerableMob { - return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.creeperTakeDamageFromWater; -+ } -+ - @Override - protected SoundEvent getHurtSound(DamageSource source) { - return SoundEvents.CREEPER_HURT; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 64acb2e81ef65acb0d41db8b5f7c924c2e2a5e00..902f831ea001145f73691b96f2fca8245a4fc05a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -95,6 +95,11 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.drownedSpawnReinforcements); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.drownedTakeDamageFromWater; -+ } -+ - @Override - public boolean jockeyOnlyBaby() { - return level().purpurConfig.drownedJockeyOnlyBaby; -diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -index 48c28b5177c26c8ab07bb4960a71cddb7d4f543d..b494d5689beb19d621f5d7c9c22d84e12d303fa0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -@@ -50,6 +50,11 @@ public class ElderGuardian extends Guardian { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.elderGuardianMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.elderGuardianTakeDamageFromWater; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index 65a6073a976ce0924ce3a9c7a34253b7e5a9fcec..d4d4ad5dfa776a39eabb840ed5b49acb8d37f587 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -93,7 +93,7 @@ public class EnderMan extends Monster implements NeutralMob { - public EnderMan(EntityType type, Level world) { - super(type, world); - this.setMaxUpStep(1.0F); -- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur - } - - // Purpur start -@@ -306,7 +306,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean isSensitiveToWater() { -- return true; -+ return this.level().purpurConfig.endermanTakeDamageFromWater; // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -index 21b02eaf6018aeab7da57b5274881e8f9ca49998..cb0a2d522176a2d1d9785f95d205d4d924912a32 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -59,6 +59,11 @@ public class Endermite extends Monster { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.endermiteMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.endermiteTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -index ea2a2c56f94d03b73a5e3a06f91aee7fcb0867f4..fbc098c741d6153e8613dc47cc89c88c62174233 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -70,6 +70,11 @@ public class Evoker extends SpellcasterIllager { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.evokerMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.evokerTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -index 61068ae356a9ffc26c5fabc0b2561fda9540ff94..ab6bfdb50ee1bb13a213a2093287f5c2465b2d1b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -@@ -137,6 +137,11 @@ public class Ghast extends FlyingMob implements Enemy { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ghastMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.ghastTakeDamageFromWater; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java -index 12e27b36b3f9949eb644175dd346c487277b2d39..6340d8641770110d61dc9337942bc233e6fb49c6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -74,6 +74,11 @@ public class Giant extends Monster { - } - } - } -+ -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.giantTakeDamageFromWater; -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java -index b3a8e98c6c094654a283387d5bf78fc9db93a85a..41b080c1fd44c93009908fcfd20180e0d11fca8a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java -@@ -98,6 +98,11 @@ public class Guardian extends Monster { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.guardianMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.guardianTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java -index 7e46ebc5c99f018cada50f491200e4fe7b7098d7..31706620960f5f153565f3cf64e32d0f4d10feb8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -64,6 +64,11 @@ public class Husk extends Zombie { - return level().purpurConfig.huskJockeyTryExistingChickens; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.huskTakeDamageFromWater; -+ } -+ - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -index 0f3e6c19e55a18cde71f3cb84c1e92b433ebcb7f..91294d87521c884c402485b923691f9fd8985353 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -83,6 +83,11 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.illusionerMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.illusionerTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index b472309f97b24f1d7b97d8b6d464c479c2d602d5..e0ebc4c2d8dd718ce78d981a1d099e7482221f1f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -67,6 +67,11 @@ public class MagmaCube extends Slime { - return level().purpurConfig.magmaCubeAttackDamageCache; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.magmaCubeTakeDamageFromWater; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 076a881412d40ce6a8f5e5f83f7598bfa08f199c..7c9f6076f68de295e882e69137ac573db8d9698b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -137,6 +137,11 @@ public class Phantom extends FlyingMob implements Enemy { - } - // Purpur end - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.phantomTakeDamageFromWater; -+ } -+ - @Override - public boolean isFlapping() { - return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -index 6a6349c7002439965422aa4979682b4ce6dfba1e..1f64dc8442de75447c17ae4fd5483345d4b7fd3e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -84,6 +84,11 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pillagerMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.pillagerTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index ab13d20dda5a8af471fd459950e7c3e2215e59db..d56e80014621dbd7e445f2f753807bd2b751310d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -96,6 +96,11 @@ public class Ravager extends Raider { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ravagerMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.ravagerTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 7d96ba5adeddf52ed712e320d5f56a37f30e138a..27b05a4657ba201096973640d7a8cbabdd69f053 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -120,6 +120,11 @@ public class Shulker extends AbstractGolem implements VariantHolder type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos blockPos = pos; - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index 4c077c94b654f54465ece2aa45c5ead0a417ad0d..88fc9317aeac79f3352fc8955f8c4f6b3f87eddc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -94,7 +94,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - super(type, world); - this.steering = new ItemBasedSteering(this.entityData, Strider.DATA_BOOST_TIME, Strider.DATA_SADDLE_ID); - this.blocksBuilding = true; -- this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F); // Purpur - this.setPathfindingMalus(BlockPathTypes.LAVA, 0.0F); - this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 0.0F); - this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F); -@@ -444,7 +444,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - - @Override - public boolean isSensitiveToWater() { -- return true; -+ return this.level().purpurConfig.striderTakeDamageFromWater; // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java -index 764fdf4654b4fa2184493b77976fc29aedf592c2..3d79723a6b1d5e099d70bb82917ee551d9f2df7e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -112,6 +112,11 @@ public class Vex extends Monster implements TraceableEntity { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vexMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.vexTakeDamageFromWater; -+ } -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return dimensions.height - 0.28125F; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index 2a5b6b94b15bf066b5722e7a4f782bc87f9debf4..34a6105c04d107278c18f3b2829282b227c5c5c5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -80,6 +80,11 @@ public class Vindicator extends AbstractIllager { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vindicatorMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.vindicatorTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index 289c9261d0019b558c4447a75b3b03229cdd9498..d49813eeaf417806a16ff7d9b7ef8a9c30cfe8d6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -79,6 +79,11 @@ public class Witch extends Raider implements RangedAttackMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witchMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.witchTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index 25e2aca9dafe63ccaf21dc42e79563c5a3c8e638..c6f36715de37c8694860fc3834f203664c264138 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -57,6 +57,11 @@ public class WitherSkeleton extends AbstractSkeleton { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherSkeletonMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.witherSkeletonTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 323ec328226e3a46ef8afeaf07178bb9f4042134..651c4f4b3d9349b8e9d6601a37ca5da192898a61 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -89,6 +89,11 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zoglinMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.zoglinTakeDamageFromWater; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index bd75aa78a7dd437d6fce79a6cb18298184affe75..a676d66dcb5ee72e6d8ffef4e210a3d2c8d605f2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -140,6 +140,11 @@ public class Zombie extends Monster { - return level().purpurConfig.zombieJockeyTryExistingChickens; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.zombieTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 6f3667751e430ff6020a9a26f82a25e4ec043ce6..c3f220a85b91a25662c943b5ee4508cd7a18c75d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -106,6 +106,11 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieVillagerSpawnReinforcements); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.zombieVillagerTakeDamageFromWater; -+ } -+ - @Override - public boolean jockeyOnlyBaby() { - return level().purpurConfig.zombieVillagerJockeyOnlyBaby; -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 67fd554cfd6b848ca1f2cf804ad4543ad2c88845..21ba6ea58dfbfd9a7c0bf7d518d7f12730841a08 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -85,6 +85,11 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.zombifiedPiglinTakeDamageFromWater; -+ } -+ - @Override - public boolean jockeyOnlyBaby() { - return level().purpurConfig.zombifiedPiglinJockeyOnlyBaby; -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index bd72dc99752faca3180ecb165b11406ada1a564e..1fd65b47b1bd56bc09902324b1dd22dad2e0bc2d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -94,6 +94,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return this.level().purpurConfig.hoglinBreedingTicks; - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.hoglinTakeDamageFromWater; -+ } -+ - @Override - public boolean canBeLeashed(Player player) { - return !this.isLeashed(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index e2e9545a583deb1e2bbdcb76e459d8bd481d5d77..83e9873138fb178511119effbe21449c303a5063 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -118,6 +118,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.piglinTakeDamageFromWater; -+ } -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index 04e54c241078e6cd6419a21ba1bf913fd3b413d1..b63d87df0f48dd63c89118f91b31dc4e3622e3b8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -63,6 +63,11 @@ public class PiglinBrute extends AbstractPiglin { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinBruteMaxHealth); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.piglinBruteTakeDamageFromWater; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D); - } -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 605171aedb4f54dcd16837ad1e36097653a26ab6..01a1732ca13300b60979d250ebf156ca68bb014f 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -189,6 +189,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return level().purpurConfig.villagerCanBeLeashed && !this.isLeashed(); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.villagerTakeDamageFromWater; -+ } -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 7413f90dfcb68476f7d514607f38a8965dfa6fd0..5c6f1d3ffa2a2b6990d2406b14679e8286f60729 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -93,6 +93,11 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return level().purpurConfig.wanderingTraderCanBeLeashed && !this.isLeashed(); - } - -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.wanderingTraderTakeDamageFromWater; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c72af60345ac9704118ff9d19295d1a663a1e985..d077e5c5bc108d7ad37caf8339be449fe27960a1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -572,11 +572,13 @@ public class PurpurWorldConfig { - public boolean axolotlControllable = true; - public double axolotlMaxHealth = 14.0D; - public int axolotlBreedingTicks = 6000; -+ public boolean axolotlTakeDamageFromWater = false; - private void axolotlSettings() { - axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); - axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); - axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth); - axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks); -+ axolotlTakeDamageFromWater = getBoolean("mobs.axolotl.takes-damage-from-water", axolotlTakeDamageFromWater); - } - - public boolean batRidable = false; -@@ -591,6 +593,7 @@ public class PurpurWorldConfig { - public double batArmor = 0.0D; - public double batArmorToughness = 0.0D; - public double batAttackKnockback = 0.0D; -+ public boolean batTakeDamageFromWater = false; - private void batSettings() { - batRidable = getBoolean("mobs.bat.ridable", batRidable); - batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater); -@@ -609,6 +612,7 @@ public class PurpurWorldConfig { - batArmor = getDouble("mobs.bat.attributes.armor", batArmor); - batArmorToughness = getDouble("mobs.bat.attributes.armor_toughness", batArmorToughness); - batAttackKnockback = getDouble("mobs.bat.attributes.attack_knockback", batAttackKnockback); -+ batTakeDamageFromWater = getBoolean("mobs.bat.takes-damage-from-water", batTakeDamageFromWater); - } - - public boolean beeRidable = false; -@@ -617,6 +621,7 @@ public class PurpurWorldConfig { - public double beeMaxY = 320D; - public double beeMaxHealth = 10.0D; - public int beeBreedingTicks = 6000; -+ public boolean beeTakeDamageFromWater = false; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -629,6 +634,7 @@ public class PurpurWorldConfig { - } - beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth); - beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks); -+ beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater); - } - - public boolean blazeRidable = false; -@@ -636,6 +642,7 @@ public class PurpurWorldConfig { - public boolean blazeControllable = true; - public double blazeMaxY = 320D; - public double blazeMaxHealth = 20.0D; -+ public boolean blazeTakeDamageFromWater = true; - private void blazeSettings() { - blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); - blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); -@@ -647,6 +654,7 @@ public class PurpurWorldConfig { - set("mobs.blaze.attributes.max_health", oldValue); - } - blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth); -+ blazeTakeDamageFromWater = getBoolean("mobs.blaze.takes-damage-from-water", blazeTakeDamageFromWater); - } - - public boolean camelRidableInWater = false; -@@ -677,6 +685,7 @@ public class PurpurWorldConfig { - public int catSpawnVillageScanRange = 48; - public int catBreedingTicks = 6000; - public DyeColor catDefaultCollarColor = DyeColor.RED; -+ public boolean catTakeDamageFromWater = false; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -696,12 +705,14 @@ public class PurpurWorldConfig { - } catch (IllegalArgumentException ignore) { - catDefaultCollarColor = DyeColor.RED; - } -+ catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater); - } - - public boolean caveSpiderRidable = false; - public boolean caveSpiderRidableInWater = true; - public boolean caveSpiderControllable = true; - public double caveSpiderMaxHealth = 12.0D; -+ public boolean caveSpiderTakeDamageFromWater = false; - private void caveSpiderSettings() { - caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); - caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); -@@ -712,6 +723,7 @@ public class PurpurWorldConfig { - set("mobs.cave_spider.attributes.max_health", oldValue); - } - caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth); -+ caveSpiderTakeDamageFromWater = getBoolean("mobs.cave_spider.takes-damage-from-water", caveSpiderTakeDamageFromWater); - } - - public boolean chickenRidable = false; -@@ -720,6 +732,7 @@ public class PurpurWorldConfig { - public double chickenMaxHealth = 4.0D; - public boolean chickenRetaliate = false; - public int chickenBreedingTicks = 6000; -+ public boolean chickenTakeDamageFromWater = false; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -@@ -732,11 +745,13 @@ public class PurpurWorldConfig { - chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth); - chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate); - chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks); -+ chickenTakeDamageFromWater = getBoolean("mobs.chicken.takes-damage-from-water", chickenTakeDamageFromWater); - } - - public boolean codRidable = false; - public boolean codControllable = true; - public double codMaxHealth = 3.0D; -+ public boolean codTakeDamageFromWater = false; - private void codSettings() { - codRidable = getBoolean("mobs.cod.ridable", codRidable); - codControllable = getBoolean("mobs.cod.controllable", codControllable); -@@ -746,6 +761,7 @@ public class PurpurWorldConfig { - set("mobs.cod.attributes.max_health", oldValue); - } - codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth); -+ codTakeDamageFromWater = getBoolean("mobs.cod.takes-damage-from-water", codTakeDamageFromWater); - } - - public boolean cowRidable = false; -@@ -754,6 +770,7 @@ public class PurpurWorldConfig { - public double cowMaxHealth = 10.0D; - public int cowFeedMushrooms = 0; - public int cowBreedingTicks = 6000; -+ public boolean cowTakeDamageFromWater = false; - private void cowSettings() { - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); -@@ -766,6 +783,7 @@ public class PurpurWorldConfig { - cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth); - cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); - cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks); -+ cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater); - } - - public boolean creeperRidable = false; -@@ -775,6 +793,7 @@ public class PurpurWorldConfig { - public double creeperChargedChance = 0.0D; - public boolean creeperAllowGriefing = true; - public boolean creeperBypassMobGriefing = false; -+ public boolean creeperTakeDamageFromWater = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -788,6 +807,7 @@ public class PurpurWorldConfig { - creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance); - creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing); - creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing); -+ creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater); - } - - public boolean dolphinRidable = false; -@@ -797,6 +817,7 @@ public class PurpurWorldConfig { - public float dolphinSpitDamage = 2.0F; - public double dolphinMaxHealth = 10.0D; - public boolean dolphinDisableTreasureSearching = false; -+ public boolean dolphinTakeDamageFromWater = false; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -810,6 +831,7 @@ public class PurpurWorldConfig { - } - dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth); - dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching); -+ dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater); - } - - public boolean donkeyRidableInWater = false; -@@ -820,6 +842,7 @@ public class PurpurWorldConfig { - public double donkeyMovementSpeedMin = 0.175D; - public double donkeyMovementSpeedMax = 0.175D; - public int donkeyBreedingTicks = 6000; -+ public boolean donkeyTakeDamageFromWater = false; - private void donkeySettings() { - donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); - if (PurpurConfig.version < 10) { -@@ -836,6 +859,7 @@ public class PurpurWorldConfig { - donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin); - donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax); - donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks); -+ donkeyTakeDamageFromWater = getBoolean("mobs.donkey.takes-damage-from-water", donkeyTakeDamageFromWater); - } - - public boolean drownedRidable = false; -@@ -846,6 +870,7 @@ public class PurpurWorldConfig { - public boolean drownedJockeyOnlyBaby = true; - public double drownedJockeyChance = 0.05D; - public boolean drownedJockeyTryExistingChickens = true; -+ public boolean drownedTakeDamageFromWater = false; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -@@ -860,11 +885,13 @@ public class PurpurWorldConfig { - drownedJockeyOnlyBaby = getBoolean("mobs.drowned.jockey.only-babies", drownedJockeyOnlyBaby); - drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance); - drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens); -+ drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater); - } - - public boolean elderGuardianRidable = false; - public boolean elderGuardianControllable = true; - public double elderGuardianMaxHealth = 80.0D; -+ public boolean elderGuardianTakeDamageFromWater = false; - private void elderGuardianSettings() { - elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); - elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); -@@ -874,6 +901,7 @@ public class PurpurWorldConfig { - set("mobs.elder_guardian.attributes.max_health", oldValue); - } - elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth); -+ elderGuardianTakeDamageFromWater = getBoolean("mobs.elder_guardian.takes-damage-from-water", elderGuardianTakeDamageFromWater); - } - - public boolean enderDragonRidable = false; -@@ -883,6 +911,7 @@ public class PurpurWorldConfig { - public double enderDragonMaxHealth = 200.0D; - public boolean enderDragonAlwaysDropsFullExp = false; - public boolean enderDragonBypassMobGriefing = false; -+ public boolean enderDragonTakeDamageFromWater = false; - private void enderDragonSettings() { - enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); - enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); -@@ -900,6 +929,7 @@ public class PurpurWorldConfig { - enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth); - enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp); - enderDragonBypassMobGriefing = getBoolean("mobs.ender_dragon.bypass-mob-griefing", enderDragonBypassMobGriefing); -+ enderDragonTakeDamageFromWater = getBoolean("mobs.ender_dragon.takes-damage-from-water", enderDragonTakeDamageFromWater); - } - - public boolean endermanRidable = false; -@@ -909,6 +939,7 @@ public class PurpurWorldConfig { - public boolean endermanAllowGriefing = true; - public boolean endermanDespawnEvenWithBlock = false; - public boolean endermanBypassMobGriefing = false; -+ public boolean endermanTakeDamageFromWater = true; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -922,12 +953,14 @@ public class PurpurWorldConfig { - endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); - endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock); - endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing); -+ endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater); - } - - public boolean endermiteRidable = false; - public boolean endermiteRidableInWater = true; - public boolean endermiteControllable = true; - public double endermiteMaxHealth = 8.0D; -+ public boolean endermiteTakeDamageFromWater = false; - private void endermiteSettings() { - endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); - endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); -@@ -938,6 +971,7 @@ public class PurpurWorldConfig { - set("mobs.endermite.attributes.max_health", oldValue); - } - endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth); -+ endermiteTakeDamageFromWater = getBoolean("mobs.endermite.takes-damage-from-water", endermiteTakeDamageFromWater); - } - - public boolean evokerRidable = false; -@@ -945,6 +979,7 @@ public class PurpurWorldConfig { - public boolean evokerControllable = true; - public double evokerMaxHealth = 24.0D; - public boolean evokerBypassMobGriefing = false; -+ public boolean evokerTakeDamageFromWater = false; - private void evokerSettings() { - evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); - evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); -@@ -956,6 +991,7 @@ public class PurpurWorldConfig { - } - evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); - evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing); -+ evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater); - } - - public boolean foxRidable = false; -@@ -965,6 +1001,7 @@ public class PurpurWorldConfig { - public boolean foxTypeChangesWithTulips = false; - public int foxBreedingTicks = 6000; - public boolean foxBypassMobGriefing = false; -+ public boolean foxTakeDamageFromWater = false; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -978,6 +1015,7 @@ public class PurpurWorldConfig { - foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips); - foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks); - foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing); -+ foxTakeDamageFromWater = getBoolean("mobs.fox.takes-damage-from-water", foxTakeDamageFromWater); - } - - public boolean frogRidable = false; -@@ -998,6 +1036,7 @@ public class PurpurWorldConfig { - public boolean ghastControllable = true; - public double ghastMaxY = 320D; - public double ghastMaxHealth = 10.0D; -+ public boolean ghastTakeDamageFromWater = false; - private void ghastSettings() { - ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); - ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); -@@ -1009,6 +1048,7 @@ public class PurpurWorldConfig { - set("mobs.ghast.attributes.max_health", oldValue); - } - ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth); -+ ghastTakeDamageFromWater = getBoolean("mobs.ghast.takes-damage-from-water", ghastTakeDamageFromWater); - } - - public boolean giantRidable = false; -@@ -1021,6 +1061,7 @@ public class PurpurWorldConfig { - public float giantJumpHeight = 1.0F; - public boolean giantHaveAI = false; - public boolean giantHaveHostileAI = false; -+ public boolean giantTakeDamageFromWater = false; - private void giantSettings() { - giantRidable = getBoolean("mobs.giant.ridable", giantRidable); - giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); -@@ -1041,17 +1082,20 @@ public class PurpurWorldConfig { - giantJumpHeight = (float) getDouble("mobs.giant.jump-height", giantJumpHeight); - giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI); - giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI); -+ giantTakeDamageFromWater = getBoolean("mobs.giant.takes-damage-from-water", giantTakeDamageFromWater); - } - - public boolean glowSquidRidable = false; - public boolean glowSquidControllable = true; - public double glowSquidMaxHealth = 10.0D; - public boolean glowSquidsCanFly = false; -+ public boolean glowSquidTakeDamageFromWater = false; - private void glowSquidSettings() { - glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); - glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); - glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth); - glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly); -+ glowSquidTakeDamageFromWater = getBoolean("mobs.glow_squid.takes-damage-from-water", glowSquidTakeDamageFromWater); - } - - public boolean goatRidable = false; -@@ -1059,17 +1103,20 @@ public class PurpurWorldConfig { - public boolean goatControllable = true; - public double goatMaxHealth = 10.0D; - public int goatBreedingTicks = 6000; -+ public boolean goatTakeDamageFromWater = false; - private void goatSettings() { - goatRidable = getBoolean("mobs.goat.ridable", goatRidable); - goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); - goatControllable = getBoolean("mobs.goat.controllable", goatControllable); - goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth); - goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks); -+ goatTakeDamageFromWater = getBoolean("mobs.goat.takes-damage-from-water", goatTakeDamageFromWater); - } - - public boolean guardianRidable = false; - public boolean guardianControllable = true; - public double guardianMaxHealth = 30.0D; -+ public boolean guardianTakeDamageFromWater = false; - private void guardianSettings() { - guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); - guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); -@@ -1079,6 +1126,7 @@ public class PurpurWorldConfig { - set("mobs.guardian.attributes.max_health", oldValue); - } - guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth); -+ guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater); - } - - public boolean hoglinRidable = false; -@@ -1086,6 +1134,7 @@ public class PurpurWorldConfig { - public boolean hoglinControllable = true; - public double hoglinMaxHealth = 40.0D; - public int hoglinBreedingTicks = 6000; -+ public boolean hoglinTakeDamageFromWater = false; - private void hoglinSettings() { - hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); - hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); -@@ -1097,6 +1146,7 @@ public class PurpurWorldConfig { - } - hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth); - hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks); -+ hoglinTakeDamageFromWater = getBoolean("mobs.hoglin.takes-damage-from-water", hoglinTakeDamageFromWater); - } - - public boolean horseRidableInWater = false; -@@ -1107,6 +1157,7 @@ public class PurpurWorldConfig { - public double horseMovementSpeedMin = 0.1125D; - public double horseMovementSpeedMax = 0.3375D; - public int horseBreedingTicks = 6000; -+ public boolean horseTakeDamageFromWater = false; - private void horseSettings() { - horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1123,6 +1174,7 @@ public class PurpurWorldConfig { - horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin); - horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax); - horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks); -+ horseTakeDamageFromWater = getBoolean("mobs.horse.takes-damage-from-water", horseTakeDamageFromWater); - } - - public boolean huskRidable = false; -@@ -1133,6 +1185,7 @@ public class PurpurWorldConfig { - public boolean huskJockeyOnlyBaby = true; - public double huskJockeyChance = 0.05D; - public boolean huskJockeyTryExistingChickens = true; -+ public boolean huskTakeDamageFromWater = false; - private void huskSettings() { - huskRidable = getBoolean("mobs.husk.ridable", huskRidable); - huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); -@@ -1147,6 +1200,7 @@ public class PurpurWorldConfig { - huskJockeyOnlyBaby = getBoolean("mobs.husk.jockey.only-babies", huskJockeyOnlyBaby); - huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance); - huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens); -+ huskTakeDamageFromWater = getBoolean("mobs.husk.takes-damage-from-water", huskTakeDamageFromWater); - } - - public boolean illusionerRidable = false; -@@ -1155,6 +1209,7 @@ public class PurpurWorldConfig { - public double illusionerMovementSpeed = 0.5D; - public double illusionerFollowRange = 18.0D; - public double illusionerMaxHealth = 32.0D; -+ public boolean illusionerTakeDamageFromWater = false; - private void illusionerSettings() { - illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); - illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); -@@ -1171,6 +1226,7 @@ public class PurpurWorldConfig { - set("mobs.illusioner.attributes.max_health", oldValue); - } - illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth); -+ illusionerTakeDamageFromWater = getBoolean("mobs.illusioner.takes-damage-from-water", illusionerTakeDamageFromWater); - } - - public boolean ironGolemRidable = false; -@@ -1178,6 +1234,7 @@ public class PurpurWorldConfig { - public boolean ironGolemControllable = true; - public boolean ironGolemCanSwim = false; - public double ironGolemMaxHealth = 100.0D; -+ public boolean ironGolemTakeDamageFromWater = false; - private void ironGolemSettings() { - ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); - ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); -@@ -1189,6 +1246,7 @@ public class PurpurWorldConfig { - set("mobs.iron_golem.attributes.max_health", oldValue); - } - ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth); -+ ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater); - } - - public boolean llamaRidable = false; -@@ -1201,6 +1259,7 @@ public class PurpurWorldConfig { - public double llamaMovementSpeedMin = 0.175D; - public double llamaMovementSpeedMax = 0.175D; - public int llamaBreedingTicks = 6000; -+ public boolean llamaTakeDamageFromWater = false; - private void llamaSettings() { - llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); - llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); -@@ -1219,6 +1278,7 @@ public class PurpurWorldConfig { - llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin); - llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax); - llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks); -+ llamaTakeDamageFromWater = getBoolean("mobs.llama.takes-damage-from-water", llamaTakeDamageFromWater); - } - - public boolean magmaCubeRidable = false; -@@ -1228,6 +1288,7 @@ public class PurpurWorldConfig { - public String magmaCubeAttackDamage = "size"; - public Map magmaCubeMaxHealthCache = new HashMap<>(); - public Map magmaCubeAttackDamageCache = new HashMap<>(); -+ public boolean magmaCubeTakeDamageFromWater = false; - private void magmaCubeSettings() { - magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable); - magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater); -@@ -1241,6 +1302,7 @@ public class PurpurWorldConfig { - magmaCubeAttackDamage = getString("mobs.magma_cube.attributes.attack_damage", magmaCubeAttackDamage); - magmaCubeMaxHealthCache.clear(); - magmaCubeAttackDamageCache.clear(); -+ magmaCubeTakeDamageFromWater = getBoolean("mobs.magma_cube.takes-damage-from-water", magmaCubeTakeDamageFromWater); - } - - public boolean mooshroomRidable = false; -@@ -1248,6 +1310,7 @@ public class PurpurWorldConfig { - public boolean mooshroomControllable = true; - public double mooshroomMaxHealth = 10.0D; - public int mooshroomBreedingTicks = 6000; -+ public boolean mooshroomTakeDamageFromWater = false; - private void mooshroomSettings() { - mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); - mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); -@@ -1259,6 +1322,7 @@ public class PurpurWorldConfig { - } - mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth); - mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks); -+ mooshroomTakeDamageFromWater = getBoolean("mobs.mooshroom.takes-damage-from-water", mooshroomTakeDamageFromWater); - } - - public boolean muleRidableInWater = false; -@@ -1269,6 +1333,7 @@ public class PurpurWorldConfig { - public double muleMovementSpeedMin = 0.175D; - public double muleMovementSpeedMax = 0.175D; - public int muleBreedingTicks = 6000; -+ public boolean muleTakeDamageFromWater = false; - private void muleSettings() { - muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1285,6 +1350,7 @@ public class PurpurWorldConfig { - muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin); - muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax); - muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks); -+ muleTakeDamageFromWater = getBoolean("mobs.mule.takes-damage-from-water", muleTakeDamageFromWater); - } - - public boolean ocelotRidable = false; -@@ -1292,6 +1358,7 @@ public class PurpurWorldConfig { - public boolean ocelotControllable = true; - public double ocelotMaxHealth = 10.0D; - public int ocelotBreedingTicks = 6000; -+ public boolean ocelotTakeDamageFromWater = false; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -@@ -1303,6 +1370,7 @@ public class PurpurWorldConfig { - } - ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth); - ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks); -+ ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater); - } - - public boolean pandaRidable = false; -@@ -1310,6 +1378,7 @@ public class PurpurWorldConfig { - public boolean pandaControllable = true; - public double pandaMaxHealth = 20.0D; - public int pandaBreedingTicks = 6000; -+ public boolean pandaTakeDamageFromWater = false; - private void pandaSettings() { - pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); - pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); -@@ -1321,6 +1390,7 @@ public class PurpurWorldConfig { - } - pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth); - pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks); -+ pandaTakeDamageFromWater = getBoolean("mobs.panda.takes-damage-from-water", pandaTakeDamageFromWater); - } - - public boolean parrotRidable = false; -@@ -1328,6 +1398,7 @@ public class PurpurWorldConfig { - public boolean parrotControllable = true; - public double parrotMaxY = 320D; - public double parrotMaxHealth = 6.0D; -+ public boolean parrotTakeDamageFromWater = false; - private void parrotSettings() { - parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); - parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); -@@ -1339,6 +1410,7 @@ public class PurpurWorldConfig { - set("mobs.parrot.attributes.max_health", oldValue); - } - parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth); -+ parrotTakeDamageFromWater = getBoolean("mobs.parrot.takes-damage-from-water", parrotTakeDamageFromWater); - } - - public boolean phantomRidable = false; -@@ -1365,6 +1437,7 @@ public class PurpurWorldConfig { - public boolean phantomIgnorePlayersWithTorch = false; - public boolean phantomBurnInDaylight = true; - public boolean phantomFlamesOnSwoop = false; -+ public boolean phantomTakeDamageFromWater = false; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -1399,6 +1472,7 @@ public class PurpurWorldConfig { - phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight); - phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch); - phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop); -+ phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater); - } - - public boolean pigRidable = false; -@@ -1407,6 +1481,7 @@ public class PurpurWorldConfig { - public double pigMaxHealth = 10.0D; - public boolean pigGiveSaddleBack = false; - public int pigBreedingTicks = 6000; -+ public boolean pigTakeDamageFromWater = false; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -@@ -1419,6 +1494,7 @@ public class PurpurWorldConfig { - pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth); - pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack); - pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks); -+ pigTakeDamageFromWater = getBoolean("mobs.pig.takes-damage-from-water", pigTakeDamageFromWater); - } - - public boolean piglinRidable = false; -@@ -1426,6 +1502,7 @@ public class PurpurWorldConfig { - public boolean piglinControllable = true; - public double piglinMaxHealth = 16.0D; - public boolean piglinBypassMobGriefing = false; -+ public boolean piglinTakeDamageFromWater = false; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -1437,12 +1514,14 @@ public class PurpurWorldConfig { - } - piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); - piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); -+ piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); - } - - public boolean piglinBruteRidable = false; - public boolean piglinBruteRidableInWater = true; - public boolean piglinBruteControllable = true; - public double piglinBruteMaxHealth = 50.0D; -+ public boolean piglinBruteTakeDamageFromWater = false; - private void piglinBruteSettings() { - piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); - piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); -@@ -1453,6 +1532,7 @@ public class PurpurWorldConfig { - set("mobs.piglin_brute.attributes.max_health", oldValue); - } - piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth); -+ piglinBruteTakeDamageFromWater = getBoolean("mobs.piglin_brute.takes-damage-from-water", piglinBruteTakeDamageFromWater); - } - - public boolean pillagerRidable = false; -@@ -1460,6 +1540,7 @@ public class PurpurWorldConfig { - public boolean pillagerControllable = true; - public double pillagerMaxHealth = 24.0D; - public boolean pillagerBypassMobGriefing = false; -+ public boolean pillagerTakeDamageFromWater = false; - private void pillagerSettings() { - pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); - pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); -@@ -1471,6 +1552,7 @@ public class PurpurWorldConfig { - } - pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); - pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing); -+ pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater); - } - - public boolean polarBearRidable = false; -@@ -1480,6 +1562,7 @@ public class PurpurWorldConfig { - public String polarBearBreedableItemString = ""; - public Item polarBearBreedableItem = null; - public int polarBearBreedingTicks = 6000; -+ public boolean polarBearTakeDamageFromWater = false; - private void polarBearSettings() { - polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); - polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); -@@ -1494,11 +1577,13 @@ public class PurpurWorldConfig { - Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(polarBearBreedableItemString)); - if (item != Items.AIR) polarBearBreedableItem = item; - polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks); -+ polarBearTakeDamageFromWater = getBoolean("mobs.polar_bear.takes-damage-from-water", polarBearTakeDamageFromWater); - } - - public boolean pufferfishRidable = false; - public boolean pufferfishControllable = true; - public double pufferfishMaxHealth = 3.0D; -+ public boolean pufferfishTakeDamageFromWater = false; - private void pufferfishSettings() { - pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); - pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); -@@ -1508,6 +1593,7 @@ public class PurpurWorldConfig { - set("mobs.pufferfish.attributes.max_health", oldValue); - } - pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth); -+ pufferfishTakeDamageFromWater = getBoolean("mobs.pufferfish.takes-damage-from-water", pufferfishTakeDamageFromWater); - } - - public boolean rabbitRidable = false; -@@ -1518,6 +1604,7 @@ public class PurpurWorldConfig { - public double rabbitNaturalKiller = 0.0D; - public int rabbitBreedingTicks = 6000; - public boolean rabbitBypassMobGriefing = false; -+ public boolean rabbitTakeDamageFromWater = false; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -@@ -1532,6 +1619,7 @@ public class PurpurWorldConfig { - rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller); - rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks); - rabbitBypassMobGriefing = getBoolean("mobs.rabbit.bypass-mob-griefing", rabbitBypassMobGriefing); -+ rabbitTakeDamageFromWater = getBoolean("mobs.rabbit.takes-damage-from-water", rabbitTakeDamageFromWater); - } - - public boolean ravagerRidable = false; -@@ -1539,6 +1627,7 @@ public class PurpurWorldConfig { - public boolean ravagerControllable = true; - public double ravagerMaxHealth = 100.0D; - public boolean ravagerBypassMobGriefing = false; -+ public boolean ravagerTakeDamageFromWater = false; - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -@@ -1550,11 +1639,13 @@ public class PurpurWorldConfig { - } - ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); - ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing); -+ ravagerTakeDamageFromWater = getBoolean("mobs.ravager.takes-damage-from-water", ravagerTakeDamageFromWater); - } - - public boolean salmonRidable = false; - public boolean salmonControllable = true; - public double salmonMaxHealth = 3.0D; -+ public boolean salmonTakeDamageFromWater = false; - private void salmonSettings() { - salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); - salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); -@@ -1564,6 +1655,7 @@ public class PurpurWorldConfig { - set("mobs.salmon.attributes.max_health", oldValue); - } - salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth); -+ salmonTakeDamageFromWater = getBoolean("mobs.salmon.takes-damage-from-water", salmonTakeDamageFromWater); - } - - public boolean sheepRidable = false; -@@ -1572,6 +1664,7 @@ public class PurpurWorldConfig { - public double sheepMaxHealth = 8.0D; - public int sheepBreedingTicks = 6000; - public boolean sheepBypassMobGriefing = false; -+ public boolean sheepTakeDamageFromWater = false; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -1584,12 +1677,14 @@ public class PurpurWorldConfig { - sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth); - sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks); - sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing); -+ sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater); - } - - public boolean shulkerRidable = false; - public boolean shulkerRidableInWater = true; - public boolean shulkerControllable = true; - public double shulkerMaxHealth = 30.0D; -+ public boolean shulkerTakeDamageFromWater = false; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -@@ -1600,6 +1695,7 @@ public class PurpurWorldConfig { - set("mobs.shulker.attributes.max_health", oldValue); - } - shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth); -+ shulkerTakeDamageFromWater = getBoolean("mobs.shulker.takes-damage-from-water", shulkerTakeDamageFromWater); - } - - public boolean silverfishRidable = false; -@@ -1607,6 +1703,7 @@ public class PurpurWorldConfig { - public boolean silverfishControllable = true; - public double silverfishMaxHealth = 8.0D; - public boolean silverfishBypassMobGriefing = false; -+ public boolean silverfishTakeDamageFromWater = false; - private void silverfishSettings() { - silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); - silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); -@@ -1618,12 +1715,14 @@ public class PurpurWorldConfig { - } - silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth); - silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing); -+ silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater); - } - - public boolean skeletonRidable = false; - public boolean skeletonRidableInWater = true; - public boolean skeletonControllable = true; - public double skeletonMaxHealth = 20.0D; -+ public boolean skeletonTakeDamageFromWater = false; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -1634,6 +1733,7 @@ public class PurpurWorldConfig { - set("mobs.skeleton.attributes.max_health", oldValue); - } - skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth); -+ skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater); - } - - public boolean skeletonHorseRidableInWater = true; -@@ -1644,6 +1744,7 @@ public class PurpurWorldConfig { - public double skeletonHorseJumpStrengthMax = 1.0D; - public double skeletonHorseMovementSpeedMin = 0.2D; - public double skeletonHorseMovementSpeedMax = 0.2D; -+ public boolean skeletonHorseTakeDamageFromWater = false; - private void skeletonHorseSettings() { - skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); - skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim); -@@ -1659,6 +1760,7 @@ public class PurpurWorldConfig { - skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax); - skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin); - skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax); -+ skeletonHorseTakeDamageFromWater = getBoolean("mobs.skeleton_horse.takes-damage-from-water", skeletonHorseTakeDamageFromWater); - } - - public boolean slimeRidable = false; -@@ -1668,6 +1770,7 @@ public class PurpurWorldConfig { - public String slimeAttackDamage = "size"; - public Map slimeMaxHealthCache = new HashMap<>(); - public Map slimeAttackDamageCache = new HashMap<>(); -+ public boolean slimeTakeDamageFromWater = false; - private void slimeSettings() { - slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable); - slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater); -@@ -1681,6 +1784,7 @@ public class PurpurWorldConfig { - slimeAttackDamage = getString("mobs.slime.attributes.attack_damage", slimeAttackDamage); - slimeMaxHealthCache.clear(); - slimeAttackDamageCache.clear(); -+ slimeTakeDamageFromWater = getBoolean("mobs.slime.takes-damage-from-water", slimeTakeDamageFromWater); - } - - public boolean snowGolemRidable = false; -@@ -1695,6 +1799,7 @@ public class PurpurWorldConfig { - public float snowGolemSnowBallModifier = 10.0F; - public double snowGolemAttackDistance = 1.25D; - public boolean snowGolemBypassMobGriefing = false; -+ public boolean snowGolemTakeDamageFromWater = true; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1713,6 +1818,7 @@ public class PurpurWorldConfig { - snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier); - snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance); - snowGolemBypassMobGriefing = getBoolean("mobs.snow_golem.bypass-mob-griefing", snowGolemBypassMobGriefing); -+ snowGolemTakeDamageFromWater = getBoolean("mobs.snow_golem.takes-damage-from-water", snowGolemTakeDamageFromWater); - } - - public boolean snifferRidable = false; -@@ -1734,6 +1840,7 @@ public class PurpurWorldConfig { - public boolean squidImmuneToEAR = true; - public double squidOffsetWaterCheck = 0.0D; - public boolean squidsCanFly = false; -+ public boolean squidTakeDamageFromWater = false; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -1746,12 +1853,14 @@ public class PurpurWorldConfig { - squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR); - squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck); - squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly); -+ squidTakeDamageFromWater = getBoolean("mobs.squid.takes-damage-from-water", squidTakeDamageFromWater); - } - - public boolean spiderRidable = false; - public boolean spiderRidableInWater = false; - public boolean spiderControllable = true; - public double spiderMaxHealth = 16.0D; -+ public boolean spiderTakeDamageFromWater = false; - private void spiderSettings() { - spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); - spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); -@@ -1762,12 +1871,14 @@ public class PurpurWorldConfig { - set("mobs.spider.attributes.max_health", oldValue); - } - spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth); -+ spiderTakeDamageFromWater = getBoolean("mobs.spider.takes-damage-from-water", spiderTakeDamageFromWater); - } - - public boolean strayRidable = false; - public boolean strayRidableInWater = true; - public boolean strayControllable = true; - public double strayMaxHealth = 20.0D; -+ public boolean strayTakeDamageFromWater = false; - private void straySettings() { - strayRidable = getBoolean("mobs.stray.ridable", strayRidable); - strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); -@@ -1778,6 +1889,7 @@ public class PurpurWorldConfig { - set("mobs.stray.attributes.max_health", oldValue); - } - strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth); -+ strayTakeDamageFromWater = getBoolean("mobs.stray.takes-damage-from-water", strayTakeDamageFromWater); - } - - public boolean striderRidable = false; -@@ -1786,6 +1898,7 @@ public class PurpurWorldConfig { - public double striderMaxHealth = 20.0D; - public int striderBreedingTicks = 6000; - public boolean striderGiveSaddleBack = false; -+ public boolean striderTakeDamageFromWater = true; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -@@ -1798,6 +1911,7 @@ public class PurpurWorldConfig { - striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth); - striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks); - striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack); -+ striderTakeDamageFromWater = getBoolean("mobs.strider.takes-damage-from-water", striderTakeDamageFromWater); - } - - public boolean tadpoleRidable = false; -@@ -1819,6 +1933,7 @@ public class PurpurWorldConfig { - public double traderLlamaMovementSpeedMin = 0.175D; - public double traderLlamaMovementSpeedMax = 0.175D; - public int traderLlamaBreedingTicks = 6000; -+ public boolean traderLlamaTakeDamageFromWater = false; - private void traderLlamaSettings() { - traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); - traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); -@@ -1837,11 +1952,13 @@ public class PurpurWorldConfig { - traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin); - traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax); - traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks); -+ traderLlamaTakeDamageFromWater = getBoolean("mobs.trader_llama.takes-damage-from-water", traderLlamaTakeDamageFromWater); - } - - public boolean tropicalFishRidable = false; - public boolean tropicalFishControllable = true; - public double tropicalFishMaxHealth = 3.0D; -+ public boolean tropicalFishTakeDamageFromWater = false; - private void tropicalFishSettings() { - tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); - tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); -@@ -1851,6 +1968,7 @@ public class PurpurWorldConfig { - set("mobs.tropical_fish.attributes.max_health", oldValue); - } - tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth); -+ tropicalFishTakeDamageFromWater = getBoolean("mobs.tropical_fish.takes-damage-from-water", tropicalFishTakeDamageFromWater); - } - - public boolean turtleRidable = false; -@@ -1858,6 +1976,7 @@ public class PurpurWorldConfig { - public boolean turtleControllable = true; - public double turtleMaxHealth = 30.0D; - public int turtleBreedingTicks = 6000; -+ public boolean turtleTakeDamageFromWater = false; - private void turtleSettings() { - turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); - turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); -@@ -1869,6 +1988,7 @@ public class PurpurWorldConfig { - } - turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth); - turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks); -+ turtleTakeDamageFromWater = getBoolean("mobs.turtle.takes-damage-from-water", turtleTakeDamageFromWater); - } - - public boolean vexRidable = false; -@@ -1876,6 +1996,7 @@ public class PurpurWorldConfig { - public boolean vexControllable = true; - public double vexMaxY = 320D; - public double vexMaxHealth = 14.0D; -+ public boolean vexTakeDamageFromWater = false; - private void vexSettings() { - vexRidable = getBoolean("mobs.vex.ridable", vexRidable); - vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); -@@ -1887,6 +2008,7 @@ public class PurpurWorldConfig { - set("mobs.vex.attributes.max_health", oldValue); - } - vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth); -+ vexTakeDamageFromWater = getBoolean("mobs.vex.takes-damage-from-water", vexTakeDamageFromWater); - } - - public boolean villagerRidable = false; -@@ -1900,6 +2022,7 @@ public class PurpurWorldConfig { - public boolean villagerClericsFarmWarts = false; - public boolean villagerClericFarmersThrowWarts = true; - public boolean villagerBypassMobGriefing = false; -+ public boolean villagerTakeDamageFromWater = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1917,6 +2040,7 @@ public class PurpurWorldConfig { - villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts); - villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts); - villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing); -+ villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater); - } - - public boolean vindicatorRidable = false; -@@ -1924,6 +2048,7 @@ public class PurpurWorldConfig { - public boolean vindicatorControllable = true; - public double vindicatorMaxHealth = 24.0D; - public double vindicatorJohnnySpawnChance = 0D; -+ public boolean vindicatorTakeDamageFromWater = false; - private void vindicatorSettings() { - vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); - vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); -@@ -1935,6 +2060,7 @@ public class PurpurWorldConfig { - } - vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth); - vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance); -+ vindicatorTakeDamageFromWater = getBoolean("mobs.vindicator.takes-damage-from-water", vindicatorTakeDamageFromWater); - } - - public boolean wanderingTraderRidable = false; -@@ -1943,6 +2069,7 @@ public class PurpurWorldConfig { - public double wanderingTraderMaxHealth = 20.0D; - public boolean wanderingTraderFollowEmeraldBlock = false; - public boolean wanderingTraderCanBeLeashed = false; -+ public boolean wanderingTraderTakeDamageFromWater = false; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -1955,6 +2082,7 @@ public class PurpurWorldConfig { - wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth); - wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock); - wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed); -+ wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater); - } - - public boolean wardenRidable = false; -@@ -1970,6 +2098,7 @@ public class PurpurWorldConfig { - public boolean witchRidableInWater = true; - public boolean witchControllable = true; - public double witchMaxHealth = 26.0D; -+ public boolean witchTakeDamageFromWater = false; - private void witchSettings() { - witchRidable = getBoolean("mobs.witch.ridable", witchRidable); - witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); -@@ -1980,6 +2109,7 @@ public class PurpurWorldConfig { - set("mobs.witch.attributes.max_health", oldValue); - } - witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth); -+ witchTakeDamageFromWater = getBoolean("mobs.witch.takes-damage-from-water", witchTakeDamageFromWater); - } - - public boolean witherRidable = false; -@@ -1990,6 +2120,7 @@ public class PurpurWorldConfig { - public float witherHealthRegenAmount = 1.0f; - public int witherHealthRegenDelay = 20; - public boolean witherBypassMobGriefing = false; -+ public boolean witherTakeDamageFromWater = false; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -2008,12 +2139,14 @@ public class PurpurWorldConfig { - witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount); - witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay); - witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing); -+ witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater); - } - - public boolean witherSkeletonRidable = false; - public boolean witherSkeletonRidableInWater = true; - public boolean witherSkeletonControllable = true; - public double witherSkeletonMaxHealth = 20.0D; -+ public boolean witherSkeletonTakeDamageFromWater = false; - private void witherSkeletonSettings() { - witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); - witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); -@@ -2024,6 +2157,7 @@ public class PurpurWorldConfig { - set("mobs.wither_skeleton.attributes.max_health", oldValue); - } - witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth); -+ witherSkeletonTakeDamageFromWater = getBoolean("mobs.wither_skeleton.takes-damage-from-water", witherSkeletonTakeDamageFromWater); - } - - public boolean wolfRidable = false; -@@ -2034,6 +2168,7 @@ public class PurpurWorldConfig { - public boolean wolfMilkCuresRabies = true; - public double wolfNaturalRabid = 0.0D; - public int wolfBreedingTicks = 6000; -+ public boolean wolfTakeDamageFromWater = false; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); - wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); -@@ -2052,12 +2187,14 @@ public class PurpurWorldConfig { - wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies); - wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid); - wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); -+ wolfTakeDamageFromWater = getBoolean("mobs.wolf.takes-damage-from-water", wolfTakeDamageFromWater); - } - - public boolean zoglinRidable = false; - public boolean zoglinRidableInWater = true; - public boolean zoglinControllable = true; - public double zoglinMaxHealth = 40.0D; -+ public boolean zoglinTakeDamageFromWater = false; - private void zoglinSettings() { - zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); - zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); -@@ -2068,6 +2205,7 @@ public class PurpurWorldConfig { - set("mobs.zoglin.attributes.max_health", oldValue); - } - zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth); -+ zoglinTakeDamageFromWater = getBoolean("mobs.zoglin.takes-damage-from-water", zoglinTakeDamageFromWater); - } - - public boolean zombieRidable = false; -@@ -2080,6 +2218,7 @@ public class PurpurWorldConfig { - public boolean zombieJockeyTryExistingChickens = true; - public boolean zombieAggressiveTowardsVillagerWhenLagging = true; - public boolean zombieBypassMobGriefing = false; -+ public boolean zombieTakeDamageFromWater = false; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -2096,6 +2235,7 @@ public class PurpurWorldConfig { - zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens); - zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging); - zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing); -+ zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater); - } - - public boolean zombieHorseRidableInWater = false; -@@ -2107,6 +2247,7 @@ public class PurpurWorldConfig { - public double zombieHorseMovementSpeedMin = 0.2D; - public double zombieHorseMovementSpeedMax = 0.2D; - public double zombieHorseSpawnChance = 0.0D; -+ public boolean zombieHorseTakeDamageFromWater = false; - private void zombieHorseSettings() { - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); - zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); -@@ -2123,6 +2264,7 @@ public class PurpurWorldConfig { - zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin); - zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax); - zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance); -+ zombieHorseTakeDamageFromWater = getBoolean("mobs.zombie_horse.takes-damage-from-water", zombieHorseTakeDamageFromWater); - } - - public boolean zombieVillagerRidable = false; -@@ -2133,6 +2275,7 @@ public class PurpurWorldConfig { - public boolean zombieVillagerJockeyOnlyBaby = true; - public double zombieVillagerJockeyChance = 0.05D; - public boolean zombieVillagerJockeyTryExistingChickens = true; -+ public boolean zombieVillagerTakeDamageFromWater = false; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -2147,6 +2290,7 @@ public class PurpurWorldConfig { - zombieVillagerJockeyOnlyBaby = getBoolean("mobs.zombie_villager.jockey.only-babies", zombieVillagerJockeyOnlyBaby); - zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance); - zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens); -+ zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater); - } - - public boolean zombifiedPiglinRidable = false; -@@ -2158,6 +2302,7 @@ public class PurpurWorldConfig { - public double zombifiedPiglinJockeyChance = 0.05D; - public boolean zombifiedPiglinJockeyTryExistingChickens = true; - public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = true; -+ public boolean zombifiedPiglinTakeDamageFromWater = false; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -@@ -2173,5 +2318,6 @@ public class PurpurWorldConfig { - zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance); - zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens); - zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry); -+ zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater); - } - } diff --git a/patches/server/0130-Config-to-always-tame-in-Creative.patch b/patches/server/0130-Config-to-always-tame-in-Creative.patch deleted file mode 100644 index 70ead4a8b..000000000 --- a/patches/server/0130-Config-to-always-tame-in-Creative.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 9 Feb 2021 21:23:37 -0500 -Subject: [PATCH] Config to always tame in Creative - -Adds a configuration option that ensures a player in Creative always tames a tameable entity. -This essentially allows Creative mode players to tame animals on their first try. - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java -index 7fc5b5d624b61a3de9c71e975b51d20c5800be00..8fb070b1ae11d34b2fa420ded58097d421263cab 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java -@@ -66,7 +66,7 @@ public class RunAroundLikeCrazyGoal extends Goal { - int i = this.horse.getTemper(); - int j = this.horse.getMaxTemper(); - -- if (j > 0 && this.horse.getRandom().nextInt(j) < i && !CraftEventFactory.callEntityTameEvent(this.horse, ((CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent -+ if ((this.horse.level().purpurConfig.alwaysTameInCreative && ((Player) entity).getAbilities().instabuild) || (j > 0 && this.horse.getRandom().nextInt(j) < i && !CraftEventFactory.callEntityTameEvent(this.horse, ((CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled())) { // CraftBukkit - fire EntityTameEvent // Purpur - this.horse.tameWithName((Player) entity); - return; - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 1554e4a0e068e06ec121adf56421f8332132f062..7f1763b820a0c5dc69bdf6cdf14a97a283ba5916 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -470,7 +470,7 @@ public class Cat extends TamableAnimal implements VariantHolder { - } - } else if (this.isFood(itemstack)) { - this.usePlayerItem(player, hand, itemstack); -- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit -+ if ((this.level().purpurConfig.alwaysTameInCreative && player.getAbilities().instabuild) || (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled())) { // CraftBukkit // Purpur - this.tame(player); - this.setOrderedToSit(true); - this.level().broadcastEntityEvent(this, (byte) 7); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -index 583cbd4e891647d974b3ca4fc51d4aae6d2acb88..4c64feca8d78b907406cc409bd6beef4b3bd35f3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -@@ -334,7 +334,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder -Date: Sat, 13 Feb 2021 09:28:56 -0500 -Subject: [PATCH] End crystal explosion options - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -index ef0098e46bda8abc456f2bb5929d874c6aeb8698..564d17bc460e2a04947ff9676fbf4c8b1569659c 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -@@ -48,6 +48,22 @@ public class EndCrystal extends Entity { - this.setPos(x, y, z); - } - -+ public boolean shouldExplode() { -+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplode : level().purpurConfig.baselessEndCrystalExplode; -+ } -+ -+ public float getExplosionPower() { -+ return (float) (showsBottom() ? level().purpurConfig.basedEndCrystalExplosionPower : level().purpurConfig.baselessEndCrystalExplosionPower); -+ } -+ -+ public boolean hasExplosionFire() { -+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionFire : level().purpurConfig.baselessEndCrystalExplosionFire; -+ } -+ -+ public Level.ExplosionInteraction getExplosionEffect() { -+ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionEffect : level().purpurConfig.baselessEndCrystalExplosionEffect; -+ } -+ - @Override - protected Entity.MovementEmission getMovementEmission() { - return Entity.MovementEmission.NONE; -@@ -173,16 +189,18 @@ public class EndCrystal extends Entity { - // CraftBukkit end - this.remove(Entity.RemovalReason.KILLED); - if (!source.is(DamageTypeTags.IS_EXPLOSION)) { -+ if (shouldExplode()) {// Purpur - DamageSource damagesource1 = source.getEntity() != null ? this.damageSources().explosion(this, source.getEntity()) : null; - - // CraftBukkit start -- ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, 6.0F, false); -+ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, getExplosionPower(), hasExplosionFire()); // Purpur - if (event.isCancelled()) { - this.unsetRemoved(); - return false; - } -- this.level().explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK); -+ this.level().explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), getExplosionEffect()); // Purpur - // CraftBukkit end -+ } else this.unsetRemoved(); // Purpur - } - - this.onDestroyedBy(source); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9f136f87117de89eac06da9e79fb844b20fe5967..b16a62ea9e57d4114fc9f0c5c9fce0c14983cea0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -468,6 +468,43 @@ public class PurpurWorldConfig { - dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils); - } - -+ public boolean baselessEndCrystalExplode = true; -+ public double baselessEndCrystalExplosionPower = 6.0D; -+ public boolean baselessEndCrystalExplosionFire = false; -+ public net.minecraft.world.level.Level.ExplosionInteraction baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ public boolean basedEndCrystalExplode = true; -+ public double basedEndCrystalExplosionPower = 6.0D; -+ public boolean basedEndCrystalExplosionFire = false; -+ public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ private void endCrystalSettings() { -+ if (PurpurConfig.version < 31) { -+ if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) { -+ set("blocks.end-crystal.baseless.explosion-effect", "BLOCK"); -+ } -+ if ("DESTROY".equals(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()))) { -+ set("blocks.end-crystal.base.explosion-effect", "BLOCK"); -+ } -+ } -+ baselessEndCrystalExplode = getBoolean("blocks.end-crystal.baseless.explode", baselessEndCrystalExplode); -+ baselessEndCrystalExplosionPower = getDouble("blocks.end-crystal.baseless.explosion-power", baselessEndCrystalExplosionPower); -+ baselessEndCrystalExplosionFire = getBoolean("blocks.end-crystal.baseless.explosion-fire", baselessEndCrystalExplosionFire); -+ try { -+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name())); -+ } catch (IllegalArgumentException e) { -+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.baseless.explosion-effect`! Using default of `BLOCK`"); -+ baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ } -+ basedEndCrystalExplode = getBoolean("blocks.end-crystal.base.explode", basedEndCrystalExplode); -+ basedEndCrystalExplosionPower = getDouble("blocks.end-crystal.base.explosion-power", basedEndCrystalExplosionPower); -+ basedEndCrystalExplosionFire = getBoolean("blocks.end-crystal.base.explosion-fire", basedEndCrystalExplosionFire); -+ try { -+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name())); -+ } catch (IllegalArgumentException e) { -+ log(Level.SEVERE, "Unknown value for `blocks.end-crystal.base.explosion-effect`! Using default of `BLOCK`"); -+ basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ } -+ } -+ - public boolean farmlandBypassMobGriefing = false; - public boolean farmlandGetsMoistFromBelow = false; - public boolean farmlandAlpha = false; diff --git a/patches/server/0132-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch b/patches/server/0132-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch deleted file mode 100644 index d6293afa2..000000000 --- a/patches/server/0132-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Sat, 20 Feb 2021 14:47:08 -0800 -Subject: [PATCH] Configs for if Wither/Ender Dragon can ride vehicles - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index 9986dbe52ad38fcc0f642401a932428afc39c9f6..a907930813b0a3176354b4b307576d9ff3b0c5ca 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -1196,6 +1196,7 @@ public class EnderDragon extends Mob implements Enemy { - - @Override - protected boolean canRide(Entity entity) { -+ if (this.level().purpurConfig.enderDragonCanRideVehicles) return this.boardingCooldown <= 0; // Purpur - return false; - } - -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 6056180cf792534b450cb00977b5f7bc011d0ae4..257cd3ea1830b46d1c51242cd639c4f0e21c8774 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -731,6 +731,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - - @Override - protected boolean canRide(Entity entity) { -+ if (this.level().purpurConfig.witherCanRideVehicles) return this.boardingCooldown <= 0; // Purpur - return false; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b16a62ea9e57d4114fc9f0c5c9fce0c14983cea0..fd2865f5be6bffd8fcbbac35151e9dde2149ab8d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -951,6 +951,7 @@ public class PurpurWorldConfig { - public boolean enderDragonAlwaysDropsFullExp = false; - public boolean enderDragonBypassMobGriefing = false; - public boolean enderDragonTakeDamageFromWater = false; -+ public boolean enderDragonCanRideVehicles = false; - private void enderDragonSettings() { - enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); - enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); -@@ -969,6 +970,7 @@ public class PurpurWorldConfig { - enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp); - enderDragonBypassMobGriefing = getBoolean("mobs.ender_dragon.bypass-mob-griefing", enderDragonBypassMobGriefing); - enderDragonTakeDamageFromWater = getBoolean("mobs.ender_dragon.takes-damage-from-water", enderDragonTakeDamageFromWater); -+ enderDragonCanRideVehicles = getBoolean("mobs.ender_dragon.can-ride-vehicles", enderDragonCanRideVehicles); - } - - public boolean endermanRidable = false; -@@ -2160,6 +2162,7 @@ public class PurpurWorldConfig { - public int witherHealthRegenDelay = 20; - public boolean witherBypassMobGriefing = false; - public boolean witherTakeDamageFromWater = false; -+ public boolean witherCanRideVehicles = false; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -2179,6 +2182,7 @@ public class PurpurWorldConfig { - witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay); - witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing); - witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater); -+ witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles); - } - - public boolean witherSkeletonRidable = false; diff --git a/patches/server/0133-Dont-run-with-scissors.patch b/patches/server/0133-Dont-run-with-scissors.patch deleted file mode 100644 index 935c2c126..000000000 --- a/patches/server/0133-Dont-run-with-scissors.patch +++ /dev/null @@ -1,134 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: JustDoom -Date: Fri, 5 Mar 2021 14:23:16 -0500 -Subject: [PATCH] Dont run with scissors! - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 6c5b0d1d8fe7fae29dc1ed449e0412c9f449acf9..7ac39b9f7a0afabe4a6fde954a85f0d4f53a01df 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1741,6 +1741,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.player.resetFallDistance(); - } - -+ // Purpur Start -+ if (this.player.level().purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.level().purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.level().purpurConfig.ignoreScissorsInLava && this.player.isInLava()) && (isScissor(this.player.getItemInHand(InteractionHand.MAIN_HAND)) || isScissor(this.player.getItemInHand(InteractionHand.OFF_HAND))) && (int) (Math.random() * 10) == 0) { -+ this.player.hurt(this.player.damageSources().scissors(), (float) this.player.level().purpurConfig.scissorsRunningDamage); -+ if (!org.purpurmc.purpur.PurpurConfig.dontRunWithScissors.isBlank()) this.player.sendActionBarMessage(org.purpurmc.purpur.PurpurConfig.dontRunWithScissors); -+ } -+ // Purpur End -+ - this.player.checkMovementStatistics(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5); - this.lastGoodX = this.player.getX(); - this.lastGoodY = this.player.getY(); -@@ -1785,6 +1792,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - } - // Paper end - optimise out extra getCubes -+ -+ // Purpur start -+ public boolean isScissor(ItemStack stack) { -+ return stack.is(Items.SHEARS) && (stack.getTag() == null || stack.getTag().getInt("CustomModelData") == 0); -+ } -+ // Purpur end -+ - private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box, double newX, double newY, double newZ) { - AABB axisalignedbb1 = this.player.getBoundingBox().move(newX - this.player.getX(), newY - this.player.getY(), newZ - this.player.getZ()); - Iterable iterable = world.getCollisions(this.player, axisalignedbb1.deflate(9.999999747378752E-6D)); -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -index c12d7bacf2c54f268b1bc5e46250a083ca041415..c6f8ae130b75fbb0ad41f9a5917e934e33fd3043 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -99,6 +99,11 @@ public class CombatTracker { - Component component = ComponentUtils.wrapInSquareBrackets(Component.translatable(string + ".link")).withStyle(INTENTIONAL_GAME_DESIGN_STYLE); - return Component.translatable(string + ".message", this.mob.getDisplayName(), component); - } else { -+ // Purpur start -+ if (damageSource.isScissors) { -+ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgRunWithScissors, this.mob); -+ } -+ // Purpur end - return damageSource.getLocalizedDeathMessage(this.mob); - } - } -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index 14fcfd7c1d3a62833978e163f4e0d6f9b2203042..33210bc66dcc5fdf03fbf438ce8734b31c4e7975 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -54,6 +54,8 @@ public class DamageSource { - // CraftBukkit end - public @Nullable org.bukkit.block.BlockState explodedBlockState; // Paper - add exploded state - -+ public boolean isScissors; // Purpur -+ - public String toString() { - return "DamageSource (" + this.type().msgId() + ")"; - } -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -index 4604f8b38460e9113e966889a679d4547f24aff6..813916852774d6482791989252ecb67b945a8f84 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -@@ -267,4 +267,11 @@ public class DamageSources { - public DamageSource genericKill() { - return this.genericKill; - } -+ // Purpur start -+ public DamageSource scissors() { -+ DamageSource source = new DamageSource(this.damageTypes.getHolderOrThrow(DamageTypes.MAGIC)); -+ source.isScissors = true; -+ return source; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 61e3bce901bdbf111ffc06521b0e6d5369fce051..ea9d99990b718beaef4bee3ff75340656f3c76c5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -183,6 +183,7 @@ public class PurpurConfig { - public static String demoCommandOutput = "%s has been shown the demo screen"; - public static String pingCommandOutput = "%s's ping is %sms"; - public static String tpsbarCommandOutput = "Tpsbar toggled for "; -+ public static String dontRunWithScissors = "Don't run with scissors!"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -@@ -194,6 +195,12 @@ public class PurpurConfig { - demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); -+ dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); -+ } -+ -+ public static String deathMsgRunWithScissors = " slipped and fell on their shears"; -+ private static void deathMessages() { -+ deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors); - } - - public static String serverModName = "Purpur"; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fd2865f5be6bffd8fcbbac35151e9dde2149ab8d..dec3823baee9264ea5257993d9b30df337e2d9bd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -197,6 +197,10 @@ public class PurpurWorldConfig { - public List itemImmuneToExplosion = new ArrayList<>(); - public List itemImmuneToFire = new ArrayList<>(); - public List itemImmuneToLightning = new ArrayList<>(); -+ public boolean dontRunWithScissors = false; -+ public boolean ignoreScissorsInWater = false; -+ public boolean ignoreScissorsInLava = false; -+ public double scissorsRunningDamage = 1D; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -234,6 +238,10 @@ public class PurpurWorldConfig { - Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(key.toString())); - if (item != Items.AIR) itemImmuneToLightning.add(item); - }); -+ dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors); -+ ignoreScissorsInWater = getBoolean("gameplay-mechanics.item.shears.ignore-in-water", ignoreScissorsInWater); -+ ignoreScissorsInLava = getBoolean("gameplay-mechanics.item.shears.ignore-in-lava", ignoreScissorsInLava); -+ scissorsRunningDamage = getDouble("gameplay-mechanics.item.shears.sprinting-damage", scissorsRunningDamage); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0134-One-Punch-Man.patch b/patches/server/0134-One-Punch-Man.patch deleted file mode 100644 index 3d67eeb1e..000000000 --- a/patches/server/0134-One-Punch-Man.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Fourmisain <8464472+Fourmisain@users.noreply.github.com> -Date: Fri, 5 Mar 2021 17:42:35 -0500 -Subject: [PATCH] One Punch Man! - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 04b98c7c738682f5c1da27a6873a814eaca5d14b..9a4ac87ae3cc766ba4080cf5b4f3743d13bd6266 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2293,6 +2293,20 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - } - -+ // Purpur start -+ if (damagesource.getEntity() instanceof net.minecraft.world.entity.player.Player player && damagesource.getEntity().level().purpurConfig.creativeOnePunch) { -+ if (player.isCreative()) { -+ double attackDamage = 0; -+ for (AttributeModifier modifier : player.getMainHandItem().getAttributeModifiers(EquipmentSlot.MAINHAND).get(Attributes.ATTACK_DAMAGE)) { -+ attackDamage += modifier.getAmount(); -+ } -+ if (attackDamage == 0) { -+ this.setHealth(0); -+ } -+ } -+ } -+ // Purpur end -+ - if (f > 0 || !human) { - if (human) { - // PAIL: Be sure to drag all this code from the EntityHuman subclass each update. -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index dec3823baee9264ea5257993d9b30df337e2d9bd..c0a6940d923a85c923502c07df11cceba093cc95 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -346,6 +346,7 @@ public class PurpurWorldConfig { - public boolean teleportIfOutsideBorder = false; - public boolean totemOfUndyingWorksInInventory = false; - public boolean playerFixStuckPortal = false; -+ public boolean creativeOnePunch = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -364,6 +365,7 @@ public class PurpurWorldConfig { - teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); - totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); - playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); -+ creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0135-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch b/patches/server/0135-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch deleted file mode 100644 index a98af83ab..000000000 --- a/patches/server/0135-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Sun, 7 Mar 2021 19:08:16 -0500 -Subject: [PATCH] Configurable Ender Pearl cooldown, damage, and Endermite RNG - -- Survival and Creative Cooldown speed -- Damage dealt on pearl usage -- Endermite spawn chance - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -index e8114d89a3129e56c0329410a49ded63cc77cb4c..5c12355ed8b9eb1367173dd956975db279e2fb73 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -@@ -68,7 +68,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - Bukkit.getPluginManager().callEvent(teleEvent); - - if (!teleEvent.isCancelled() && entityplayer.connection.isAcceptingMessages()) { -- if (this.random.nextFloat() < 0.05F && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { -+ if (this.random.nextFloat() < this.level().purpurConfig.enderPearlEndermiteChance && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur - Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(this.level()); - - if (entityendermite != null) { -@@ -84,7 +84,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - entityplayer.connection.teleport(teleEvent.getTo()); - entity.resetFallDistance(); - CraftEventFactory.entityDamage = this; -- entity.hurt(this.damageSources().fall(), 5.0F); -+ entity.hurt(this.damageSources().fall(), this.level().purpurConfig.enderPearlDamage); // Purpur - CraftEventFactory.entityDamage = null; - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -index 749ab72edc0d2e9c6f1161415ab8d59d3d6ca976..897c202c0905040072a06fdfa2032a7f9461b088 100644 ---- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java -+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -@@ -36,7 +36,7 @@ public class EnderpearlItem extends Item { - - world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (net.minecraft.world.entity.Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); - user.awardStat(Stats.ITEM_USED.get(this)); -- user.getCooldowns().addCooldown(this, 20); -+ user.getCooldowns().addCooldown(this, user.getAbilities().instabuild ? world.purpurConfig.enderPearlCooldownCreative : world.purpurConfig.enderPearlCooldown); // Purpur - } else { - // Paper end - if (user instanceof net.minecraft.server.level.ServerPlayer) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c0a6940d923a85c923502c07df11cceba093cc95..02f332f2d1497668fbdf2e430d757f8699dc3a9a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -201,6 +201,10 @@ public class PurpurWorldConfig { - public boolean ignoreScissorsInWater = false; - public boolean ignoreScissorsInLava = false; - public double scissorsRunningDamage = 1D; -+ public float enderPearlDamage = 5.0F; -+ public int enderPearlCooldown = 20; -+ public int enderPearlCooldownCreative = 20; -+ public float enderPearlEndermiteChance = 0.05F; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -242,6 +246,10 @@ public class PurpurWorldConfig { - ignoreScissorsInWater = getBoolean("gameplay-mechanics.item.shears.ignore-in-water", ignoreScissorsInWater); - ignoreScissorsInLava = getBoolean("gameplay-mechanics.item.shears.ignore-in-lava", ignoreScissorsInLava); - scissorsRunningDamage = getDouble("gameplay-mechanics.item.shears.sprinting-damage", scissorsRunningDamage); -+ enderPearlDamage = (float) getDouble("gameplay-mechanics.item.ender-pearl.damage", enderPearlDamage); -+ enderPearlCooldown = getInt("gameplay-mechanics.item.ender-pearl.cooldown", enderPearlCooldown); -+ enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative); -+ enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0136-Config-to-ignore-nearby-mobs-when-sleeping.patch b/patches/server/0136-Config-to-ignore-nearby-mobs-when-sleeping.patch deleted file mode 100644 index 28d309f07..000000000 --- a/patches/server/0136-Config-to-ignore-nearby-mobs-when-sleeping.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Mon, 8 Mar 2021 16:46:54 -0500 -Subject: [PATCH] Config to ignore nearby mobs when sleeping - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 08532053face8fcba4a808830d618d2e5f0da8ee..fe1591b8ae12eaffdd44b23c4c55f580512f293e 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1430,7 +1430,7 @@ public class ServerPlayer extends Player { - return entitymonster.isPreventingPlayerRest(this); - }); - -- if (!list.isEmpty()) { -+ if (!this.level().purpurConfig.playerSleepNearMonsters && !list.isEmpty()) { // Purpur - return Either.left(Player.BedSleepingProblem.NOT_SAFE); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 02f332f2d1497668fbdf2e430d757f8699dc3a9a..7db005d02e762163c403a8358281afc2fa4f2df1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -355,6 +355,7 @@ public class PurpurWorldConfig { - public boolean totemOfUndyingWorksInInventory = false; - public boolean playerFixStuckPortal = false; - public boolean creativeOnePunch = false; -+ public boolean playerSleepNearMonsters = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -374,6 +375,7 @@ public class PurpurWorldConfig { - totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); - playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); - creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); -+ playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0137-Add-back-player-spawned-endermite-API.patch b/patches/server/0137-Add-back-player-spawned-endermite-API.patch deleted file mode 100644 index 9681f9e0c..000000000 --- a/patches/server/0137-Add-back-player-spawned-endermite-API.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 9 Mar 2021 16:10:39 -0500 -Subject: [PATCH] Add back player spawned endermite API - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -index cb0a2d522176a2d1d9785f95d205d4d924912a32..0cbf5a32611848e15e83eb0fc6f16c68337753c5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -31,6 +31,7 @@ import net.minecraft.world.level.block.state.BlockState; - public class Endermite extends Monster { - private static final int MAX_LIFE = 2400; - public int life; -+ private boolean isPlayerSpawned; // Purpur - - public Endermite(EntityType type, Level world) { - super(type, world); -@@ -64,6 +65,14 @@ public class Endermite extends Monster { - return this.level().purpurConfig.endermiteTakeDamageFromWater; - } - -+ public boolean isPlayerSpawned() { -+ return this.isPlayerSpawned; -+ } -+ -+ public void setPlayerSpawned(boolean playerSpawned) { -+ this.isPlayerSpawned = playerSpawned; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -@@ -116,12 +125,14 @@ public class Endermite extends Monster { - public void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); - this.life = nbt.getInt("Lifetime"); -+ this.isPlayerSpawned = nbt.getBoolean("PlayerSpawned"); // Purpur - } - - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putInt("Lifetime", this.life); -+ nbt.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -index 5c12355ed8b9eb1367173dd956975db279e2fb73..d7359c675707eade00f9b737fd67ef8d066e813f 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -@@ -72,6 +72,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(this.level()); - - if (entityendermite != null) { -+ entityendermite.setPlayerSpawned(true); // Purpur - entityendermite.moveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot()); - this.level().addFreshEntity(entityendermite, CreatureSpawnEvent.SpawnReason.ENDER_PEARL); - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java -index d657fd2c507a5b215aeab0a5f3e9c2ee892a27c8..985e9ec21c60a1f47973bd5fc53b96a6f9b7d04a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java -@@ -21,12 +21,12 @@ public class CraftEndermite extends CraftMonster implements Endermite { - - @Override - public boolean isPlayerSpawned() { -- return false; -+ return getHandle().isPlayerSpawned(); // Purpur - } - - @Override - public void setPlayerSpawned(boolean playerSpawned) { -- // Nop -+ getHandle().setPlayerSpawned(playerSpawned); // Purpur - } - // Paper start - @Override diff --git a/patches/server/0138-Config-Enderman-aggressiveness-towards-Endermites.patch b/patches/server/0138-Config-Enderman-aggressiveness-towards-Endermites.patch deleted file mode 100644 index 1bf71dd9e..000000000 --- a/patches/server/0138-Config-Enderman-aggressiveness-towards-Endermites.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 9 Mar 2021 16:10:39 -0500 -Subject: [PATCH] Config Enderman aggressiveness towards Endermites - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index d4d4ad5dfa776a39eabb840ed5b49acb8d37f587..ea596b7344f1849a8c6a8d536881c9a8b9cadb86 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -132,7 +132,7 @@ public class EnderMan extends Monster implements NeutralMob { - this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt)); - this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0])); -- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false)); -+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur - this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false)); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7db005d02e762163c403a8358281afc2fa4f2df1..a10a43afa33a025e89474786810db45241c145c5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1001,6 +1001,8 @@ public class PurpurWorldConfig { - public boolean endermanDespawnEvenWithBlock = false; - public boolean endermanBypassMobGriefing = false; - public boolean endermanTakeDamageFromWater = true; -+ public boolean endermanAggroEndermites = true; -+ public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -1010,11 +1012,17 @@ public class PurpurWorldConfig { - set("mobs.enderman.attributes.max-health", null); - set("mobs.enderman.attributes.max_health", oldValue); - } -+ if (PurpurConfig.version < 15) { -+ // remove old option -+ set("mobs.enderman.aggressive-towards-spawned-endermites", null); -+ } - endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); - endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); - endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock); - endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing); - endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater); -+ endermanAggroEndermites = getBoolean("mobs.enderman.aggressive-towards-endermites", endermanAggroEndermites); -+ endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0139-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch b/patches/server/0139-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch deleted file mode 100644 index 9a8380841..000000000 --- a/patches/server/0139-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 9 Mar 2021 16:16:01 -0500 -Subject: [PATCH] Config to ignore Dragon Head wearers and stare aggro - -Prevents Enderman from becoming aggresive towards players that are wearing a Dragon Head. -Adds functionality to a useless item! - -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index ea596b7344f1849a8c6a8d536881c9a8b9cadb86..f4bf81ebcc792c8e04fa700e84b13b3b688dbdac 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -269,7 +269,7 @@ public class EnderMan extends Monster implements NeutralMob { - // Paper end - ItemStack itemstack = (ItemStack) player.getInventory().armor.get(3); - -- if (itemstack.is(Blocks.CARVED_PUMPKIN.asItem())) { -+ if (this.level().purpurConfig.endermanDisableStareAggro || itemstack.is(Blocks.CARVED_PUMPKIN.asItem()) || (this.level().purpurConfig.endermanIgnorePlayerDragonHead && itemstack.is(net.minecraft.world.item.Items.DRAGON_HEAD))) { // Purpur - return false; - } else { - Vec3 vec3d = player.getViewVector(1.0F).normalize(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a10a43afa33a025e89474786810db45241c145c5..27581d6a560398e4e18425cfccd80e1dd9ec4dde 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1003,6 +1003,8 @@ public class PurpurWorldConfig { - public boolean endermanTakeDamageFromWater = true; - public boolean endermanAggroEndermites = true; - public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false; -+ public boolean endermanIgnorePlayerDragonHead = false; -+ public boolean endermanDisableStareAggro = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -1023,6 +1025,8 @@ public class PurpurWorldConfig { - endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater); - endermanAggroEndermites = getBoolean("mobs.enderman.aggressive-towards-endermites", endermanAggroEndermites); - endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned); -+ endermanIgnorePlayerDragonHead = getBoolean("mobs.enderman.ignore-players-wearing-dragon-head", endermanIgnorePlayerDragonHead); -+ endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0140-Tick-fluids-config.patch b/patches/server/0140-Tick-fluids-config.patch deleted file mode 100644 index 7ad38eb66..000000000 --- a/patches/server/0140-Tick-fluids-config.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 15 Mar 2021 03:52:17 -0500 -Subject: [PATCH] Tick fluids config - - -diff --git a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -index bbabe4ad8afcc3a2069f6e9d4a9adcb643266894..55419bd653f7f5391fa13cd15a0b00fbff5e9c39 100644 ---- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -@@ -105,7 +105,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) { -- if (this.shouldSpreadLiquid(world, pos, state)) { -+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur - world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - } - -@@ -133,7 +133,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { -- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { -+ if (world.getMinecraftWorld().purpurConfig.tickFluids && state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { // Purpur - world.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world)); - } - -@@ -142,7 +142,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { -- if (this.shouldSpreadLiquid(world, pos, state)) { -+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur - world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 27581d6a560398e4e18425cfccd80e1dd9ec4dde..ea25998e4855b6e900ed96e1d814231da97ea325 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -129,6 +129,7 @@ public class PurpurWorldConfig { - public boolean noteBlockIgnoreAbove = false; - public boolean persistentDroppableEntityDisplayNames = true; - public boolean projectilesBypassMobGriefing = false; -+ public boolean tickFluids = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; -@@ -148,6 +149,7 @@ public class PurpurWorldConfig { - noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); -+ tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); diff --git a/patches/server/0141-Config-to-disable-Llama-caravans.patch b/patches/server/0141-Config-to-disable-Llama-caravans.patch deleted file mode 100644 index 55d233b79..000000000 --- a/patches/server/0141-Config-to-disable-Llama-caravans.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Wed, 17 Mar 2021 10:12:53 -0400 -Subject: [PATCH] Config to disable Llama caravans - -Disables the mechanic where llamas follow leashed llamas. - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -index 849f0c7c6d13df00d90211a48d8b56ab156812b8..3fc9528201fb96d6a0f905afe0b6a82ec88a7235 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -@@ -22,7 +22,7 @@ public class LlamaFollowCaravanGoal extends Goal { - - @Override - public boolean canUse() { -- if (!this.llama.shouldJoinCaravan) return false; // Purpur -+ if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur - if (!this.llama.isLeashed() && !this.llama.inCaravan()) { - List list = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0D, 4.0D, 9.0D), (entity) -> { - EntityType entityType = entity.getType(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index b7450d182ecf10ef17ac9eb3fce04e4334013c91..fac46dd905f9a634cff393494f6ff0404eb17fb7 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -539,7 +539,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder -Date: Tue, 16 Mar 2021 19:50:58 -0400 -Subject: [PATCH] Config to make Creepers explode on death - -Creepers exploded after being killed in the alpha days. This brings that back. - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index e8191f0df3420d5a531cf226547a177732ff65ea..fd3b1e92a626402112bbd371a0ac1264a2c19c32 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -63,6 +63,7 @@ public class Creeper extends Monster implements PowerableMob { - private int spacebarCharge = 0; - private int prevSpacebarCharge = 0; - private int powerToggleDelay = 0; -+ private boolean exploding = false; - // Purpur end - - public Creeper(EntityType type, Level world) { -@@ -268,6 +269,14 @@ public class Creeper extends Monster implements PowerableMob { - return this.level().purpurConfig.creeperTakeDamageFromWater; - } - -+ @Override -+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource damagesource) { -+ if (!exploding && this.level().purpurConfig.creeperExplodeWhenKilled && damagesource.getEntity() instanceof net.minecraft.server.level.ServerPlayer) { -+ this.explodeCreeper(); -+ } -+ return super.dropAllDeathLoot(damagesource); -+ } -+ - @Override - protected SoundEvent getHurtSound(DamageSource source) { - return SoundEvents.CREEPER_HURT; -@@ -359,6 +368,7 @@ public class Creeper extends Monster implements PowerableMob { - } - - public void explodeCreeper() { -+ this.exploding = true; // Purpur - if (!this.level().isClientSide) { - float f = this.isPowered() ? 2.0F : 1.0F; - -@@ -377,7 +387,7 @@ public class Creeper extends Monster implements PowerableMob { - } - // CraftBukkit end - } -- -+ this.exploding = false; // Purpur - } - - private void spawnLingeringCloud() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index eed4919f50c081fc98ec448b99ee34083f72aa26..f477176dd6ba0b093fe81f11acfe98ddb25723cd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -855,6 +855,7 @@ public class PurpurWorldConfig { - public boolean creeperAllowGriefing = true; - public boolean creeperBypassMobGriefing = false; - public boolean creeperTakeDamageFromWater = false; -+ public boolean creeperExplodeWhenKilled = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -869,6 +870,7 @@ public class PurpurWorldConfig { - creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing); - creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing); - creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater); -+ creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled); - } - - public boolean dolphinRidable = false; diff --git a/patches/server/0143-Configurable-ravager-griefable-blocks-list.patch b/patches/server/0143-Configurable-ravager-griefable-blocks-list.patch deleted file mode 100644 index 09d963849..000000000 --- a/patches/server/0143-Configurable-ravager-griefable-blocks-list.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 17 Mar 2021 14:54:43 -0500 -Subject: [PATCH] Configurable ravager griefable blocks list - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index d56e80014621dbd7e445f2f753807bd2b751310d..1de85b0a3aafb725452197de245e884cacdb4e3b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -208,7 +208,7 @@ public class Ravager extends Raider { - BlockState iblockdata = this.level().getBlockState(blockposition); - Block block = iblockdata.getBlock(); - -- if (block instanceof LeavesBlock) { -+ if (this.level().purpurConfig.ravagerGriefableBlocks.contains(block)) { // Purpur - // CraftBukkit start - if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state - continue; -diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java -index 5ba5a510b30917235e135ccda1b7c98cb7dd93ec..373fda589014c71c07170ec6707d5ff66d46ddd5 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -168,7 +168,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { - @Override - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { - if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper -- if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), (!world.purpurConfig.ravagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)))) { // CraftBukkit // Purpur -+ if (entity instanceof Ravager && world.purpurConfig.ravagerGriefableBlocks.contains(world.getBlockState(pos).getBlock()) && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), (!world.purpurConfig.ravagerBypassMobGriefing && !world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)))) { // CraftBukkit // Purpur - world.destroyBlock(pos, true, entity); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f477176dd6ba0b093fe81f11acfe98ddb25723cd..b946f607cd1689e6eae4759e100500c4db5c647f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1707,6 +1707,7 @@ public class PurpurWorldConfig { - public double ravagerMaxHealth = 100.0D; - public boolean ravagerBypassMobGriefing = false; - public boolean ravagerTakeDamageFromWater = false; -+ public List ravagerGriefableBlocks = new ArrayList<>(); - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -@@ -1719,6 +1720,23 @@ public class PurpurWorldConfig { - ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); - ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing); - ravagerTakeDamageFromWater = getBoolean("mobs.ravager.takes-damage-from-water", ravagerTakeDamageFromWater); -+ getList("mobs.ravager.griefable-blocks", new ArrayList(){{ -+ add("minecraft:oak_leaves"); -+ add("minecraft:spruce_leaves"); -+ add("minecraft:birch_leaves"); -+ add("minecraft:jungle_leaves"); -+ add("minecraft:acacia_leaves"); -+ add("minecraft:dark_oak_leaves"); -+ add("minecraft:beetroots"); -+ add("minecraft:carrots"); -+ add("minecraft:potatoes"); -+ add("minecraft:wheat"); -+ }}).forEach(key -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString())); -+ if (!block.defaultBlockState().isAir()) { -+ ravagerGriefableBlocks.add(block); -+ } -+ }); - } - - public boolean salmonRidable = false; diff --git a/patches/server/0144-Sneak-to-bulk-process-composter.patch b/patches/server/0144-Sneak-to-bulk-process-composter.patch deleted file mode 100644 index f5a12925f..000000000 --- a/patches/server/0144-Sneak-to-bulk-process-composter.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 7 May 2023 22:26:59 -0700 -Subject: [PATCH] Sneak to bulk process composter - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index 618ab9a2903f6d4139acd4aaa2e6db0a26e88ba9..26f78343b5ecaafc3252654848c21a5405379c0e 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -578,7 +578,7 @@ public class ServerPlayerGameMode { - boolean flag1 = player.isSecondaryUseActive() && flag; - ItemStack itemstack1 = stack.copy(); - -- if (!flag1) { -+ if (!flag1 || (player.level().purpurConfig.composterBulkProcess && iblockdata.is(Blocks.COMPOSTER))) { // Purpur - enuminteractionresult = iblockdata.use(world, player, hand, hitResult); - - if (enuminteractionresult.consumesAction()) { -diff --git a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -index 10d3912ef043eefdf89105332e29b0d2bf4a5539..596b77306f690a2298835f0f0fea1abee2a7c85d 100644 ---- a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -@@ -229,20 +229,28 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { - ItemStack itemstack = player.getItemInHand(hand); - - if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(itemstack.getItem())) { -- if (i < 7 && !world.isClientSide) { -- BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, itemstack); -- // Paper start - handle cancelled events -- if (iblockdata1 == null) { -- return InteractionResult.PASS; -- } -- // Paper end -+ // Purpur start -+ BlockState newState = process(i, state, world, itemstack, pos, player); -+ if (newState == null) { -+ return InteractionResult.PASS; -+ } - -- world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0); -- player.awardStat(Stats.ITEM_USED.get(itemstack.getItem())); -- if (!player.getAbilities().instabuild) { -- itemstack.shrink(1); -- } -+ if (world.purpurConfig.composterBulkProcess && player.isShiftKeyDown() && newState != state) { -+ BlockState oldState; -+ int oldCount, newCount, oldLevel, newLevel; -+ do { -+ oldState = newState; -+ oldCount = itemstack.getCount(); -+ oldLevel = oldState.getValue(ComposterBlock.LEVEL); -+ newState = process(oldLevel, oldState, world, itemstack, pos, player); -+ if (newState == null) { -+ return InteractionResult.PASS; -+ } -+ newCount = itemstack.getCount(); -+ newLevel = newState.getValue(ComposterBlock.LEVEL); -+ } while (newCount > 0 && (newCount != oldCount || newLevel != oldLevel || newState != oldState)); - } -+ // Purpur end - - return InteractionResult.sidedSuccess(world.isClientSide); - } else if (i == 8) { -@@ -253,6 +261,26 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { - } - } - -+ private static BlockState process(int level, BlockState state, Level world, ItemStack itemstack, BlockPos pos, Player player) { -+ if (level < 7 && !world.isClientSide) { -+ BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, itemstack); -+ // Paper start - handle cancelled events -+ if (iblockdata1 == null) { -+ return iblockdata1; -+ } -+ // Paper end -+ -+ world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0); -+ player.awardStat(Stats.ITEM_USED.get(itemstack.getItem())); -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(1); -+ } -+ return iblockdata1; -+ } -+ return state; -+ } -+ // Purpur end -+ - public static BlockState insertItem(Entity user, BlockState state, ServerLevel world, ItemStack stack, BlockPos pos) { - int i = (Integer) state.getValue(ComposterBlock.LEVEL); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b946f607cd1689e6eae4759e100500c4db5c647f..57f5792a06e55c7d7e9f0bf20c89c5d4337abd8d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -483,6 +483,11 @@ public class PurpurWorldConfig { - chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); - } - -+ public boolean composterBulkProcess = false; -+ private void composterSettings() { -+ composterBulkProcess = getBoolean("blocks.composter.sneak-to-bulk-process", composterBulkProcess); -+ } -+ - public boolean dispenserApplyCursedArmor = true; - public boolean dispenserPlaceAnvils = false; - private void dispenserSettings() { diff --git a/patches/server/0145-Config-for-skipping-night.patch b/patches/server/0145-Config-for-skipping-night.patch deleted file mode 100644 index e6ef57be2..000000000 --- a/patches/server/0145-Config-for-skipping-night.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Thu, 18 Mar 2021 06:22:40 -0400 -Subject: [PATCH] Config for skipping night - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 0e8452f6a8300992f397fb15db763304d72fe3b9..49c412f46854ee877de1721b08629e8a3cd319f9 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -805,7 +805,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); - long j; - -- if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { -+ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { - // CraftBukkit start - j = this.levelData.getDayTime() + 24000L; - TimeSkipEvent event = new TimeSkipEvent(this.getWorld(), TimeSkipEvent.SkipReason.NIGHT_SKIP, (j - j % 24000L) - this.getDayTime()); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 57f5792a06e55c7d7e9f0bf20c89c5d4337abd8d..c324b47449148a2b07b748cca456faa67c9358ba 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -358,6 +358,7 @@ public class PurpurWorldConfig { - public boolean playerFixStuckPortal = false; - public boolean creativeOnePunch = false; - public boolean playerSleepNearMonsters = false; -+ public boolean playersSkipNight = true; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -378,6 +379,7 @@ public class PurpurWorldConfig { - playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); - creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); - playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters); -+ playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0146-Add-config-for-villager-trading.patch b/patches/server/0146-Add-config-for-villager-trading.patch deleted file mode 100644 index e77624d45..000000000 --- a/patches/server/0146-Add-config-for-villager-trading.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Thu, 18 Mar 2021 07:23:27 -0400 -Subject: [PATCH] Add config for villager trading - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 01a1732ca13300b60979d250ebf156ca68bb014f..444dbab9a1c95d1f8bc97e165e5302473a29c7e8 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -371,7 +371,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur - } else { - if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur -- if (!this.level().isClientSide && !this.offers.isEmpty()) { -+ if (this.level().purpurConfig.villagerAllowTrading && !this.offers.isEmpty()) { - this.startTrading(player); - } - -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 5c6f1d3ffa2a2b6990d2406b14679e8286f60729..68d263a7647e395d981014f7cc761fe76fc74cb6 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -149,7 +149,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return tryRide(player, hand, InteractionResult.sidedSuccess(this.level().isClientSide)); // Purpur - } else { - if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur -- if (!this.level().isClientSide) { -+ if (this.level().purpurConfig.wanderingTraderAllowTrading) { - this.setTradingPlayer(player); - this.openTradingScreen(player, this.getDisplayName(), 1); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c324b47449148a2b07b748cca456faa67c9358ba..37ee42c46d469e11dc48eb292442beaac3e74445 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2127,6 +2127,7 @@ public class PurpurWorldConfig { - public boolean villagerClericFarmersThrowWarts = true; - public boolean villagerBypassMobGriefing = false; - public boolean villagerTakeDamageFromWater = false; -+ public boolean villagerAllowTrading = true; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2145,6 +2146,7 @@ public class PurpurWorldConfig { - villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts); - villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing); - villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater); -+ villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); - } - - public boolean vindicatorRidable = false; -@@ -2174,6 +2176,7 @@ public class PurpurWorldConfig { - public boolean wanderingTraderFollowEmeraldBlock = false; - public boolean wanderingTraderCanBeLeashed = false; - public boolean wanderingTraderTakeDamageFromWater = false; -+ public boolean wanderingTraderAllowTrading = true; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -2187,6 +2190,7 @@ public class PurpurWorldConfig { - wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock); - wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed); - wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater); -+ wanderingTraderAllowTrading = getBoolean("mobs.wandering_trader.allow-trading", wanderingTraderAllowTrading); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0147-Allow-infinity-on-crossbows.patch b/patches/server/0147-Allow-infinity-on-crossbows.patch deleted file mode 100644 index d41d1c95a..000000000 --- a/patches/server/0147-Allow-infinity-on-crossbows.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Thu, 18 Mar 2021 12:25:29 -0400 -Subject: [PATCH] Allow infinity on crossbows - - -diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java -index eede02c3f125d230af537bb67bebed9b88f7d1b4..1ae5e17b95d994d69b060fc06c0a807c40a90f17 100644 ---- a/src/main/java/net/minecraft/world/item/CrossbowItem.java -+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java -@@ -114,7 +114,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable { - // Paper end - int i = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MULTISHOT, crossbow); - int j = i == 0 ? 1 : 3; -- boolean flag = !consume || shooter instanceof Player && ((Player) shooter).getAbilities().instabuild; // Paper - add consume -+ boolean flag = !consume || shooter instanceof Player && ((Player) shooter).getAbilities().instabuild || (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, crossbow) > 0); // Paper - add consume // Purpur - ItemStack itemstack1 = shooter.getProjectile(crossbow); - ItemStack itemstack2 = itemstack1.copy(); - -@@ -300,7 +300,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable { - - for (int i = 0; i < list.size(); ++i) { - ItemStack itemstack1 = (ItemStack) list.get(i); -- boolean flag = entity instanceof Player && ((Player) entity).getAbilities().instabuild; -+ boolean flag = entity instanceof Player && ((Player) entity).getAbilities().instabuild || (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, stack) > 0); // Purpur - - if (!itemstack1.isEmpty()) { - if (i == 0) { -diff --git a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java -index 2c4ce164ab3011f372ff1719c8d4a3331d8db55f..27512787b37381a5236b1b473e9ce3f06df8e2d0 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/ArrowInfiniteEnchantment.java -@@ -7,6 +7,14 @@ public class ArrowInfiniteEnchantment extends Enchantment { - super(weight, EnchantmentCategory.BOW, slotTypes); - } - -+ // Purpur start -+ @Override -+ public boolean canEnchant(net.minecraft.world.item.ItemStack stack) { -+ // we have to cheat the system because this class is loaded before purpur's config is loaded -+ return (org.purpurmc.purpur.PurpurConfig.allowCrossbowInfinity ? EnchantmentCategory.BOW_AND_CROSSBOW : EnchantmentCategory.BOW).canEnchant(stack.getItem()); -+ } -+ // Purpur end -+ - @Override - public int getMinCost(int level) { - return 20; -diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -index 246516e67db0b8b197b287c067d5a0163d8bde22..859435f747ceef860cb4e9e825a7353ea3b90798 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -@@ -121,6 +121,14 @@ public enum EnchantmentCategory { - public boolean canEnchant(Item item) { - return item instanceof Vanishable || Block.byItem(item) instanceof Vanishable || BREAKABLE.canEnchant(item); - } -+ // Purpur start -+ }, -+ BOW_AND_CROSSBOW { -+ @Override -+ public boolean canEnchant(Item item) { -+ return item instanceof BowItem || item instanceof CrossbowItem; -+ } -+ // Purpur end - }; - - public abstract boolean canEnchant(Item item); -diff --git a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java -index 3d0ce0803e1da8a2681a3cb41096ac942ece54a1..bcd075a771c7f43c6d1549aeec2ccb20ee168b57 100644 ---- a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java -+++ b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java -@@ -59,6 +59,7 @@ public class CraftEnchantment extends Enchantment { - return EnchantmentTarget.CROSSBOW; - case VANISHABLE: - return EnchantmentTarget.VANISHABLE; -+ case BOW_AND_CROSSBOW: return EnchantmentTarget.BOW_AND_CROSSBOW; // Purpur - default: - return null; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index ea9d99990b718beaef4bee3ff75340656f3c76c5..55963ed03a2e552636035b498cf7441bd4c3ee59 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -284,6 +284,7 @@ public class PurpurConfig { - } - - public static boolean allowInfinityMending = false; -+ public static boolean allowCrossbowInfinity = false; - private static void enchantmentSettings() { - if (version < 5) { - boolean oldValue = getBoolean("settings.enchantment.allow-infinite-and-mending-together", false); -@@ -291,6 +292,7 @@ public class PurpurConfig { - set("settings.enchantment.allow-infinite-and-mending-together", null); - } - allowInfinityMending = getBoolean("settings.enchantment.allow-infinity-and-mending-together", allowInfinityMending); -+ allowCrossbowInfinity = getBoolean("settings.enchantment.allow-infinity-on-crossbow", allowCrossbowInfinity); - } - - public static boolean endermanShortHeight = false; diff --git a/patches/server/0148-Drowning-Settings.patch b/patches/server/0148-Drowning-Settings.patch deleted file mode 100644 index 39028e93c..000000000 --- a/patches/server/0148-Drowning-Settings.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sun, 21 Mar 2021 15:26:52 -0400 -Subject: [PATCH] Drowning Settings - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 36e9b635f81f2799cd210075bced9e976a90a351..dbd61da31192896ced9839cf8a3b123a6625ae60 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3305,7 +3305,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public int getMaxAirSupply() { -- return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() -+ return this.level == null? this.maxAirTicks : this.level().purpurConfig.drowningAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() // Purpur - } - - public int getAirSupply() { -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 9a4ac87ae3cc766ba4080cf5b4f3743d13bd6266..217a0082f7f260678253f31e381a432099555177 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -439,7 +439,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - if (flag1) { - this.setAirSupply(this.decreaseAirSupply(this.getAirSupply())); -- if (this.getAirSupply() == -20) { -+ if (this.getAirSupply() == -this.level().purpurConfig.drowningDamageInterval) { // Purpur - this.setAirSupply(0); - Vec3 vec3d = this.getDeltaMovement(); - -@@ -451,7 +451,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d2, this.getY() + d3, this.getZ() + d4, vec3d.x, vec3d.y, vec3d.z); - } - -- this.hurt(this.damageSources().drown(), 2.0F); -+ this.hurt(this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur - } - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 37ee42c46d469e11dc48eb292442beaac3e74445..f7e9f7ebb35dcbcbd8dce440824e4729e0239503 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -164,6 +164,15 @@ public class PurpurWorldConfig { - nighttimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.nighttime", nighttimeTicks); - } - -+ public int drowningAirTicks = 300; -+ public int drowningDamageInterval = 20; -+ public double damageFromDrowning = 2.0F; -+ private void drowningSettings() { -+ drowningAirTicks = getInt("gameplay-mechanics.drowning.air-ticks", drowningAirTicks); -+ drowningDamageInterval = getInt("gameplay-mechanics.drowning.ticks-per-damage", drowningDamageInterval); -+ damageFromDrowning = getDouble("gameplay-mechanics.drowning.damage-from-drowning", damageFromDrowning); -+ } -+ - public int elytraDamagePerSecond = 1; - public double elytraDamageMultiplyBySpeed = 0; - public boolean elytraIgnoreUnbreaking = false; diff --git a/patches/server/0149-Break-individual-slabs-when-sneaking.patch b/patches/server/0149-Break-individual-slabs-when-sneaking.patch deleted file mode 100644 index d302f347c..000000000 --- a/patches/server/0149-Break-individual-slabs-when-sneaking.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 23 Mar 2021 19:38:53 -0500 -Subject: [PATCH] Break individual slabs when sneaking - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index 26f78343b5ecaafc3252654848c21a5405379c0e..b9e9f20842274024dd1e2a0253666002c914a2b2 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -398,6 +398,7 @@ public class ServerPlayerGameMode { - } else {capturedBlockEntity = true;} // Paper end - return false; - } -+ if (this.player.level().purpurConfig.slabHalfBreak && this.player.isShiftKeyDown() && iblockdata.getBlock() instanceof net.minecraft.world.level.block.SlabBlock && ((net.minecraft.world.level.block.SlabBlock) iblockdata.getBlock()).halfBreak(iblockdata, pos, this.player)) return true; // Purpur - } - // CraftBukkit end - -diff --git a/src/main/java/net/minecraft/world/level/block/SlabBlock.java b/src/main/java/net/minecraft/world/level/block/SlabBlock.java -index 18b603d646081926343dea108b55d641df1c2c34..370772b1297b78bcc7419684015830a87c4d9a17 100644 ---- a/src/main/java/net/minecraft/world/level/block/SlabBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SlabBlock.java -@@ -130,4 +130,25 @@ public class SlabBlock extends Block implements SimpleWaterloggedBlock { - return false; - } - } -+ -+ // Purpur start -+ public boolean halfBreak(BlockState state, BlockPos pos, net.minecraft.server.level.ServerPlayer player) { -+ if (state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE) { -+ return false; -+ } -+ net.minecraft.world.phys.HitResult result = player.getRayTrace(16, net.minecraft.world.level.ClipContext.Fluid.NONE); -+ if (result.getType() != net.minecraft.world.phys.HitResult.Type.BLOCK) { -+ return false; -+ } -+ double hitY = result.getLocation().y(); -+ int blockY = org.bukkit.util.NumberConversions.floor(hitY); -+ player.level().setBlock(pos, state.setValue(SlabBlock.TYPE, (hitY - blockY > 0.5 || blockY - pos.getY() == 1) ? SlabType.BOTTOM : SlabType.TOP), 3); -+ if (!player.getAbilities().instabuild) { -+ net.minecraft.world.entity.item.ItemEntity item = new net.minecraft.world.entity.item.ItemEntity(player.level(), pos.getX(), pos.getY(), pos.getZ(), new ItemStack(asItem())); -+ item.setDefaultPickUpDelay(); -+ player.level().addFreshEntity(item); -+ } -+ return true; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f7e9f7ebb35dcbcbd8dce440824e4729e0239503..fcd78eb993ed41ae560bd76d8f090a3e5931e707 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -604,6 +604,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean slabHalfBreak = false; -+ private void slabSettings() { -+ slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak); -+ } -+ - public boolean spawnerDeactivateByRedstone = false; - private void spawnerSettings() { - spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); diff --git a/patches/server/0150-Config-to-disable-hostile-mob-spawn-on-ice.patch b/patches/server/0150-Config-to-disable-hostile-mob-spawn-on-ice.patch deleted file mode 100644 index f8eb243fd..000000000 --- a/patches/server/0150-Config-to-disable-hostile-mob-spawn-on-ice.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 23 Mar 2021 15:40:45 -0400 -Subject: [PATCH] Config to disable hostile mob spawn on ice - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Monster.java b/src/main/java/net/minecraft/world/entity/monster/Monster.java -index 55c245d0dfa369dc6de2197ae37335fba4fae4ae..c9b40515f4c2ff1eedfc9510930c3baebc078ebd 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Monster.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java -@@ -89,6 +89,14 @@ public abstract class Monster extends PathfinderMob implements Enemy { - } - - public static boolean isDarkEnoughToSpawn(ServerLevelAccessor world, BlockPos pos, RandomSource random) { -+ // Purpur start -+ if (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce || !world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce) { -+ net.minecraft.world.level.block.state.BlockState spawnBlock = world.getBlockState(pos.below()); -+ if ((!world.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.PACKED_ICE)) || (!world.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.BLUE_ICE))) { -+ return false; -+ } -+ } -+ // Purpur end - if (world.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) { - return false; - } else { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fcd78eb993ed41ae560bd76d8f090a3e5931e707..99fefdf8f5d34189a0e046cd9dd9ad9a240c3948 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -569,6 +569,13 @@ public class PurpurWorldConfig { - furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath); - } - -+ public boolean mobsSpawnOnPackedIce = true; -+ public boolean mobsSpawnOnBlueIce = true; -+ private void iceSettings() { -+ mobsSpawnOnPackedIce = getBoolean("blocks.packed_ice.allow-mob-spawns", mobsSpawnOnPackedIce); -+ mobsSpawnOnBlueIce = getBoolean("blocks.blue_ice.allow-mob-spawns", mobsSpawnOnBlueIce); -+ } -+ - public int lavaInfiniteRequiredSources = 2; - public int lavaSpeedNether = 10; - public int lavaSpeedNotNether = 30; diff --git a/patches/server/0151-Config-to-show-Armor-Stand-arms-on-spawn.patch b/patches/server/0151-Config-to-show-Armor-Stand-arms-on-spawn.patch deleted file mode 100644 index afa7b3135..000000000 --- a/patches/server/0151-Config-to-show-Armor-Stand-arms-on-spawn.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 23 Mar 2021 22:42:20 -0400 -Subject: [PATCH] Config to show Armor Stand arms on spawn - - -diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -index b48a3d318a14889c68a1de5c1997b1090aab4029..4c5f20bafe589fbd4a034408edb721ac79bbc666 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -114,6 +114,7 @@ public class ArmorStand extends LivingEntity { - this.leftLegPose = ArmorStand.DEFAULT_LEFT_LEG_POSE; - this.rightLegPose = ArmorStand.DEFAULT_RIGHT_LEG_POSE; - this.setMaxUpStep(0.0F); -+ this.setShowArms(world != null && world.purpurConfig.armorstandPlaceWithArms); // Purpur - } - - public ArmorStand(Level world, double x, double y, double z) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 99fefdf8f5d34189a0e046cd9dd9ad9a240c3948..3610fe6652e156066745ecac3551d6610a868603 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -102,6 +102,7 @@ public class PurpurWorldConfig { - public boolean armorstandMovement = true; - public boolean armorstandWaterMovement = true; - public boolean armorstandWaterFence = true; -+ public boolean armorstandPlaceWithArms = false; - private void armorstandSettings() { - armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); - armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible); -@@ -109,6 +110,7 @@ public class PurpurWorldConfig { - armorstandMovement = getBoolean("gameplay-mechanics.armorstand.can-movement-tick", armorstandMovement); - armorstandWaterMovement = getBoolean("gameplay-mechanics.armorstand.can-move-in-water", armorstandWaterMovement); - armorstandWaterFence = getBoolean("gameplay-mechanics.armorstand.can-move-in-water-over-fence", armorstandWaterFence); -+ armorstandPlaceWithArms = getBoolean("gameplay-mechanics.armorstand.place-with-arms-visible", armorstandPlaceWithArms); - } - - public boolean arrowMovementResetsDespawnCounter = true; diff --git a/patches/server/0152-Option-to-make-doors-require-redstone.patch b/patches/server/0152-Option-to-make-doors-require-redstone.patch deleted file mode 100644 index 259de554d..000000000 --- a/patches/server/0152-Option-to-make-doors-require-redstone.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 24 Mar 2021 04:40:11 -0500 -Subject: [PATCH] Option to make doors require redstone - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java -index 42ae4d293a420f0b8eb476df6389b2e7a693895f..97c20c5b89e6d7e4ed844eff39ee55dfa8988d37 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java -@@ -57,7 +57,7 @@ public class InteractWithDoor { - - if (iblockdata.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition)) { // Purpur - DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock(); - - if (!blockdoor.isOpen(iblockdata)) { -@@ -79,7 +79,7 @@ public class InteractWithDoor { - - if (iblockdata1.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition1)) { // Purpur - DoorBlock blockdoor1 = (DoorBlock) iblockdata1.getBlock(); - - if (!blockdoor1.isOpen(iblockdata1)) { -@@ -122,7 +122,7 @@ public class InteractWithDoor { - - if (!iblockdata.is(BlockTags.WOODEN_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) || DoorBlock.requiresRedstone(entity.level(), iblockdata, blockposition)) { // Purpur - iterator.remove(); - } else { - DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock(); -diff --git a/src/main/java/net/minecraft/world/level/block/DoorBlock.java b/src/main/java/net/minecraft/world/level/block/DoorBlock.java -index c028a7158e41a0754abb8e24dcd647633fbf3fe8..cd65d32f4af016d4937e598c71386a3072f4c490 100644 ---- a/src/main/java/net/minecraft/world/level/block/DoorBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/DoorBlock.java -@@ -167,6 +167,7 @@ public class DoorBlock extends Block { - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - if (!this.type.canOpenByHand()) { - return InteractionResult.PASS; -+ } else if (requiresRedstone(world, state, pos)) { return InteractionResult.CONSUME; // Purpur - } else { - state = (BlockState) state.cycle(DoorBlock.OPEN); - world.setBlock(pos, state, 10); -@@ -270,4 +271,18 @@ public class DoorBlock extends Block { - flag = false; - return flag; - } -+ -+ // Purpur start -+ public static boolean requiresRedstone(Level level, BlockState state, BlockPos pos) { -+ if (level.purpurConfig.doorRequiresRedstone.contains(state.getBlock())) { -+ // force update client -+ BlockPos otherPos = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN); -+ BlockState otherState = level.getBlockState(otherPos); -+ level.sendBlockUpdated(pos, state, state, 3); -+ level.sendBlockUpdated(otherPos, otherState, otherState, 3); -+ return true; -+ } -+ return false; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3610fe6652e156066745ecac3551d6610a868603..761f8546ce1314e488aaa21faa0a912b64699c2b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -508,6 +508,16 @@ public class PurpurWorldConfig { - dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils); - } - -+ public List doorRequiresRedstone = new ArrayList<>(); -+ private void doorSettings() { -+ getList("blocks.door.requires-redstone", new ArrayList()).forEach(key -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString())); -+ if (!block.defaultBlockState().isAir()) { -+ doorRequiresRedstone.add(block); -+ } -+ }); -+ } -+ - public boolean baselessEndCrystalExplode = true; - public double baselessEndCrystalExplosionPower = 6.0D; - public boolean baselessEndCrystalExplosionFire = false; diff --git a/patches/server/0153-Config-to-allow-for-unsafe-enchants.patch b/patches/server/0153-Config-to-allow-for-unsafe-enchants.patch deleted file mode 100644 index 000e45e72..000000000 --- a/patches/server/0153-Config-to-allow-for-unsafe-enchants.patch +++ /dev/null @@ -1,131 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Wed, 24 Mar 2021 17:59:54 -0400 -Subject: [PATCH] Config to allow for unsafe enchants - - -diff --git a/src/main/java/net/minecraft/server/commands/EnchantCommand.java b/src/main/java/net/minecraft/server/commands/EnchantCommand.java -index 664cbce2e06fcb95d3d3d6c5302fc9119f938925..bc9778c705d23acd84fa1cdeff6b403b4cda3686 100644 ---- a/src/main/java/net/minecraft/server/commands/EnchantCommand.java -+++ b/src/main/java/net/minecraft/server/commands/EnchantCommand.java -@@ -48,7 +48,7 @@ public class EnchantCommand { - - private static int enchant(CommandSourceStack source, Collection targets, Holder enchantment, int level) throws CommandSyntaxException { - Enchantment enchantment2 = enchantment.value(); -- if (level > enchantment2.getMaxLevel()) { -+ if (!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && level > enchantment2.getMaxLevel()) { // Purpur - throw ERROR_LEVEL_TOO_HIGH.create(level, enchantment2.getMaxLevel()); - } else { - int i = 0; -@@ -58,7 +58,7 @@ public class EnchantCommand { - LivingEntity livingEntity = (LivingEntity)entity; - ItemStack itemStack = livingEntity.getMainHandItem(); - if (!itemStack.isEmpty()) { -- if (enchantment2.canEnchant(itemStack) && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantments(itemStack).keySet(), enchantment2)) { -+ if ((enchantment2.canEnchant(itemStack) && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantments(itemStack).keySet(), enchantment2)) || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && !itemStack.hasEnchantment(enchantment2))) { // Purpur - itemStack.enchant(enchantment2, level); - ++i; - } else if (targets.size() == 1) { -diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -index 8a7c30e316db4960b0b62ca0e366c19febead214..fdf2bf10a533c3d4ee145880e6949dadf6ce5410 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -228,7 +228,8 @@ public class AnvilMenu extends ItemCombinerMenu { - int i2 = (Integer) map1.get(enchantment); - - i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1); -- boolean flag3 = canDoUnsafeEnchants || enchantment.canEnchant(itemstack); // Purpur -+ boolean flag3 = canDoUnsafeEnchants || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants && org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants) || enchantment.canEnchant(itemstack); // Purpur -+ boolean flag4 = true; // Purpur - - if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) { - flag3 = true; -@@ -240,16 +241,20 @@ public class AnvilMenu extends ItemCombinerMenu { - Enchantment enchantment1 = (Enchantment) iterator1.next(); - - if (enchantment1 != enchantment && !enchantment.isCompatibleWith(enchantment1)) { -- flag3 = canDoUnsafeEnchants; // Purpur -+ flag4 = canDoUnsafeEnchants || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants && org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants); // Purpur flag3 -> flag4 -+ if (!flag4 && org.purpurmc.purpur.PurpurConfig.replaceIncompatibleEnchants) { -+ iterator1.remove(); -+ flag4 = true; -+ } - ++i; - } - } - -- if (!flag3) { -+ if (!flag3 || !flag4) { // Purpur - flag2 = true; - } else { - flag1 = true; -- if (i2 > enchantment.getMaxLevel()) { -+ if ((!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants || !org.purpurmc.purpur.PurpurConfig.allowHigherEnchantsLevels) && i2 > enchantment.getMaxLevel()) { // Purpur - i2 = enchantment.getMaxLevel(); - } - -@@ -389,7 +394,7 @@ public class AnvilMenu extends ItemCombinerMenu { - sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686: Always send completed inventory to stay in sync with client - this.broadcastChanges(); - // Purpur start -- if (canDoUnsafeEnchants && itemstack1 != ItemStack.EMPTY) { -+ if ((canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchants) && itemstack1 != ItemStack.EMPTY) { - ((ServerPlayer) player).connection.send(new ClientboundContainerSetSlotPacket(containerId, incrementStateId(), 2, itemstack1)); - ((ServerPlayer) player).connection.send(new ClientboundContainerSetDataPacket(containerId, 0, cost.get())); - } -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 33548cd10758f3cc891c40d760b9e0a86dc8a2cb..a7909c60a17ab004ab8f6cd39abb211887bdeb90 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -1207,6 +1207,12 @@ public final class ItemStack { - return this.tag != null && this.tag.contains("Enchantments", 9) ? !this.tag.getList("Enchantments", 10).isEmpty() : false; - } - -+ // Purpur start -+ public boolean hasEnchantment(Enchantment enchantment) { -+ return isEnchanted() && EnchantmentHelper.deserializeEnchantments(getEnchantmentTags()).containsKey(enchantment); -+ } -+ // Purpur end -+ - public void addTagElement(String key, Tag element) { - this.getOrCreateTag().put(key, element); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 55963ed03a2e552636035b498cf7441bd4c3ee59..5697ce5ead8dc7e463206a56d489c99c861a9bb1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -285,14 +285,34 @@ public class PurpurConfig { - - public static boolean allowInfinityMending = false; - public static boolean allowCrossbowInfinity = false; -+ public static boolean allowUnsafeEnchants = false; -+ public static boolean allowInapplicableEnchants = true; -+ public static boolean allowIncompatibleEnchants = true; -+ public static boolean allowHigherEnchantsLevels = true; -+ public static boolean allowUnsafeEnchantCommand = false; -+ public static boolean replaceIncompatibleEnchants = false; - private static void enchantmentSettings() { - if (version < 5) { - boolean oldValue = getBoolean("settings.enchantment.allow-infinite-and-mending-together", false); - set("settings.enchantment.allow-infinity-and-mending-together", oldValue); - set("settings.enchantment.allow-infinite-and-mending-together", null); - } -+ if (version < 30) { -+ boolean oldValue = getBoolean("settings.enchantment.allow-unsafe-enchants", false); -+ set("settings.enchantment.anvil.allow-unsafe-enchants", oldValue); -+ set("settings.enchantment.anvil.allow-inapplicable-enchants", true); -+ set("settings.enchantment.anvil.allow-incompatible-enchants", true); -+ set("settings.enchantment.anvil.allow-higher-enchants-levels", true); -+ set("settings.enchantment.allow-unsafe-enchants", null); -+ } - allowInfinityMending = getBoolean("settings.enchantment.allow-infinity-and-mending-together", allowInfinityMending); - allowCrossbowInfinity = getBoolean("settings.enchantment.allow-infinity-on-crossbow", allowCrossbowInfinity); -+ allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", allowUnsafeEnchants); -+ allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants); -+ allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants); -+ allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels); -+ allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchants); // allowUnsafeEnchants as default for backwards compatability -+ replaceIncompatibleEnchants = getBoolean("settings.enchantment.anvil.replace-incompatible-enchants", replaceIncompatibleEnchants); - } - - public static boolean endermanShortHeight = false; diff --git a/patches/server/0154-Configurable-sponge-absorption.patch b/patches/server/0154-Configurable-sponge-absorption.patch deleted file mode 100644 index c87a1153a..000000000 --- a/patches/server/0154-Configurable-sponge-absorption.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Wed, 24 Mar 2021 20:30:37 -0400 -Subject: [PATCH] Configurable sponge absorption - -Allows the total area and radius of water blocks the sponge can absorb to be changed. - -Co-authored by: granny - -diff --git a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -index 4bce895268542531598a01a1bccd8ac1ed703b7d..709dd0af07f2439d7c7e8b5cd0677580dc3f6278 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -@@ -48,7 +48,7 @@ public class SpongeBlock extends Block { - - private boolean removeWaterBreadthFirstSearch(Level world, BlockPos pos) { - BlockStateListPopulator blockList = new BlockStateListPopulator(world); // CraftBukkit - Use BlockStateListPopulator -- BlockPos.breadthFirstTraversal(pos, 6, 65, (blockposition1, consumer) -> { -+ BlockPos.breadthFirstTraversal(pos, world.purpurConfig.spongeAbsorptionRadius, world.purpurConfig.spongeAbsorptionArea, (blockposition1, consumer) -> { // Purpur - Direction[] aenumdirection = SpongeBlock.ALL_DIRECTIONS; - int i = aenumdirection.length; - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 761f8546ce1314e488aaa21faa0a912b64699c2b..347d2f5d5a8d380cea2fd483a68423375fc76908 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -633,6 +633,13 @@ public class PurpurWorldConfig { - spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); - } - -+ public int spongeAbsorptionArea = 65; -+ public int spongeAbsorptionRadius = 6; -+ private void spongeSettings() { -+ spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea); -+ spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; diff --git a/patches/server/0155-Projectile-offset-config.patch b/patches/server/0155-Projectile-offset-config.patch deleted file mode 100644 index bd929c08e..000000000 --- a/patches/server/0155-Projectile-offset-config.patch +++ /dev/null @@ -1,125 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Thu, 25 Mar 2021 01:56:38 +0100 -Subject: [PATCH] Projectile offset config - - -diff --git a/src/main/java/net/minecraft/world/item/BowItem.java b/src/main/java/net/minecraft/world/item/BowItem.java -index 33df0ca406dc8321b76b393f317bbd1c8ebe6366..220513d3fd5645322886522ea4f6b8c55d043b3c 100644 ---- a/src/main/java/net/minecraft/world/item/BowItem.java -+++ b/src/main/java/net/minecraft/world/item/BowItem.java -@@ -44,7 +44,7 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable { - ArrowItem itemarrow = (ArrowItem) (itemstack1.getItem() instanceof ArrowItem ? itemstack1.getItem() : Items.ARROW); - AbstractArrow entityarrow = itemarrow.createArrow(world, itemstack1, entityhuman); - -- entityarrow.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, f * 3.0F, 1.0F); -+ entityarrow.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, f * 3.0F, (float) world.purpurConfig.bowProjectileOffset); // Purpur - if (f == 1.0F) { - entityarrow.setCritArrow(true); - } -diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java -index 1ae5e17b95d994d69b060fc06c0a807c40a90f17..0a0f19f73a6a4e5aece7c17089dc4d31ed2a5299 100644 ---- a/src/main/java/net/minecraft/world/item/CrossbowItem.java -+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java -@@ -64,7 +64,7 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable { - ItemStack itemstack = user.getItemInHand(hand); - - if (CrossbowItem.isCharged(itemstack)) { -- CrossbowItem.performShooting(world, user, hand, itemstack, CrossbowItem.getShootingPower(itemstack), 1.0F); -+ CrossbowItem.performShooting(world, user, hand, itemstack, CrossbowItem.getShootingPower(itemstack), (float) world.purpurConfig.crossbowProjectileOffset); // Purpur - CrossbowItem.setCharged(itemstack, false); - return InteractionResultHolder.consume(itemstack); - } else if (!user.getProjectile(itemstack).isEmpty()) { -diff --git a/src/main/java/net/minecraft/world/item/EggItem.java b/src/main/java/net/minecraft/world/item/EggItem.java -index 58cb992c5defec2f092755cbde661ff10f38bf9d..52f48681407d23f0925f4c9c072d5f0a2a6b1778 100644 ---- a/src/main/java/net/minecraft/world/item/EggItem.java -+++ b/src/main/java/net/minecraft/world/item/EggItem.java -@@ -24,7 +24,7 @@ public class EggItem extends Item { - ThrownEgg entityegg = new ThrownEgg(world, user); - - entityegg.setItem(itemstack); -- entityegg.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F); -+ entityegg.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.eggProjectileOffset); // Purpur - // Paper start - com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityegg.getBukkitEntity()); - if (event.callEvent() && world.addFreshEntity(entityegg)) { -diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -index 897c202c0905040072a06fdfa2032a7f9461b088..6b27d98d06b163243bb0e1bb979aad03f48d7770 100644 ---- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java -+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -@@ -24,7 +24,7 @@ public class EnderpearlItem extends Item { - ThrownEnderpearl entityenderpearl = new ThrownEnderpearl(world, user); - - entityenderpearl.setItem(itemstack); -- entityenderpearl.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F); -+ entityenderpearl.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.enderPearlProjectileOffset); // Purpur - // Paper start - com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityenderpearl.getBukkitEntity()); - if (event.callEvent() && world.addFreshEntity(entityenderpearl)) { -diff --git a/src/main/java/net/minecraft/world/item/SnowballItem.java b/src/main/java/net/minecraft/world/item/SnowballItem.java -index ef3f90a5bcdd7b9815a4053cff166f9d2552f55d..e7e5e1cc92f56e3daba8fa09c59188febec5e8f2 100644 ---- a/src/main/java/net/minecraft/world/item/SnowballItem.java -+++ b/src/main/java/net/minecraft/world/item/SnowballItem.java -@@ -25,7 +25,7 @@ public class SnowballItem extends Item { - Snowball entitysnowball = new Snowball(world, user); - - entitysnowball.setItem(itemstack); -- entitysnowball.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, 1.0F); -+ entitysnowball.shootFromRotation(user, user.getXRot(), user.getYRot(), 0.0F, 1.5F, (float) world.purpurConfig.snowballProjectileOffset); // Purpur - // Paper start - com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitysnowball.getBukkitEntity()); - if (event.callEvent() && world.addFreshEntity(entitysnowball)) { -diff --git a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -index de5bdceb4c8578fb972a2fd5ee0dfdae509e46dc..bcf63ccb6e679cb97d658780b2663aafa3568bcb 100644 ---- a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -+++ b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -@@ -18,7 +18,7 @@ public class ThrowablePotionItem extends PotionItem { - if (!world.isClientSide) { - ThrownPotion thrownPotion = new ThrownPotion(world, user); - thrownPotion.setItem(itemStack); -- thrownPotion.shootFromRotation(user, user.getXRot(), user.getYRot(), -20.0F, 0.5F, 1.0F); -+ thrownPotion.shootFromRotation(user, user.getXRot(), user.getYRot(), -20.0F, 0.5F, (float) world.purpurConfig.throwablePotionProjectileOffset); // Purpur - // Paper start - com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.getBukkitEntity()); - if (event.callEvent() && world.addFreshEntity(thrownPotion)) { -diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java -index 06c2f30b77a2c8aecc65e0c305f643d53798f364..6d1573161f0d8c7999f84925ba7bbf536ee9583a 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -77,7 +77,7 @@ public class TridentItem extends Item implements Vanishable { - if (k == 0) { - ThrownTrident entitythrowntrident = new ThrownTrident(world, entityhuman, stack); - -- entitythrowntrident.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, 2.5F + (float) k * 0.5F, 1.0F); -+ entitythrowntrident.shootFromRotation(entityhuman, entityhuman.getXRot(), entityhuman.getYRot(), 0.0F, 2.5F + (float) k * 0.5F, (float) world.purpurConfig.tridentProjectileOffset); // Purpur - if (entityhuman.getAbilities().instabuild) { - entitythrowntrident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 347d2f5d5a8d380cea2fd483a68423375fc76908..683c1b7a3cc283b7806d5fa9784aba2d96f962f8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -426,6 +426,23 @@ public class PurpurWorldConfig { - //} - } - -+ public double bowProjectileOffset = 1.0D; -+ public double crossbowProjectileOffset = 1.0D; -+ public double eggProjectileOffset = 1.0D; -+ public double enderPearlProjectileOffset = 1.0D; -+ public double throwablePotionProjectileOffset = 1.0D; -+ public double tridentProjectileOffset = 1.0D; -+ public double snowballProjectileOffset = 1.0D; -+ private void projectileOffsetSettings() { -+ bowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.bow", bowProjectileOffset); -+ crossbowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.crossbow", crossbowProjectileOffset); -+ eggProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.egg", eggProjectileOffset); -+ enderPearlProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.ender-pearl", enderPearlProjectileOffset); -+ throwablePotionProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.throwable-potion", throwablePotionProjectileOffset); -+ tridentProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.trident", tridentProjectileOffset); -+ snowballProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.snowball", snowballProjectileOffset); -+ } -+ - public int snowballDamage = -1; - private void snowballSettings() { - snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage); diff --git a/patches/server/0156-Config-for-powered-rail-activation-distance.patch b/patches/server/0156-Config-for-powered-rail-activation-distance.patch deleted file mode 100644 index da810f131..000000000 --- a/patches/server/0156-Config-for-powered-rail-activation-distance.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Thu, 25 Mar 2021 18:10:03 -0400 -Subject: [PATCH] Config for powered rail activation distance - - -diff --git a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java -index 7fddb6fa8fd30ef88346a59f7867aae792f13772..40893e71fe8447b695350273bef9623bd5accdcd 100644 ---- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java -@@ -23,7 +23,7 @@ public class PoweredRailBlock extends BaseRailBlock { - } - - protected boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) { -- if (distance >= 8) { -+ if (distance >= world.purpurConfig.railActivationRange) { // Purpur - return false; - } else { - int j = pos.getX(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 683c1b7a3cc283b7806d5fa9784aba2d96f962f8..46364a5178218bee2de43f4725efe15ab7521b3b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -619,6 +619,11 @@ public class PurpurWorldConfig { - powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing); - } - -+ public int railActivationRange = 8; -+ private void railSettings() { -+ railActivationRange = getInt("blocks.powered-rail.activation-range", railActivationRange); -+ } -+ - public boolean respawnAnchorExplode = true; - public double respawnAnchorExplosionPower = 5.0D; - public boolean respawnAnchorExplosionFire = true; diff --git a/patches/server/0157-Piglin-portal-spawn-modifier.patch b/patches/server/0157-Piglin-portal-spawn-modifier.patch deleted file mode 100644 index 7b6630453..000000000 --- a/patches/server/0157-Piglin-portal-spawn-modifier.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 13 Apr 2021 11:19:35 -0500 -Subject: [PATCH] Piglin portal spawn modifier - -Allows changing the modifier for the piglin spawn chance from a portal block -based on the world difficulty. - -For example, with the default vanilla value of 2000 there is a 2 out of 2000 chance -for a piglin to spawn in a portal block each tick in normal mode. - -Equation: random.nextInt(modifier) < difficulty - -Difficulties: -0 - peaceful -1 - easy -2 - normal -3 - hard - -diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -index 2c085c4a154cb0f8a1d38453f43474a764398784..589b437e7c97c846410f293e2f014bdcd7cb333e 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -@@ -52,7 +52,7 @@ public class NetherPortalBlock extends Block { - - @Override - public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { -- if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < world.getDifficulty().getId()) { // Spigot -+ if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(world.purpurConfig.piglinPortalSpawnModifier) < world.getDifficulty().getId()) { // Spigot // Purpur - while (world.getBlockState(pos).is((Block) this)) { - pos = pos.below(); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 46364a5178218bee2de43f4725efe15ab7521b3b..52a903facc05e61e36b0919946c4a18604ebcd92 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1651,6 +1651,7 @@ public class PurpurWorldConfig { - public double piglinMaxHealth = 16.0D; - public boolean piglinBypassMobGriefing = false; - public boolean piglinTakeDamageFromWater = false; -+ public int piglinPortalSpawnModifier = 2000; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -1663,6 +1664,7 @@ public class PurpurWorldConfig { - piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); - piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); - piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); -+ piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier); - } - - public boolean piglinBruteRidable = false; diff --git a/patches/server/0158-Config-to-change-max-number-of-bees.patch b/patches/server/0158-Config-to-change-max-number-of-bees.patch deleted file mode 100644 index 80aeb3a34..000000000 --- a/patches/server/0158-Config-to-change-max-number-of-bees.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Thu, 29 Apr 2021 19:37:48 +0200 -Subject: [PATCH] Config to change max number of bees - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -index 41c9f074203915c31c1ae7a160ce509c13383f84..a16a1df28258d605cf5908dbe19bda5d71ad4f45 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -@@ -43,7 +43,7 @@ public class BeehiveBlockEntity extends BlockEntity { - private final List stored = Lists.newArrayList(); - @Nullable - public BlockPos savedFlowerPos; -- public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold -+ public int maxBees = org.purpurmc.purpur.PurpurConfig.beeInsideBeeHive; // CraftBukkit - allow setting max amount of bees a hive can hold // Purpur - - public BeehiveBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.BEEHIVE, pos, state); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 0ebd43cf4163e2f3b06d1f6475421c03e08a1bd5..a212357e968393f4d303364e63092defad3d7835 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -250,6 +250,7 @@ public class PurpurConfig { - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; - public static boolean cryingObsidianValidForPortalFrame = false; -+ public static int beeInsideBeeHive = 3; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -281,6 +282,7 @@ public class PurpurConfig { - org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); -+ beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); - } - - public static boolean allowInfinityMending = false; diff --git a/patches/server/0159-Config-for-wither-explosion-radius.patch b/patches/server/0159-Config-for-wither-explosion-radius.patch deleted file mode 100644 index 7e086249d..000000000 --- a/patches/server/0159-Config-for-wither-explosion-radius.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Thu, 29 Apr 2021 14:39:07 -0400 -Subject: [PATCH] Config for wither explosion radius - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -index f40ab9d5b18c189ba9b572e49243640bd44362d1..b4687453256ead43cf5288994316c7bf946b86df 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -@@ -97,7 +97,7 @@ public class WitherSkull extends AbstractHurtingProjectile { - if (!this.level().isClientSide) { - // CraftBukkit start - // this.level().explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, World.a.MOB); -- ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); -+ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.level().purpurConfig.witherExplosionRadius, false); // Purpur - this.level().getCraftServer().getPluginManager().callEvent(event); - - if (!event.isCancelled()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 52a903facc05e61e36b0919946c4a18604ebcd92..2188706d89a36500da3571d2761b830f7c8fe60f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2294,6 +2294,7 @@ public class PurpurWorldConfig { - public boolean witherBypassMobGriefing = false; - public boolean witherTakeDamageFromWater = false; - public boolean witherCanRideVehicles = false; -+ public float witherExplosionRadius = 1.0F; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -2314,6 +2315,7 @@ public class PurpurWorldConfig { - witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing); - witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater); - witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles); -+ witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius); - } - - public boolean witherSkeletonRidable = false; diff --git a/patches/server/0160-Gamemode-extra-permissions.patch b/patches/server/0160-Gamemode-extra-permissions.patch deleted file mode 100644 index 31f78b319..000000000 --- a/patches/server/0160-Gamemode-extra-permissions.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 30 Apr 2021 13:39:39 -0500 -Subject: [PATCH] Gamemode extra permissions - - -diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java -index f03fd95412883a3a5bbe2b91c603874bf147e6cb..873659dcf8c8431f21bd2b23b8d7bdb4ea7e03e8 100644 ---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java -+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java -@@ -227,6 +227,19 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy - } - // CraftBukkit end - -+ // Purpur start -+ public boolean testPermission(int i, String bukkitPermission) { -+ if (hasPermission(i, bukkitPermission)) { -+ return true; -+ } -+ net.kyori.adventure.text.Component permissionMessage = getLevel().getServer().server.permissionMessage(); -+ if (!permissionMessage.equals(net.kyori.adventure.text.Component.empty())) { -+ sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(permissionMessage.replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(bukkitPermission).build()))); -+ } -+ return false; -+ } -+ // Purpur end -+ - public Vec3 getPosition() { - return this.worldPosition; - } -diff --git a/src/main/java/net/minecraft/server/commands/GameModeCommand.java b/src/main/java/net/minecraft/server/commands/GameModeCommand.java -index 5cb15e2209d7b315904a1fc6d650ce1e75584271..7e21db60f3ace2a19686d6ea04b994ec3a793ec8 100644 ---- a/src/main/java/net/minecraft/server/commands/GameModeCommand.java -+++ b/src/main/java/net/minecraft/server/commands/GameModeCommand.java -@@ -45,6 +45,18 @@ public class GameModeCommand { - } - - private static int setMode(CommandContext context, Collection targets, GameType gameMode) { -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.commandGamemodeRequiresPermission) { -+ String gamemode = gameMode.getName(); -+ CommandSourceStack sender = context.getSource(); -+ if (!sender.testPermission(2, "minecraft.command.gamemode." + gamemode)) { -+ return 0; -+ } -+ if (sender.getEntity() instanceof ServerPlayer player && (targets.size() > 1 || !targets.contains(player)) && !sender.testPermission(2, "minecraft.command.gamemode." + gamemode + ".other")) { -+ return 0; -+ } -+ } -+ // Purpur end - int i = 0; - - for(ServerPlayer serverPlayer : targets) { -diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java -index 2959f713ce75a1df9c6c7cf5e021690cfcb6e1e7..3fa9539cfb2c35beeba6d44fa05cee971444b7d0 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java -@@ -23,7 +23,15 @@ public final class CommandPermissions { - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands); - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands); - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands); -- DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands); -+ // Purpur start -+ Permission gamemodeVanilla = DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode", PermissionDefault.OP, commands); -+ for (net.minecraft.world.level.GameType gametype : net.minecraft.world.level.GameType.values()) { -+ Permission gamemodeSelf = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName(), "Allows the user to set " + gametype.getName() + " gamemode for self", PermissionDefault.OP); -+ Permission gamemodeOther = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName() + ".other", "Allows the user to set " + gametype.getName() + " gamemode for other players", PermissionDefault.OP); -+ gamemodeSelf.addParent(gamemodeOther, true); -+ gamemodeVanilla.addParent(gamemodeSelf, true); -+ } -+ // Purpur end - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "experience", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); // Paper - wrong permission; redirects are de-redirected and the root literal name is used, so xp -> experience - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands); - DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 85a41b50a1f0a89264585ecd621e7e5a1de67efc..a66c35ce86437311b38d1787c766bbe179cc5dfa 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -233,6 +233,7 @@ public class PurpurConfig { - public static String commandTPSBarTextColorMedium = ""; - public static String commandTPSBarTextColorLow = ""; - public static int commandTPSBarTickInterval = 20; -+ public static boolean commandGamemodeRequiresPermission = false; - private static void commandSettings() { - commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); - commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); -@@ -244,6 +245,7 @@ public class PurpurConfig { - commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium); - commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); - commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); -+ commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission); - } - - public static int barrelRows = 3; diff --git a/patches/server/0161-Configurable-piston-push-limit.patch b/patches/server/0161-Configurable-piston-push-limit.patch deleted file mode 100644 index 3b884bb01..000000000 --- a/patches/server/0161-Configurable-piston-push-limit.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Sun, 2 May 2021 23:14:54 +0200 -Subject: [PATCH] Configurable piston push limit - - -diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java -index 744d91546d1a810f60a43c15ed74b4158f341a4a..354538daefa603f6df5a139b6bff87dbb4cef178 100644 ---- a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java -+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java -@@ -86,7 +86,7 @@ public class PistonStructureResolver { - return true; - } else { - int i = 1; -- if (i + this.toPush.size() > 12) { -+ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - return false; - } else { - while(isSticky(blockState)) { -@@ -98,7 +98,7 @@ public class PistonStructureResolver { - } - - ++i; -- if (i + this.toPush.size() > 12) { -+ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - return false; - } - } -@@ -142,7 +142,7 @@ public class PistonStructureResolver { - return true; - } - -- if (this.toPush.size() >= 12) { -+ if (this.toPush.size() >= this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - return false; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2188706d89a36500da3571d2761b830f7c8fe60f..5baac1391199d4877c566beb45b5eaca29cfbff2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -614,6 +614,11 @@ public class PurpurWorldConfig { - lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether); - } - -+ public int pistonBlockPushLimit = 12; -+ private void pistonSettings() { -+ pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit); -+ } -+ - public boolean powderSnowBypassMobGriefing = false; - private void powderSnowSettings() { - powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing); diff --git a/patches/server/0162-Configurable-broadcast-settings.patch b/patches/server/0162-Configurable-broadcast-settings.patch deleted file mode 100644 index 64196d579..000000000 --- a/patches/server/0162-Configurable-broadcast-settings.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 3 May 2021 01:33:14 +0200 -Subject: [PATCH] Configurable broadcast settings - - -diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java -index 52891c4a4260d1938f2f4565b5219ad303555638..69d349613f01ab0ac1890734657da52984704f29 100644 ---- a/src/main/java/net/minecraft/server/PlayerAdvancements.java -+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java -@@ -250,6 +250,7 @@ public class PlayerAdvancements { - advancement.getRewards().grant(this.player); - // Paper start - Add Adventure message to PlayerAdvancementDoneEvent - if (message != null && this.player.level().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { -+ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), false); - // Paper end - } -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index ea7a533546c53539355ea1501b027399e6ba1bd8..784634596696521cfbd58e85392183dd96b843b0 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1004,6 +1004,7 @@ public class ServerPlayer extends Player { - })); - Team scoreboardteambase = this.getTeam(); - -+ if (org.purpurmc.purpur.PurpurConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(ichatbasecomponent); else // Purpur - if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { - if (scoreboardteambase.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { - this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 3d0fc534454cd8b543e4fa55aa9dfe1e55ee1b49..6420f7043f7f88eef90051db9af8c6567945c553 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -203,6 +203,18 @@ public class PurpurConfig { - deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors); - } - -+ public static boolean advancementOnlyBroadcastToAffectedPlayer = false; -+ public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false; -+ private static void broadcastSettings() { -+ if (version < 13) { -+ boolean oldValue = getBoolean("settings.advancement.only-broadcast-to-affected-player", false); -+ set("settings.broadcasts.advancement.only-broadcast-to-affected-player", oldValue); -+ set("settings.advancement.only-broadcast-to-affected-player", null); -+ } -+ advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer); -+ deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer); -+ } -+ - public static String serverModName = "Purpur"; - private static void serverModName() { - serverModName = getString("settings.server-mod-name", serverModName); diff --git a/patches/server/0163-Configurable-mob-blindness.patch b/patches/server/0163-Configurable-mob-blindness.patch deleted file mode 100644 index 397bef277..000000000 --- a/patches/server/0163-Configurable-mob-blindness.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 11 May 2021 21:00:53 -0400 -Subject: [PATCH] Configurable mob blindness - -Ported from https://github.com/raltsmc/mobblindness - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 217a0082f7f260678253f31e381a432099555177..cdae47c490555efad0eb2d2ae6498888aeaf21bd 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1036,6 +1036,17 @@ public abstract class LivingEntity extends Entity implements Attackable { - if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL) || entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD) || entitytypes == EntityType.PIGLIN && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.PIGLIN_BRUTE && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) { - d0 *= 0.5D; - } -+ -+ // Purpur start -+ if (entity instanceof LivingEntity entityliving) { -+ if (entityliving.hasEffect(MobEffects.BLINDNESS)) { -+ int amplifier = entityliving.getEffect(MobEffects.BLINDNESS).getAmplifier(); -+ for (int i = 0; i < amplifier; i++) { -+ d0 *= this.level().purpurConfig.mobsBlindnessMultiplier; -+ } -+ } -+ } -+ // Purpur end - } - - return d0; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5baac1391199d4877c566beb45b5eaca29cfbff2..08f0f4d9c7abb4db26f11655f2544e1af3775531 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -132,6 +132,7 @@ public class PurpurWorldConfig { - public boolean persistentDroppableEntityDisplayNames = true; - public boolean projectilesBypassMobGriefing = false; - public boolean tickFluids = true; -+ public double mobsBlindnessMultiplier = 1; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public double voidDamageHeight = -64.0D; - public double voidDamageDealt = 4.0D; -@@ -152,6 +153,7 @@ public class PurpurWorldConfig { - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids); -+ mobsBlindnessMultiplier = getDouble("gameplay-mechanics.entity-blindness-multiplier", mobsBlindnessMultiplier); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight); - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); diff --git a/patches/server/0164-Hide-hidden-players-from-entity-selector.patch b/patches/server/0164-Hide-hidden-players-from-entity-selector.patch deleted file mode 100644 index cc4803150..000000000 --- a/patches/server/0164-Hide-hidden-players-from-entity-selector.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 13 May 2021 16:18:29 -0500 -Subject: [PATCH] Hide hidden players from entity selector - - -diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java -index f25b9330e068c7d9e12cb57a7761cfef9ebaf7bc..7e66aaa960ce7b6dda7c064d4c6856cc4b368b58 100644 ---- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java -+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java -@@ -200,10 +200,10 @@ public class EntitySelector { - - if (this.playerName != null) { - entityplayer = source.getServer().getPlayerList().getPlayerByName(this.playerName); -- return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new ServerPlayer[]{entityplayer})); -+ return entityplayer == null || !canSee(source, entityplayer) ? Collections.emptyList() : Lists.newArrayList(entityplayer); // Purpur - } else if (this.entityUUID != null) { - entityplayer = source.getServer().getPlayerList().getPlayer(this.entityUUID); -- return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new ServerPlayer[]{entityplayer})); -+ return entityplayer == null || !canSee(source, entityplayer) ? Collections.emptyList() : Lists.newArrayList(entityplayer); // Purpur - } else { - Vec3 vec3d = (Vec3) this.position.apply(source.getPosition()); - Predicate predicate = this.getPredicate(vec3d); -@@ -213,7 +213,7 @@ public class EntitySelector { - ServerPlayer entityplayer1 = (ServerPlayer) source.getEntity(); - - if (predicate.test(entityplayer1)) { -- return Lists.newArrayList(new ServerPlayer[]{entityplayer1}); -+ return !canSee(source, entityplayer1) ? Collections.emptyList() : Lists.newArrayList(entityplayer1); // Purpur - } - } - -@@ -224,6 +224,7 @@ public class EntitySelector { - - if (this.isWorldLimited()) { - object = source.getLevel().getPlayers(predicate, i); -+ ((List) object).removeIf(entityplayer3 -> !canSee(source, (ServerPlayer) entityplayer3)); // Purpur - } else { - object = Lists.newArrayList(); - Iterator iterator = source.getServer().getPlayerList().getPlayers().iterator(); -@@ -231,7 +232,7 @@ public class EntitySelector { - while (iterator.hasNext()) { - ServerPlayer entityplayer2 = (ServerPlayer) iterator.next(); - -- if (predicate.test(entityplayer2)) { -+ if (predicate.test(entityplayer2) && canSee(source, entityplayer2)) { // Purpur - ((List) object).add(entityplayer2); - if (((List) object).size() >= i) { - return (List) object; -@@ -276,4 +277,10 @@ public class EntitySelector { - public static Component joinNames(List entities) { - return ComponentUtils.formatList(entities, Entity::getDisplayName); - } -+ -+ // Purpur start -+ private boolean canSee(CommandSourceStack sender, ServerPlayer target) { -+ return !org.purpurmc.purpur.PurpurConfig.hideHiddenPlayersFromEntitySelector || !(sender.getEntity() instanceof ServerPlayer player) || player.getBukkitEntity().canSee(target.getBukkitEntity()); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6420f7043f7f88eef90051db9af8c6567945c553..3efda1177f3141851f8f1580b462e3a839303845 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -246,6 +246,7 @@ public class PurpurConfig { - public static String commandTPSBarTextColorLow = ""; - public static int commandTPSBarTickInterval = 20; - public static boolean commandGamemodeRequiresPermission = false; -+ public static boolean hideHiddenPlayersFromEntitySelector = false; - private static void commandSettings() { - commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); - commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); -@@ -258,6 +259,7 @@ public class PurpurConfig { - commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); - commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); - commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission); -+ hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector); - } - - public static int barrelRows = 3; diff --git a/patches/server/0165-Config-for-health-to-impact-Creeper-explosion-radius.patch b/patches/server/0165-Config-for-health-to-impact-Creeper-explosion-radius.patch deleted file mode 100644 index 06177143d..000000000 --- a/patches/server/0165-Config-for-health-to-impact-Creeper-explosion-radius.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Thu, 29 Apr 2021 20:28:18 -0400 -Subject: [PATCH] Config for health to impact Creeper explosion radius - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -index fd3b1e92a626402112bbd371a0ac1264a2c19c32..a8bc27c858c6ddec2ff2f84b5c1dc51c3b1b04b0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -371,9 +371,10 @@ public class Creeper extends Monster implements PowerableMob { - this.exploding = true; // Purpur - if (!this.level().isClientSide) { - float f = this.isPowered() ? 2.0F : 1.0F; -+ float multiplier = this.level().purpurConfig.creeperHealthRadius ? this.getHealth() / this.getMaxHealth() : 1; // Purpur - - // CraftBukkit start -- ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * f, false); -+ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, (this.explosionRadius * f) * multiplier, false); // Purpur - if (!event.isCancelled()) { - // CraftBukkit end - this.dead = true; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 08f0f4d9c7abb4db26f11655f2544e1af3775531..7057382f67491b6b21b66c81e0ccaf8b59b04c1b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -932,6 +932,7 @@ public class PurpurWorldConfig { - public boolean creeperBypassMobGriefing = false; - public boolean creeperTakeDamageFromWater = false; - public boolean creeperExplodeWhenKilled = false; -+ public boolean creeperHealthRadius = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -947,6 +948,7 @@ public class PurpurWorldConfig { - creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing); - creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater); - creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled); -+ creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius); - } - - public boolean dolphinRidable = false; diff --git a/patches/server/0166-Iron-golem-calm-anger-options.patch b/patches/server/0166-Iron-golem-calm-anger-options.patch deleted file mode 100644 index 00080d2cd..000000000 --- a/patches/server/0166-Iron-golem-calm-anger-options.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 13 May 2021 21:22:51 -0500 -Subject: [PATCH] Iron golem calm anger options - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 58539d29b8e2a8c0676cc574f64e7d61613e1451..1ffb8e1c386fc85796432281ac407a935169b186 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -96,6 +96,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - @Override - protected void registerGoals() { - if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur -+ if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true)); - this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F)); -@@ -315,6 +316,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - itemstack.shrink(1); - } - -+ if (this.level().purpurConfig.ironGolemHealCalm && isAngry() && getHealth() == getMaxHealth()) stopBeingAngry(); // Purpur - return InteractionResult.sidedSuccess(this.level().isClientSide); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7057382f67491b6b21b66c81e0ccaf8b59b04c1b..956b1281d0484c86ee7642b1732de489679dc3d9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1390,6 +1390,8 @@ public class PurpurWorldConfig { - public boolean ironGolemCanSwim = false; - public double ironGolemMaxHealth = 100.0D; - public boolean ironGolemTakeDamageFromWater = false; -+ public boolean ironGolemPoppyCalm = false; -+ public boolean ironGolemHealCalm = false; - private void ironGolemSettings() { - ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); - ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); -@@ -1402,6 +1404,8 @@ public class PurpurWorldConfig { - } - ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth); - ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater); -+ ironGolemPoppyCalm = getBoolean("mobs.iron_golem.poppy-calms-anger", ironGolemPoppyCalm); -+ ironGolemHealCalm = getBoolean("mobs.iron_golem.healing-calms-anger", ironGolemHealCalm); - } - - public boolean llamaRidable = false; -diff --git a/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java -new file mode 100644 -index 0000000000000000000000000000000000000000..9660716f4162a4441c6e1b0baddef8f5086566c5 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java -@@ -0,0 +1,91 @@ -+package org.purpurmc.purpur.entity.ai; -+ -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.server.level.ServerPlayer; -+import net.minecraft.world.InteractionHand; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.ai.goal.Goal; -+import net.minecraft.world.entity.animal.IronGolem; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.level.block.Blocks; -+ -+import java.util.EnumSet; -+import java.util.UUID; -+ -+public class ReceiveFlower extends Goal { -+ private final IronGolem irongolem; -+ private ServerPlayer target; -+ private int cooldown; -+ -+ public ReceiveFlower(IronGolem entity) { -+ this.irongolem = entity; -+ setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); -+ } -+ -+ @Override -+ public boolean canUse() { -+ if (this.irongolem.getOfferFlowerTick() > 0) { -+ return false; -+ } -+ if (!this.irongolem.isAngry()) { -+ return false; -+ } -+ UUID uuid = this.irongolem.getPersistentAngerTarget(); -+ if (uuid == null) { -+ return false; -+ } -+ Entity target = ((ServerLevel) this.irongolem.level()).getEntity(uuid); -+ if (!(target instanceof ServerPlayer player)) { -+ return false; -+ } -+ InteractionHand hand = getPoppyHand(player); -+ if (hand == null) { -+ return false; -+ } -+ removeFlower(player, hand); -+ this.target = player; -+ return true; -+ } -+ -+ @Override -+ public boolean canContinueToUse() { -+ return this.cooldown > 0; -+ } -+ -+ @Override -+ public void start() { -+ this.cooldown = 100; -+ this.irongolem.stopBeingAngry(); -+ this.irongolem.offerFlower(true); -+ } -+ -+ @Override -+ public void stop() { -+ this.irongolem.offerFlower(false); -+ this.target = null; -+ } -+ -+ @Override -+ public void tick() { -+ this.irongolem.getLookControl().setLookAt(this.target, 30.0F, 30.0F); -+ --this.cooldown; -+ } -+ -+ private InteractionHand getPoppyHand(ServerPlayer player) { -+ if (isPoppy(player.getMainHandItem())) { -+ return InteractionHand.MAIN_HAND; -+ } -+ if (isPoppy(player.getOffhandItem())) { -+ return InteractionHand.OFF_HAND; -+ } -+ return null; -+ } -+ -+ private void removeFlower(ServerPlayer player, InteractionHand hand) { -+ player.setItemInHand(hand, ItemStack.EMPTY); -+ } -+ -+ private boolean isPoppy(ItemStack item) { -+ return item.getItem() == Blocks.POPPY.asItem(); -+ } -+} diff --git a/patches/server/0167-Breedable-parrots.patch b/patches/server/0167-Breedable-parrots.patch deleted file mode 100644 index 2878f11ba..000000000 --- a/patches/server/0167-Breedable-parrots.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 13 May 2021 22:17:50 -0500 -Subject: [PATCH] Breedable parrots - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -index 4c64feca8d78b907406cc409bd6beef4b3bd35f3..6e8fc61b04796da5a8820812b5e88110f774b77a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -@@ -226,6 +226,7 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { -@@ -379,13 +381,13 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder -Date: Fri, 14 May 2021 21:22:57 +0100 -Subject: [PATCH] Configurable powered rail boost modifier - - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -index 5cdcc5792f19766d2d55d16859f8e0f68bd6479b..b99b94c6ec4767aba16d82eaca8b2761d779b226 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -@@ -724,7 +724,7 @@ public abstract class AbstractMinecart extends Entity { - if (d18 > 0.01D) { - double d20 = 0.06D; - -- this.setDeltaMovement(vec3d4.add(vec3d4.x / d18 * 0.06D, 0.0D, vec3d4.z / d18 * 0.06D)); -+ this.setDeltaMovement(vec3d4.add(vec3d4.x / d18 * this.level().purpurConfig.poweredRailBoostModifier, 0.0D, vec3d4.z / d18 * this.level().purpurConfig.poweredRailBoostModifier)); // Purpur - } else { - Vec3 vec3d5 = this.getDeltaMovement(); - double d21 = vec3d5.x; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f06d6863c2f48f59817e7d1d1f3ea928c29140bb..7684e05000e1913b4ee04b87b6304f7a4b73e82a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -275,6 +275,7 @@ public class PurpurWorldConfig { - public boolean minecartControllableFallDamage = true; - public double minecartControllableBaseSpeed = 0.1D; - public Map minecartControllableBlockSpeeds = new HashMap<>(); -+ public double poweredRailBoostModifier = 0.06; - private void minecartSettings() { - if (PurpurConfig.version < 12) { - boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere); -@@ -327,6 +328,7 @@ public class PurpurWorldConfig { - set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D); - set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D); - } -+ poweredRailBoostModifier = getDouble("gameplay-mechanics.minecart.powered-rail.boost-modifier", poweredRailBoostModifier); - } - - public boolean catSpawning; diff --git a/patches/server/0169-Add-config-change-multiplier-critical-damage-value.patch b/patches/server/0169-Add-config-change-multiplier-critical-damage-value.patch deleted file mode 100644 index ca4739693..000000000 --- a/patches/server/0169-Add-config-change-multiplier-critical-damage-value.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 17 May 2021 02:40:13 +0200 -Subject: [PATCH] Add config change multiplier critical damage value - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 04e816f01db7d2de7ffa9bbaaa72daadaab34831..81ba8875f0077ac1be80533061bcb0e632c13ded 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1313,7 +1313,7 @@ public abstract class Player extends LivingEntity { - flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - flag2 = flag2 && !this.isSprinting(); - if (flag2) { -- f *= 1.5F; -+ f *= this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur - } - - f += f1; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7684e05000e1913b4ee04b87b6304f7a4b73e82a..6e5b055117023afe69ed8226ef0caf246f7560b4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -374,6 +374,7 @@ public class PurpurWorldConfig { - public boolean creativeOnePunch = false; - public boolean playerSleepNearMonsters = false; - public boolean playersSkipNight = true; -+ public double playerCriticalDamageMultiplier = 1.5D; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -395,6 +396,7 @@ public class PurpurWorldConfig { - creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); - playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters); - playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight); -+ playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0170-Option-to-disable-dragon-egg-teleporting.patch b/patches/server/0170-Option-to-disable-dragon-egg-teleporting.patch deleted file mode 100644 index f72d97fd3..000000000 --- a/patches/server/0170-Option-to-disable-dragon-egg-teleporting.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 17 May 2021 04:46:23 -0500 -Subject: [PATCH] Option to disable dragon egg teleporting - - -diff --git a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java -index 7e1edcc7b9f170b7c649437c2f0dd78c0bab9be4..5f8ac1fdac2c334951261f2b9702f5e711743c88 100644 ---- a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java -@@ -42,8 +42,8 @@ public class DragonEggBlock extends FallingBlock { - } - - private void teleport(BlockState state, Level world, BlockPos pos) { -+ if (!world.purpurConfig.dragonEggTeleport) return; // Purpur - WorldBorder worldborder = world.getWorldBorder(); -- - for (int i = 0; i < 1000; ++i) { - BlockPos blockposition1 = pos.offset(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16)); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6e5b055117023afe69ed8226ef0caf246f7560b4..9486f753b7c33a324431171ab017d43889887908 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -541,6 +541,11 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean dragonEggTeleport = true; -+ private void dragonEggSettings() { -+ dragonEggTeleport = getBoolean("blocks.dragon_egg.teleport", dragonEggTeleport); -+ } -+ - public boolean baselessEndCrystalExplode = true; - public double baselessEndCrystalExplosionPower = 6.0D; - public boolean baselessEndCrystalExplosionFire = false; diff --git a/patches/server/0171-Config-for-unverified-username-message.patch b/patches/server/0171-Config-for-unverified-username-message.patch deleted file mode 100644 index aaab688c2..000000000 --- a/patches/server/0171-Config-for-unverified-username-message.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Wed, 19 May 2021 15:33:43 -0400 -Subject: [PATCH] Config for unverified username message - - -diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 878001928327d92423d5f7f6d5ce8772d6fa477f..b4b88a3d4dc66c44ca8f3bad1025c17a9993ac1d 100644 ---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -340,7 +340,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - ServerLoginPacketListenerImpl.this.gameProfile = gameprofile; - ServerLoginPacketListenerImpl.this.state = ServerLoginPacketListenerImpl.State.READY_TO_ACCEPT; - } else { -- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); -+ ServerLoginPacketListenerImpl.this.disconnect(org.purpurmc.purpur.PurpurConfig.unverifiedUsername.equals("default") ? Component.translatable("multiplayer.disconnect.unverified_username") : io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.unverifiedUsername))); // Purpur - ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName()); - } - } catch (AuthenticationUnavailableException authenticationunavailableexception) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 3efda1177f3141851f8f1580b462e3a839303845..566e71e92f7bc58dc6c5518c91cbd07dd157cb12 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -184,6 +184,7 @@ public class PurpurConfig { - public static String pingCommandOutput = "%s's ping is %sms"; - public static String tpsbarCommandOutput = "Tpsbar toggled for "; - public static String dontRunWithScissors = "Don't run with scissors!"; -+ public static String unverifiedUsername = "default"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -@@ -196,6 +197,7 @@ public class PurpurConfig { - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); - dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); -+ unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername); - } - - public static String deathMsgRunWithScissors = " slipped and fell on their shears"; diff --git a/patches/server/0172-Make-anvil-cumulative-cost-configurable.patch b/patches/server/0172-Make-anvil-cumulative-cost-configurable.patch deleted file mode 100644 index e48a72e76..000000000 --- a/patches/server/0172-Make-anvil-cumulative-cost-configurable.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Fri, 21 May 2021 16:58:45 +0200 -Subject: [PATCH] Make anvil cumulative cost configurable - - -diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -index fdf2bf10a533c3d4ee145880e6949dadf6ce5410..531b911c1bcdd3735529ee18f2bb0ccdf4ad6089 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -403,7 +403,7 @@ public class AnvilMenu extends ItemCombinerMenu { - } - - public static int calculateIncreasedRepairCost(int cost) { -- return cost * 2 + 1; -+ return org.purpurmc.purpur.PurpurConfig.anvilCumulativeCost ? cost * 2 + 1 : 0; - } - - public boolean setItemName(String newItemName) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index bda7ebcedcfc7086dcbd3a402e4c5ace3c508037..cb0125f7aaa514578465d455b2b1fab98d7b13c1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -269,6 +269,7 @@ public class PurpurConfig { - public static boolean enderChestPermissionRows = false; - public static boolean cryingObsidianValidForPortalFrame = false; - public static int beeInsideBeeHive = 3; -+ public static boolean anvilCumulativeCost = true; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -301,6 +302,7 @@ public class PurpurConfig { - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); - beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); -+ anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); - } - - public static boolean allowInfinityMending = false; diff --git a/patches/server/0173-ShulkerBox-allow-oversized-stacks.patch b/patches/server/0173-ShulkerBox-allow-oversized-stacks.patch deleted file mode 100644 index b229d4ffd..000000000 --- a/patches/server/0173-ShulkerBox-allow-oversized-stacks.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 27 May 2021 04:04:23 -0500 -Subject: [PATCH] ShulkerBox allow oversized stacks - -This fixes PaperMC/Paper#4748 where breaking a shulkerbox in survival mode -with oversized itemstacks would cause a "chunk ban". This fixes it by always -creating an itemstack using the TileEntity's NBT data (how it handles it for -creative players) instead of routing it through the LootableBuilder. - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index b9e9f20842274024dd1e2a0253666002c914a2b2..1e0d07949e8b4450a400625a23d386024192eef1 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -429,7 +429,7 @@ public class ServerPlayerGameMode { - - ItemStack mainHandStack = null; // Paper - boolean isCorrectTool = false; // Paper -- if (this.isCreative()) { -+ if (this.isCreative() || (this.level.purpurConfig.shulkerBoxAllowOversizedStacks && block instanceof net.minecraft.world.level.block.ShulkerBoxBlock)) { // Purpur - // return true; // CraftBukkit - } else { - ItemStack itemstack = this.player.getMainHandItem(); -diff --git a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java -index b51155ad12515b2d0dd0f202580b9f455c114d9a..dd6c82a418ee299d7a5614cb0260949c198b4149 100644 ---- a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java -@@ -137,7 +137,7 @@ public class ShulkerBoxBlock extends BaseEntityBlock { - public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) { - BlockEntity blockEntity = world.getBlockEntity(pos); - if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) { -- if (!world.isClientSide && player.isCreative() && !shulkerBoxBlockEntity.isEmpty()) { -+ if (world.purpurConfig.shulkerBoxAllowOversizedStacks || (!world.isClientSide && player.isCreative() && !shulkerBoxBlockEntity.isEmpty())) { // Purpur - ItemStack itemStack = getColoredItemStack(this.getColor()); - blockEntity.saveToItem(itemStack); - if (shulkerBoxBlockEntity.hasCustomName()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9486f753b7c33a324431171ab017d43889887908..c1c76db494392cb00e58847ba4a01a08b9027fc8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -661,6 +661,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean shulkerBoxAllowOversizedStacks = false; -+ private void shulkerBoxSettings() { -+ shulkerBoxAllowOversizedStacks = getBoolean("blocks.shulker_box.allow-oversized-stacks", shulkerBoxAllowOversizedStacks); -+ } -+ - public boolean slabHalfBreak = false; - private void slabSettings() { - slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak); diff --git a/patches/server/0174-Bee-can-work-when-raining-or-at-night.patch b/patches/server/0174-Bee-can-work-when-raining-or-at-night.patch deleted file mode 100644 index 838a9cb53..000000000 --- a/patches/server/0174-Bee-can-work-when-raining-or-at-night.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Thu, 27 May 2021 06:46:30 +0200 -Subject: [PATCH] Bee can work when raining or at night - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index bfafbc90c710514e1a5699f76381f593c8790c3b..0ee62562d36292d48226a3b20ac54aafe6d12394 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -399,7 +399,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - - boolean wantsToEnterHive() { - if (this.stayOutOfHiveCountdown <= 0 && !this.beePollinateGoal.isPollinating() && !this.hasStung() && this.getTarget() == null) { -- boolean flag = this.isTiredOfLookingForNectar() || this.level().isRaining() || this.level().isNight() || this.hasNectar(); -+ boolean flag = this.isTiredOfLookingForNectar() || (this.level().isRaining() && !this.level().purpurConfig.beeCanWorkInRain) || (this.level().isNight() && !this.level().purpurConfig.beeCanWorkAtNight) || this.hasNectar(); // Purpur - - return flag && !this.isHiveNearFire(); - } else { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -index a16a1df28258d605cf5908dbe19bda5d71ad4f45..7b82842b97ce795745cf6ee6399f618c55acbbf3 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -@@ -203,7 +203,7 @@ public class BeehiveBlockEntity extends BlockEntity { - } - - private static boolean releaseBee(Level world, BlockPos blockposition, BlockState iblockdata, BeehiveBlockEntity.BeeData tileentitybeehive_hivebee, @Nullable List list, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, @Nullable BlockPos blockposition1, boolean force) { -- if (!force && (world.isNight() || world.isRaining()) && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ if (!force && ((world.isNight() && !world.purpurConfig.beeCanWorkAtNight) || (world.isRaining() && !world.purpurConfig.beeCanWorkInRain)) && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { // Purpur - // CraftBukkit end - return false; - } else { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c1c76db494392cb00e58847ba4a01a08b9027fc8..bd485bc18e7012f14a7f85cda26f8a1ec2b0cca0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -773,6 +773,8 @@ public class PurpurWorldConfig { - public double beeMaxHealth = 10.0D; - public int beeBreedingTicks = 6000; - public boolean beeTakeDamageFromWater = false; -+ public boolean beeCanWorkAtNight = false; -+ public boolean beeCanWorkInRain = false; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -786,6 +788,8 @@ public class PurpurWorldConfig { - beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth); - beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks); - beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater); -+ beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight); -+ beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain); - } - - public boolean blazeRidable = false; diff --git a/patches/server/0175-API-for-any-mob-to-burn-daylight.patch b/patches/server/0175-API-for-any-mob-to-burn-daylight.patch deleted file mode 100644 index ea8bfba01..000000000 --- a/patches/server/0175-API-for-any-mob-to-burn-daylight.patch +++ /dev/null @@ -1,390 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Tue, 25 May 2021 16:31:09 -0400 -Subject: [PATCH] API for any mob to burn daylight - -Co-authored by: Encode42 - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index dbd61da31192896ced9839cf8a3b123a6625ae60..68be1bb59c7e5176c82ed128be5296a2511212ec 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -497,6 +497,21 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - return true; - } - -+ // Purpur start - copied from Mob -+ public boolean isSunBurnTick() { -+ if (this.level().isDay() && !this.level().isClientSide) { -+ float f = this.getLightLevelDependentMagicValue(); -+ BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); -+ boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow; -+ -+ if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) { -+ return true; -+ } -+ } -+ -+ return false; -+ } -+ - public final boolean hardCollides() { - return this.hardCollides; - } -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index cdae47c490555efad0eb2d2ae6498888aeaf21bd..972c0e43e3f099706b9ee744551d43d31ffb3fa7 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -263,6 +263,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper - public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event - public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper -+ protected boolean shouldBurnInDay = false; public boolean shouldBurnInDay() { return this.shouldBurnInDay; } public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } // Purpur - - @Override - public float getBukkitYaw() { -@@ -804,6 +805,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { - nbt.put("Brain", nbtbase); - }); -+ nbt.putBoolean("Purpur.ShouldBurnInDay", shouldBurnInDay); // Purpur - } - - @Override -@@ -888,6 +890,11 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain"))); - } - -+ // Purpur start -+ if (nbt.contains("Purpur.ShouldBurnInDay")) { -+ shouldBurnInDay = nbt.getBoolean("Purpur.ShouldBurnInDay"); -+ } -+ // Purpur end - } - - // CraftBukkit start -@@ -3537,6 +3544,27 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.hurt(this.damageSources().drown(), 1.0F); - } - -+ // Purpur start - copied from Zombie -+ if (this.isAlive()) { -+ boolean flag = this.shouldBurnInDay() && this.isSunBurnTick(); -+ if (flag) { -+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -+ if (!itemstack.isEmpty()) { -+ if (itemstack.isDamageableItem()) { -+ itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -+ if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -+ this.broadcastBreakEvent(EquipmentSlot.HEAD); -+ this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); -+ } -+ } -+ flag = false; -+ } -+ if (flag) { -+ this.setSecondsOnFire(8); -+ } -+ } -+ } -+ // Purpur end - } - - public boolean isSensitiveToWater() { -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index d24f94f2c27523af10184cae27157169d5deabae..7dae759b777cf9077bf46d2e1189c20c1c2db60e 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1739,17 +1739,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - } - - public boolean isSunBurnTick() { -- if (this.level().isDay() && !this.level().isClientSide) { -- float f = this.getLightLevelDependentMagicValue(); -- BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); -- boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow; -- -- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) { -- return true; -- } -- } -- -- return false; -+ return super.isSunBurnTick(); - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -index 93443238b77c04f6a9da3c25427d200ccf4699f8..892ae3a9ac2395896ff7881c49eaece8d46e831e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -66,6 +66,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - protected AbstractSkeleton(EntityType type, Level world) { - super(type, world); - this.reassessWeaponGoal(); -+ this.setShouldBurnInDay(true); // Purpur - } - - @Override -@@ -101,35 +102,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - } - - // Paper start -- private boolean shouldBurnInDay = true; -+ // private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity - keep methods for ABI compatibility - public boolean shouldBurnInDay() { return shouldBurnInDay; } - public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } - // Paper end - - @Override - public void aiStep() { -- boolean flag = shouldBurnInDay && this.isSunBurnTick(); // Paper - Configurable Burning -- -- if (flag) { -- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -- -- if (!itemstack.isEmpty()) { -- if (itemstack.isDamageableItem()) { -- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -- this.broadcastBreakEvent(EquipmentSlot.HEAD); -- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); -- } -- } -- -- flag = false; -- } -- -- if (flag) { -- this.setSecondsOnFire(8); -- } -- } -- -+ // Purpur start - implemented in LivingEntity - super.aiStep(); - } - -@@ -194,7 +174,6 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - } else { - this.goalSelector.addGoal(4, this.meleeGoal); - } -- - } - } - -@@ -238,7 +217,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - this.reassessWeaponGoal(); - // Paper start - if (nbt.contains("Paper.ShouldBurnInDay")) { -- this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); -+ // this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); // Purpur - implemented in LivingEntity - } - // Paper end - } -@@ -247,7 +226,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); -+ // nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - implemented in LivingEntity - } - // Paper end - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java -index 31706620960f5f153565f3cf64e32d0f4d10feb8..1df39e11d4fe3146fba9a0605c623384513311ac 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -20,6 +20,7 @@ public class Husk extends Zombie { - - public Husk(EntityType type, Level world) { - super(type, world); -+ this.setShouldBurnInDay(false); // Purpur - } - - // Purpur start -@@ -75,7 +76,7 @@ public class Husk extends Zombie { - - @Override - public boolean isSunSensitive() { -- return false; -+ return this.shouldBurnInDay; // Purpur - moved to LivingEntity - keep methods for ABI compatibility - } - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 7c9f6076f68de295e882e69137ac573db8d9698b..8605dfe152a09f31492226b1eb2a5eb39db9fcbc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -60,6 +60,7 @@ public class Phantom extends FlyingMob implements Enemy { - this.xpReward = 5; - this.moveControl = new Phantom.PhantomMoveControl(this); - this.lookControl = new Phantom.PhantomLookControl(this); -+ this.setShouldBurnInDay(true); // Purpur - } - - // Purpur start -@@ -253,16 +254,7 @@ public class Phantom extends FlyingMob implements Enemy { - - @Override - public void aiStep() { -- // Purpur start -- boolean burnFromDaylight = this.shouldBurnInDay && this.level().purpurConfig.phantomBurnInDaylight; -- boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; -- if (this.isAlive() && (burnFromDaylight || burnFromLightSource)) { // Paper - Configurable Burning -- if (getRider() == null || !this.isControllable()) -- // Purpur end -- if (getRider() == null || !this.isControllable()) // Purpur -- this.setSecondsOnFire(8); -- } -- -+ // Purpur - moved down to shouldBurnInDay() - super.aiStep(); - } - -@@ -290,7 +282,7 @@ public class Phantom extends FlyingMob implements Enemy { - if (nbt.hasUUID("Paper.SpawningEntity")) { - this.spawningEntity = nbt.getUUID("Paper.SpawningEntity"); - } -- if (nbt.contains("Paper.ShouldBurnInDay")) { -+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); - } - // Paper end -@@ -307,7 +299,7 @@ public class Phantom extends FlyingMob implements Enemy { - if (this.spawningEntity != null) { - nbt.putUUID("Paper.SpawningEntity", this.spawningEntity); - } -- nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); -+ // nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); // Purpur - implemented in LivingEntity - // Paper end - } - -@@ -373,8 +365,14 @@ public class Phantom extends FlyingMob implements Enemy { - } - public void setSpawningEntity(java.util.UUID entity) { this.spawningEntity = entity; } - -- private boolean shouldBurnInDay = true; -- public boolean shouldBurnInDay() { return shouldBurnInDay; } -+ // private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity - keep methods for ABI compatibility -+ // Purpur start -+ public boolean shouldBurnInDay() { -+ boolean burnFromDaylight = this.shouldBurnInDay && this.level().purpurConfig.phantomBurnInDaylight; -+ boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; -+ return burnFromDaylight || burnFromLightSource; -+ } -+ // Purpur End - public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } - // Paper end - private static enum AttackPhase { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index a676d66dcb5ee72e6d8ffef4e210a3d2c8d605f2..0bc90b6d5c5a3cb3477d41336a9bb1130ff32fa1 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -95,11 +95,12 @@ public class Zombie extends Monster { - private int inWaterTime; - public int conversionTime; - private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field -- private boolean shouldBurnInDay = true; // Paper -+ // private boolean shouldBurnInDay = true; // Paper // Purpur - implemented in LivingEntity - - public Zombie(EntityType type, Level world) { - super(type, world); - this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(world.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, world.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper -+ this.setShouldBurnInDay(true); // Purpur - } - - public Zombie(Level world) { -@@ -295,30 +296,7 @@ public class Zombie extends Monster { - - @Override - public void aiStep() { -- if (this.isAlive()) { -- boolean flag = this.isSunSensitive() && this.isSunBurnTick(); -- -- if (flag) { -- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -- -- if (!itemstack.isEmpty()) { -- if (itemstack.isDamageableItem()) { -- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -- this.broadcastBreakEvent(EquipmentSlot.HEAD); -- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); -- } -- } -- -- flag = false; -- } -- -- if (flag) { -- this.setSecondsOnFire(8); -- } -- } -- } -- -+ // Purpur - implemented in LivingEntity - super.aiStep(); - } - -@@ -356,6 +334,7 @@ public class Zombie extends Monster { - - } - -+ public boolean shouldBurnInDay() { return isSunSensitive(); } // Purpur - for ABI compatibility - public boolean isSunSensitive() { - return this.shouldBurnInDay; // Paper - use api value instead - } -@@ -485,7 +464,7 @@ public class Zombie extends Monster { - nbt.putBoolean("CanBreakDoors", this.canBreakDoors()); - nbt.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1); - nbt.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1); -- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper -+ // nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper // Purpur - implemented in LivingEntity - } - - @Override -@@ -499,7 +478,7 @@ public class Zombie extends Monster { - } - // Paper start - if (nbt.contains("Paper.ShouldBurnInDay")) { -- this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); -+ // this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); // Purpur - implemented in LivingEntity - } - // Paper end - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 283836981f5ed94dfc8999c3c5450213a1f2cfc1..feeca8d318fa0b7b22e728baa3bf3269d42bf0a6 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -224,6 +224,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - this.entityType = (type != null) ? type : EntityType.UNKNOWN; - } - -+ @Override -+ public boolean isInDaylight() { -+ return getHandle().isSunBurnTick(); -+ } -+ - public static CraftEntity getEntity(CraftServer server, Entity entity) { - /* - * Order is *EXTREMELY* important -- keep it right! =D -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index f39747767d9a8da86bb25c34415b06172f39bcfc..72dcac5708c06556549d268e54ecb4b6e71c93be 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -1111,5 +1111,15 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - if (slot == null) return; - getHandle().broadcastBreakEvent(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)); - } -+ -+ @Override -+ public boolean shouldBurnInDay() { -+ return getHandle().shouldBurnInDay(); -+ } -+ -+ @Override -+ public void setShouldBurnInDay(boolean shouldBurnInDay) { -+ getHandle().setShouldBurnInDay(shouldBurnInDay); -+ } - // Purpur end - } diff --git a/patches/server/0176-Config-MobEffect-by-world.patch b/patches/server/0176-Config-MobEffect-by-world.patch deleted file mode 100644 index ff3d92622..000000000 --- a/patches/server/0176-Config-MobEffect-by-world.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 31 May 2021 11:06:54 +0200 -Subject: [PATCH] Config MobEffect by world - - -diff --git a/src/main/java/net/minecraft/world/effect/MobEffect.java b/src/main/java/net/minecraft/world/effect/MobEffect.java -index 53cc6befb752affcfec65e18365f6d369448d407..01850fc596a85974287ff6750427186d21acac41 100644 ---- a/src/main/java/net/minecraft/world/effect/MobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/MobEffect.java -@@ -59,16 +59,16 @@ public class MobEffect { - public void applyEffectTick(LivingEntity entity, int amplifier) { - if (this == MobEffects.REGENERATION) { - if (entity.getHealth() < entity.getMaxHealth()) { -- entity.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit -+ entity.heal(entity.level().purpurConfig.entityHealthRegenAmount, RegainReason.MAGIC_REGEN); // CraftBukkit // Purpur - } - } else if (this == MobEffects.POISON) { -- if (entity.getHealth() > 1.0F) { -- entity.hurt(entity.damageSources().poison, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON -+ if (entity.getHealth() > entity.level().purpurConfig.entityMinimalHealthPoison) { // Purpur -+ entity.hurt(entity.damageSources().poison, entity.level().purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON // Purpur - } - } else if (this == MobEffects.WITHER) { -- entity.hurt(entity.damageSources().wither(), 1.0F); -+ entity.hurt(entity.damageSources().wither(), entity.level().purpurConfig.entityWitherDegenerationAmount); // Purpur - } else if (this == MobEffects.HUNGER && entity instanceof Player) { -- ((Player) entity).causeFoodExhaustion(0.005F * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent -+ ((Player) entity).causeFoodExhaustion(entity.level().purpurConfig.humanHungerExhaustionAmount * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent // Purpur - } else if (this == MobEffects.SATURATION && entity instanceof Player) { - if (!entity.level().isClientSide) { - // CraftBukkit start -@@ -76,7 +76,7 @@ public class MobEffect { - int oldFoodLevel = entityhuman.getFoodData().foodLevel; - org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, amplifier + 1 + oldFoodLevel); - if (!event.isCancelled()) { -- entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); -+ entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level().purpurConfig.humanSaturationRegenAmount); // Purpur - } - - ((CraftPlayer) entityhuman.getBukkitEntity()).sendHealthUpdate(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index bd485bc18e7012f14a7f85cda26f8a1ec2b0cca0..f95596f39021e3da3155c3175a6fcec8c6eb7ff9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -331,6 +331,21 @@ public class PurpurWorldConfig { - poweredRailBoostModifier = getDouble("gameplay-mechanics.minecart.powered-rail.boost-modifier", poweredRailBoostModifier); - } - -+ public float entityHealthRegenAmount = 1.0F; -+ public float entityMinimalHealthPoison = 1.0F; -+ public float entityPoisonDegenerationAmount = 1.0F; -+ public float entityWitherDegenerationAmount = 1.0F; -+ public float humanHungerExhaustionAmount = 0.005F; -+ public float humanSaturationRegenAmount = 1.0F; -+ private void mobEffectSettings() { -+ entityHealthRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.health-regen-amount", entityHealthRegenAmount); -+ entityMinimalHealthPoison = (float) getDouble("gameplay-mechanics.mob-effects.minimal-health-poison-amount", entityMinimalHealthPoison); -+ entityPoisonDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.poison-degeneration-amount", entityPoisonDegenerationAmount); -+ entityWitherDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.wither-degeneration-amount", entityWitherDegenerationAmount); -+ humanHungerExhaustionAmount = (float) getDouble("gameplay-mechanics.mob-effects.hunger-exhaustion-amount", humanHungerExhaustionAmount); -+ humanSaturationRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.saturation-regen-amount", humanSaturationRegenAmount); -+ } -+ - public boolean catSpawning; - public boolean patrolSpawning; - public boolean phantomSpawning; diff --git a/patches/server/0177-Beacon-Activation-Range-Configurable.patch b/patches/server/0177-Beacon-Activation-Range-Configurable.patch deleted file mode 100644 index 1f0fcaf33..000000000 --- a/patches/server/0177-Beacon-Activation-Range-Configurable.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Wed, 2 Jun 2021 02:45:47 +0200 -Subject: [PATCH] Beacon Activation Range Configurable - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -index 3b866e2c20ee7bfc981ff09b29065530de993778..12578b377b6e939971fb2dcba08637df60643e37 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -@@ -84,6 +84,16 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name - - public double getEffectRange() { - if (this.effectRange < 0) { -+ // Purpur Start -+ if (this.level != null) { -+ switch (this.levels) { -+ case 1: return this.level.purpurConfig.beaconLevelOne; -+ case 2: return this.level.purpurConfig.beaconLevelTwo; -+ case 3: return this.level.purpurConfig.beaconLevelThree; -+ case 4: return this.level.purpurConfig.beaconLevelFour; -+ } -+ } -+ // Purpur End - return this.levels * 10 + 10; - } else { - return effectRange; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f95596f39021e3da3155c3175a6fcec8c6eb7ff9..0eb16c3d850a92f3dc99e5daad9c3b6faba97137 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -508,6 +508,17 @@ public class PurpurWorldConfig { - anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage); - } - -+ public int beaconLevelOne = 20; -+ public int beaconLevelTwo = 30; -+ public int beaconLevelThree = 40; -+ public int beaconLevelFour = 50; -+ private void beaconSettings() { -+ beaconLevelOne = getInt("blocks.beacon.effect-range.level-1", beaconLevelOne); -+ beaconLevelTwo = getInt("blocks.beacon.effect-range.level-2", beaconLevelTwo); -+ beaconLevelThree = getInt("blocks.beacon.effect-range.level-3", beaconLevelThree); -+ beaconLevelFour = getInt("blocks.beacon.effect-range.level-4", beaconLevelFour); -+ } -+ - public boolean bedExplode = true; - public double bedExplosionPower = 5.0D; - public boolean bedExplosionFire = true; diff --git a/patches/server/0178-Add-toggle-for-sand-duping-fix.patch b/patches/server/0178-Add-toggle-for-sand-duping-fix.patch deleted file mode 100644 index 1c8f2c424..000000000 --- a/patches/server/0178-Add-toggle-for-sand-duping-fix.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 4 Jun 2021 09:13:54 -0500 -Subject: [PATCH] Add toggle for sand duping fix - - -diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 18eaccb39a4c81338a8cbebe3de03934913ac2a4..7ad5583f05b608d5a34a33fb77c5bf91f25de9a1 100644 ---- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -@@ -133,7 +133,7 @@ public class FallingBlockEntity extends Entity { - @Override - public void tick() { - // Paper start - fix sand duping -- if (this.isRemoved()) { -+ if (this.level().purpurConfig.fixSandDuping && this.isRemoved()) { // Purpur - return; - } - // Paper end - fix sand duping -@@ -150,7 +150,7 @@ public class FallingBlockEntity extends Entity { - this.move(MoverType.SELF, this.getDeltaMovement()); - - // Paper start - fix sand duping -- if (this.isRemoved()) { -+ if (this.level().purpurConfig.fixSandDuping && this.isRemoved()) { // Purpur - return; - } - // Paper end - fix sand duping -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0eb16c3d850a92f3dc99e5daad9c3b6faba97137..3dc0ce7a13238c5545fbf878af86f4c185e8931a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -687,6 +687,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean fixSandDuping = true; -+ private void sandSettings() { -+ fixSandDuping = getBoolean("blocks.sand.fix-duping", fixSandDuping); -+ } -+ - public boolean shulkerBoxAllowOversizedStacks = false; - private void shulkerBoxSettings() { - shulkerBoxAllowOversizedStacks = getBoolean("blocks.shulker_box.allow-oversized-stacks", shulkerBoxAllowOversizedStacks); diff --git a/patches/server/0179-Add-toggle-for-end-portal-safe-teleporting.patch b/patches/server/0179-Add-toggle-for-end-portal-safe-teleporting.patch deleted file mode 100644 index bdfe517bc..000000000 --- a/patches/server/0179-Add-toggle-for-end-portal-safe-teleporting.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 9 Jun 2021 11:03:40 -0500 -Subject: [PATCH] Add toggle for end portal safe teleporting - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 68be1bb59c7e5176c82ed128be5296a2511212ec..c99d273b8d686872d5699e4490769786951256e1 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3134,7 +3134,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - this.processPortalCooldown(); -- this.tickEndPortal(); // Paper - make end portalling safe -+ if (this.level().purpurConfig.endPortalSafeTeleporting) this.tickEndPortal(); // Paper - make end portalling safe // Purpur - } - } - -diff --git a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -index f80f6da484f4144e743079e5104bf503419074b2..2deddc746e43896584bd65ba8e7971a80acb4a4d 100644 ---- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -@@ -61,6 +61,22 @@ public class EndPortalBlock extends BaseEntityBlock { - // return; // CraftBukkit - always fire event in case plugins wish to change it - } - -+ // Purpur start -+ if (!world.purpurConfig.endPortalSafeTeleporting) { -+ // CraftBukkit start - Entity in portal -+ EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ())); -+ world.getCraftServer().getPluginManager().callEvent(event); -+ -+ if (entity instanceof ServerPlayer) { -+ ((ServerPlayer) entity).changeDimension(worldserver, PlayerTeleportEvent.TeleportCause.END_PORTAL); -+ return; -+ } -+ // CraftBukkit end -+ entity.changeDimension(worldserver); -+ return; -+ } -+ // Purpur end -+ - // Paper start - move all of this logic into portal tick - entity.portalWorld = ((ServerLevel)world); - entity.portalBlock = pos.immutable(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3dc0ce7a13238c5545fbf878af86f4c185e8931a..5c949339f386f805606e7d8f36785d44d53eb0f3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -635,6 +635,11 @@ public class PurpurWorldConfig { - furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath); - } - -+ public boolean endPortalSafeTeleporting = true; -+ private void endPortalSettings() { -+ endPortalSafeTeleporting = getBoolean("blocks.end_portal.safe-teleporting", endPortalSafeTeleporting); -+ } -+ - public boolean mobsSpawnOnPackedIce = true; - public boolean mobsSpawnOnBlueIce = true; - private void iceSettings() { diff --git a/patches/server/0180-Make-lightning-rod-range-configurable.patch b/patches/server/0180-Make-lightning-rod-range-configurable.patch deleted file mode 100644 index ded4440c8..000000000 --- a/patches/server/0180-Make-lightning-rod-range-configurable.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Wed, 23 Jun 2021 13:36:20 +0200 -Subject: [PATCH] Make lightning rod range configurable - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 49c412f46854ee877de1721b08629e8a3cd319f9..fea5481c98e4cbcaecb9f4adef35b7340ad0c9b8 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1136,7 +1136,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - return holder.is(PoiTypes.LIGHTNING_ROD); - }, (blockposition1) -> { - return blockposition1.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition1.getX(), blockposition1.getZ()) - 1; -- }, pos, 128, PoiManager.Occupancy.ANY); -+ }, pos, org.purpurmc.purpur.PurpurConfig.lightningRodRange, PoiManager.Occupancy.ANY); - - return optional.map((blockposition1) -> { - return blockposition1.above(1); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 2aaa4cff8d02c35bfe194b548027be904e1cf982..6919c6779e54526b7666eb6b21556b7b752bf053 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -270,6 +270,7 @@ public class PurpurConfig { - public static boolean cryingObsidianValidForPortalFrame = false; - public static int beeInsideBeeHive = 3; - public static boolean anvilCumulativeCost = true; -+ public static int lightningRodRange = 128; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -303,6 +304,7 @@ public class PurpurConfig { - cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); - beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); - anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); -+ lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange); - } - - public static boolean allowInfinityMending = false; diff --git a/patches/server/0181-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch b/patches/server/0181-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch deleted file mode 100644 index ad2fd48d6..000000000 --- a/patches/server/0181-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 24 Jun 2021 21:19:30 -0500 -Subject: [PATCH] Burp delay, burp after eating food fills hunger bar - completely - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 81ba8875f0077ac1be80533061bcb0e632c13ded..bea9f52a6240794d45128659a62487f48f9f03dd 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -188,6 +188,7 @@ public abstract class Player extends LivingEntity { - public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; - // Paper end - public int sixRowEnderchestSlotCount = -1; // Purpur -+ public int burpDelay = 0; // Purpur - - // CraftBukkit start - public boolean fauxSleeping; -@@ -263,6 +264,12 @@ public abstract class Player extends LivingEntity { - - @Override - public void tick() { -+ // Purpur start -+ if (this.burpDelay > 0 && --this.burpDelay == 0) { -+ this.level().playSound(null, getX(), getY(), getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1.0F, this.level().random.nextFloat() * 0.1F + 0.9F); -+ } -+ // Purpur end -+ - this.noPhysics = this.isSpectator(); - if (this.isSpectator()) { - this.setOnGround(false); -@@ -2380,7 +2387,7 @@ public abstract class Player extends LivingEntity { - public ItemStack eat(Level world, ItemStack stack) { - this.getFoodData().eat(stack.getItem(), stack); - this.awardStat(Stats.ITEM_USED.get(stack.getItem())); -- world.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); -+ // world.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); // Purpur - moved to tick() - if (this instanceof ServerPlayer) { - CriteriaTriggers.CONSUME_ITEM.trigger((ServerPlayer) this, stack); - } -diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java -index 2038df72f8d7d33d4105de8129628daf21de6f0f..31ec6022b305df0ae02364c916ecf18425a0bc36 100644 ---- a/src/main/java/net/minecraft/world/food/FoodData.java -+++ b/src/main/java/net/minecraft/world/food/FoodData.java -@@ -33,8 +33,10 @@ public class FoodData { - // CraftBukkit end - - public void eat(int food, float saturationModifier) { -+ int oldValue = this.foodLevel; // Purpur - this.foodLevel = Math.min(food + this.foodLevel, 20); - this.saturationLevel = Math.min(this.saturationLevel + (float) food * saturationModifier * 2.0F, (float) this.foodLevel); -+ if (this.entityhuman.level().purpurConfig.playerBurpWhenFull && this.foodLevel == 20 && oldValue < 20) this.entityhuman.burpDelay = this.entityhuman.level().purpurConfig.playerBurpDelay; // Purpur - } - - public void eat(Item item, ItemStack stack) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5c949339f386f805606e7d8f36785d44d53eb0f3..fb5028401408c4e335e4dde2ba845aa75d1115be 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -390,6 +390,8 @@ public class PurpurWorldConfig { - public boolean playerSleepNearMonsters = false; - public boolean playersSkipNight = true; - public double playerCriticalDamageMultiplier = 1.5D; -+ public int playerBurpDelay = 10; -+ public boolean playerBurpWhenFull = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -412,6 +414,8 @@ public class PurpurWorldConfig { - playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters); - playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight); - playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier); -+ playerBurpDelay = getInt("gameplay-mechanics.player.burp-delay", playerBurpDelay); -+ playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0182-Allow-player-join-full-server-by-permission.patch b/patches/server/0182-Allow-player-join-full-server-by-permission.patch deleted file mode 100644 index a708631be..000000000 --- a/patches/server/0182-Allow-player-join-full-server-by-permission.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Thu, 24 Jun 2021 02:28:32 +0200 -Subject: [PATCH] Allow player join full server by permission - - -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 9a60fa370f37179a7460b316d26e601197b478c7..43fdebcd107a7ca08b93de1d649b4d5e6acf5e81 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -749,7 +749,7 @@ public abstract class PlayerList { - event.disallow(PlayerLoginEvent.Result.KICK_BANNED, PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure - } else { - // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile) ? IChatBaseComponent.translatable("multiplayer.disconnect.server_full") : null; -- if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile)) { -+ if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameprofile))) { // Purpur - event.disallow(PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure - } - } diff --git a/patches/server/0183-Add-portal-waiting-option-permission-bypass.patch b/patches/server/0183-Add-portal-waiting-option-permission-bypass.patch deleted file mode 100644 index 644ade4af..000000000 --- a/patches/server/0183-Add-portal-waiting-option-permission-bypass.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 26 Jun 2021 23:05:12 -0500 -Subject: [PATCH] Add portal waiting option & permission bypass - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index bea9f52a6240794d45128659a62487f48f9f03dd..528aeec3f322ee2094098093e6016e41ff382cf3 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -189,6 +189,7 @@ public abstract class Player extends LivingEntity { - // Paper end - public int sixRowEnderchestSlotCount = -1; // Purpur - public int burpDelay = 0; // Purpur -+ public boolean canPortalInstant = false; // Purpur - - // CraftBukkit start - public boolean fauxSleeping; -@@ -469,7 +470,7 @@ public abstract class Player extends LivingEntity { - - @Override - public int getPortalWaitTime() { -- return this.abilities.invulnerable ? 1 : 80; -+ return canPortalInstant ? 1 : this.abilities.invulnerable ? this.level().purpurConfig.playerCreativePortalWaitTime : this.level().purpurConfig.playerPortalWaitTime; // Purpur - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -index 5fe9a0985432ac6cdd28a2a138854a24f10e42ba..9163bb797a22daedbc822ecc2ad33683a2d8a022 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -@@ -266,6 +266,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { - @Override - public void recalculatePermissions() { - this.perm.recalculatePermissions(); -+ getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fb5028401408c4e335e4dde2ba845aa75d1115be..1b8d6ad995e7944f4b8c098c8f4df34b8670b859 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -392,6 +392,8 @@ public class PurpurWorldConfig { - public double playerCriticalDamageMultiplier = 1.5D; - public int playerBurpDelay = 10; - public boolean playerBurpWhenFull = false; -+ public int playerPortalWaitTime = 80; -+ public int playerCreativePortalWaitTime = 1; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -416,6 +418,8 @@ public class PurpurWorldConfig { - playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier); - playerBurpDelay = getInt("gameplay-mechanics.player.burp-delay", playerBurpDelay); - playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull); -+ playerPortalWaitTime = getInt("gameplay-mechanics.player.portal-wait-time", playerPortalWaitTime); -+ playerCreativePortalWaitTime = getInt("gameplay-mechanics.player.creative-portal-wait-time", playerCreativePortalWaitTime); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0184-Shulker-spawn-from-bullet-options.patch b/patches/server/0184-Shulker-spawn-from-bullet-options.patch deleted file mode 100644 index 9be10ff36..000000000 --- a/patches/server/0184-Shulker-spawn-from-bullet-options.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 25 Jun 2021 19:48:21 -0500 -Subject: [PATCH] Shulker spawn from bullet options - -(0 - 1) / 5.0 = -0.2 (can never happen because self is included in count) -(1 - 1) / 5.0 = 0.0 1.0 - 0.0 = 1.0 100% (just self) -(2 - 1) / 5.0 = 0.2 1.0 - 0.2 = 0.8 80% (1 other shulker) -(3 - 1) / 5.0 = 0.4 1.0 - 0.4 = 0.6 60% (2 other shulkers) -(4 - 1) / 5.0 = 0.6 1.0 - 0.6 = 0.4 40% (3 other shulkers) -(5 - 1) / 5.0 = 0.8 1.0 - 0.8 = 0.2 20% (4 other shulkers) -(6 - 1) / 5.0 = 1.0 1.0 - 1.0 = 0.0 0% (5 other shulkers) -(7 - 1) / 5.0 = 1.2 1.0 - 1.2 = -0.2 0% (6 other shulkers) - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 27b05a4657ba201096973640d7a8cbabdd69f053..a0b19ffbdb64c40d8b92ed82104dde323b466190 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -511,12 +511,21 @@ public class Shulker extends AbstractGolem implements VariantHolder= f) { -+ if ((!this.level().purpurConfig.shulkerSpawnFromBulletRequireOpenLid || !this.isClosed()) && this.teleportSomewhere()) { -+ // Purpur start -+ float chance = this.level().purpurConfig.shulkerSpawnFromBulletBaseChance; -+ if (!this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation.isBlank()) { -+ int nearby = this.level().getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(this.level().purpurConfig.shulkerSpawnFromBulletNearbyRange), Entity::isAlive).size(); -+ try { -+ chance -= ((Number) scriptEngine.eval("let nearby = " + nearby + "; " + this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation)).floatValue(); -+ } catch (javax.script.ScriptException e) { -+ e.printStackTrace(); -+ chance -= (nearby - 1) / 5.0F; -+ } -+ } -+ if (this.level().random.nextFloat() <= chance) { - Shulker entityshulker = (Shulker) EntityType.SHULKER.create(this.level()); -+ // Purpur end - - if (entityshulker != null) { - entityshulker.setVariant(this.getVariant()); -@@ -628,7 +637,7 @@ public class Shulker extends AbstractGolem implements VariantHolder getVariant() { -- return Optional.ofNullable(this.getColor()); -+ return Optional.ofNullable(this.level().purpurConfig.shulkerSpawnFromBulletRandomColor ? DyeColor.random(this.level().random) : this.getColor()); // Purpur - } - - @Nullable -diff --git a/src/main/java/net/minecraft/world/item/DyeColor.java b/src/main/java/net/minecraft/world/item/DyeColor.java -index 88e1c2431d51d8cdc3d555b711e506648225d289..ac8735cc9d127fc1f867b40d4000c033ef73bb83 100644 ---- a/src/main/java/net/minecraft/world/item/DyeColor.java -+++ b/src/main/java/net/minecraft/world/item/DyeColor.java -@@ -103,4 +103,10 @@ public enum DyeColor implements StringRepresentable { - public String getSerializedName() { - return this.name; - } -+ -+ // Purpur start -+ public static DyeColor random(net.minecraft.util.RandomSource random) { -+ return values()[random.nextInt(values().length)]; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1b8d6ad995e7944f4b8c098c8f4df34b8670b859..9da836e7bf5f2ab51eb323b035dfc02ef390cc40 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1930,6 +1930,11 @@ public class PurpurWorldConfig { - public boolean shulkerControllable = true; - public double shulkerMaxHealth = 30.0D; - public boolean shulkerTakeDamageFromWater = false; -+ public float shulkerSpawnFromBulletBaseChance = 1.0F; -+ public boolean shulkerSpawnFromBulletRequireOpenLid = true; -+ public double shulkerSpawnFromBulletNearbyRange = 8.0D; -+ public String shulkerSpawnFromBulletNearbyEquation = "(nearby - 1) / 5.0"; -+ public boolean shulkerSpawnFromBulletRandomColor = false; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -@@ -1941,6 +1946,11 @@ public class PurpurWorldConfig { - } - shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth); - shulkerTakeDamageFromWater = getBoolean("mobs.shulker.takes-damage-from-water", shulkerTakeDamageFromWater); -+ shulkerSpawnFromBulletBaseChance = (float) getDouble("mobs.shulker.spawn-from-bullet.base-chance", shulkerSpawnFromBulletBaseChance); -+ shulkerSpawnFromBulletRequireOpenLid = getBoolean("mobs.shulker.spawn-from-bullet.require-open-lid", shulkerSpawnFromBulletRequireOpenLid); -+ shulkerSpawnFromBulletNearbyRange = getDouble("mobs.shulker.spawn-from-bullet.nearby-range", shulkerSpawnFromBulletNearbyRange); -+ shulkerSpawnFromBulletNearbyEquation = getString("mobs.shulker.spawn-from-bullet.nearby-equation", shulkerSpawnFromBulletNearbyEquation); -+ shulkerSpawnFromBulletRandomColor = getBoolean("mobs.shulker.spawn-from-bullet.random-color", shulkerSpawnFromBulletRandomColor); - } - - public boolean silverfishRidable = false; diff --git a/patches/server/0185-Eating-glow-berries-adds-glow-effect.patch b/patches/server/0185-Eating-glow-berries-adds-glow-effect.patch deleted file mode 100644 index 66613f891..000000000 --- a/patches/server/0185-Eating-glow-berries-adds-glow-effect.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 28 Jun 2021 14:07:35 -0500 -Subject: [PATCH] Eating glow berries adds glow effect - - -diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java -index e6f8cb165f7e3da5f0edfc952d14059516de8acf..31f5ed9dd1727eee24804a384817d2b76a45676b 100644 ---- a/src/main/java/net/minecraft/world/item/Items.java -+++ b/src/main/java/net/minecraft/world/item/Items.java -@@ -1184,7 +1184,7 @@ public class Items { - public static final Item LANTERN = registerBlock(Blocks.LANTERN); - public static final Item SOUL_LANTERN = registerBlock(Blocks.SOUL_LANTERN); - public static final Item SWEET_BERRIES = registerItem("sweet_berries", new ItemNameBlockItem(Blocks.SWEET_BERRY_BUSH, (new Item.Properties()).food(Foods.SWEET_BERRIES))); -- public static final Item GLOW_BERRIES = registerItem("glow_berries", new ItemNameBlockItem(Blocks.CAVE_VINES, (new Item.Properties()).food(Foods.GLOW_BERRIES))); -+ public static final Item GLOW_BERRIES = registerItem("glow_berries", new org.purpurmc.purpur.item.GlowBerryItem(Blocks.CAVE_VINES, (new Item.Properties()).food(Foods.GLOW_BERRIES))); // Purpur - public static final Item CAMPFIRE = registerBlock(Blocks.CAMPFIRE); - public static final Item SOUL_CAMPFIRE = registerBlock(Blocks.SOUL_CAMPFIRE); - public static final Item SHROOMLIGHT = registerBlock(Blocks.SHROOMLIGHT); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9da836e7bf5f2ab51eb323b035dfc02ef390cc40..6c8c3272bd1c5f9c2082cdcbf5af2d03ade69517 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -220,6 +220,7 @@ public class PurpurWorldConfig { - public int enderPearlCooldown = 20; - public int enderPearlCooldownCreative = 20; - public float enderPearlEndermiteChance = 0.05F; -+ public int glowBerriesEatGlowDuration = 0; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -265,6 +266,7 @@ public class PurpurWorldConfig { - enderPearlCooldown = getInt("gameplay-mechanics.item.ender-pearl.cooldown", enderPearlCooldown); - enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative); - enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance); -+ glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration); - } - - public double minecartMaxSpeed = 0.4D; -diff --git a/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7f526883495b3222746de3d0442e9e4fb5107036 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java -@@ -0,0 +1,26 @@ -+package org.purpurmc.purpur.item; -+ -+import net.minecraft.server.level.ServerPlayer; -+import net.minecraft.world.effect.MobEffectInstance; -+import net.minecraft.world.effect.MobEffects; -+import net.minecraft.world.entity.LivingEntity; -+import net.minecraft.world.item.ItemNameBlockItem; -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.Block; -+import org.bukkit.event.entity.EntityPotionEffectEvent; -+ -+public class GlowBerryItem extends ItemNameBlockItem { -+ public GlowBerryItem(Block block, Properties settings) { -+ super(block, settings); -+ } -+ -+ @Override -+ public ItemStack finishUsingItem(ItemStack stack, Level world, LivingEntity user) { -+ ItemStack result = super.finishUsingItem(stack, world, user); -+ if (world.purpurConfig.glowBerriesEatGlowDuration > 0 && user instanceof ServerPlayer player) { -+ player.addEffect(new MobEffectInstance(MobEffects.GLOWING, world.purpurConfig.glowBerriesEatGlowDuration), EntityPotionEffectEvent.Cause.FOOD); -+ } -+ return result; -+ } -+} diff --git a/patches/server/0186-Option-to-make-drowned-break-doors.patch b/patches/server/0186-Option-to-make-drowned-break-doors.patch deleted file mode 100644 index c450eb4a8..000000000 --- a/patches/server/0186-Option-to-make-drowned-break-doors.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Fri, 25 Jun 2021 13:56:15 +0200 -Subject: [PATCH] Option to make drowned break doors - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 902f831ea001145f73691b96f2fca8245a4fc05a..9346d634dd2b8cb3d89aa7ccf12fec6ff79bbe2f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -29,6 +29,7 @@ import net.minecraft.world.entity.ai.goal.MoveToBlockGoal; - import net.minecraft.world.entity.ai.goal.RandomStrollGoal; - import net.minecraft.world.entity.ai.goal.RangedAttackGoal; - import net.minecraft.world.entity.ai.goal.ZombieAttackGoal; -+import net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal; - import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; - import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; - import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; -@@ -122,6 +123,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0D, false)); - this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0D, this.level().getSeaLevel())); -+ if (level().purpurConfig.drownedBreakDoors) this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors)); - this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D)); - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Drowned.class})).setAlertOthers(ZombifiedPiglin.class)); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::okTarget)); -@@ -171,7 +173,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - - @Override - public boolean supportsBreakDoorGoal() { -- return false; -+ return level().purpurConfig.drownedBreakDoors ? true : false; - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6c8c3272bd1c5f9c2082cdcbf5af2d03ade69517..32be01402d3966a64dbc0b3f63289638163a1e70 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1076,6 +1076,7 @@ public class PurpurWorldConfig { - public double drownedJockeyChance = 0.05D; - public boolean drownedJockeyTryExistingChickens = true; - public boolean drownedTakeDamageFromWater = false; -+ public boolean drownedBreakDoors = false; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -@@ -1091,6 +1092,7 @@ public class PurpurWorldConfig { - drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance); - drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens); - drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater); -+ drownedBreakDoors = getBoolean("mobs.drowned.can-break-doors", drownedBreakDoors); - } - - public boolean elderGuardianRidable = false; diff --git a/patches/server/0187-Configurable-hunger-starvation-damage.patch b/patches/server/0187-Configurable-hunger-starvation-damage.patch deleted file mode 100644 index 70dea8a9b..000000000 --- a/patches/server/0187-Configurable-hunger-starvation-damage.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 29 Jun 2021 23:44:36 -0400 -Subject: [PATCH] Configurable hunger starvation damage - - -diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java -index 31ec6022b305df0ae02364c916ecf18425a0bc36..e54af9ff2a786e919b8261aa27509be942e70261 100644 ---- a/src/main/java/net/minecraft/world/food/FoodData.java -+++ b/src/main/java/net/minecraft/world/food/FoodData.java -@@ -102,7 +102,7 @@ public class FoodData { - ++this.tickTimer; - if (this.tickTimer >= this.starvationRate) { // CraftBukkit - add regen rate manipulation - if (player.getHealth() > 10.0F || enumdifficulty == Difficulty.HARD || player.getHealth() > 1.0F && enumdifficulty == Difficulty.NORMAL) { -- player.hurt(player.damageSources().starve(), 1.0F); -+ player.hurt(player.damageSources().starve(), player.level().purpurConfig.hungerStarvationDamage); // Purpur - } - - this.tickTimer = 0; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 32be01402d3966a64dbc0b3f63289638163a1e70..1d7510da470ae85a622f6954027e163e9825f07f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2587,4 +2587,9 @@ public class PurpurWorldConfig { - zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry); - zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater); - } -+ -+ public float hungerStarvationDamage = 1.0F; -+ private void hungerSettings() { -+ hungerStarvationDamage = (float) getDouble("hunger.starvation-damage", hungerStarvationDamage); -+ } - } diff --git a/patches/server/0189-Add-uptime-command.patch b/patches/server/0189-Add-uptime-command.patch deleted file mode 100644 index 1d2350db3..000000000 --- a/patches/server/0189-Add-uptime-command.patch +++ /dev/null @@ -1,143 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 1 Jul 2021 15:48:02 -0500 -Subject: [PATCH] Add uptime command - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 4e721dfca7559620d8ce65a6703f2089a839f4a0..7aae9e3c60ba15b8dcd8174a4d70866edebb6cca 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -227,6 +227,7 @@ public class Commands { - org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur -+ org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur - } - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 70c2ccc971600f856b90b2fb1b51cb2f51e21cdd..f814bb61cb6981202db1a04ef3c114afb0aa55ee 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -294,6 +294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); - public int autosavePeriod; - public Commands vanillaCommandDispatcher; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6919c6779e54526b7666eb6b21556b7b752bf053..f5a5cc0f954f33f75b542c33d8385076a8aad163 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -184,6 +184,7 @@ public class PurpurConfig { - public static String pingCommandOutput = "%s's ping is %sms"; - public static String tpsbarCommandOutput = "Tpsbar toggled for "; - public static String dontRunWithScissors = "Don't run with scissors!"; -+ public static String uptimeCommandOutput = "Server uptime is "; - public static String unverifiedUsername = "default"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); -@@ -197,6 +198,7 @@ public class PurpurConfig { - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); - tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); - dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); -+ uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput); - unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername); - } - -@@ -249,6 +251,15 @@ public class PurpurConfig { - public static int commandTPSBarTickInterval = 20; - public static boolean commandGamemodeRequiresPermission = false; - public static boolean hideHiddenPlayersFromEntitySelector = false; -+ public static String uptimeFormat = ""; -+ public static String uptimeDay = "%02d day, "; -+ public static String uptimeDays = "%02d days, "; -+ public static String uptimeHour = "%02d hour, "; -+ public static String uptimeHours = "%02d hours, "; -+ public static String uptimeMinute = "%02d minute, and "; -+ public static String uptimeMinutes = "%02d minutes, and "; -+ public static String uptimeSecond = "%02d second"; -+ public static String uptimeSeconds = "%02d seconds"; - private static void commandSettings() { - commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); - commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); -@@ -262,6 +273,15 @@ public class PurpurConfig { - commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); - commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission); - hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector); -+ uptimeFormat = getString("settings.command.uptime.format", uptimeFormat); -+ uptimeDay = getString("settings.command.uptime.day", uptimeDay); -+ uptimeDays = getString("settings.command.uptime.days", uptimeDays); -+ uptimeHour = getString("settings.command.uptime.hour", uptimeHour); -+ uptimeHours = getString("settings.command.uptime.hours", uptimeHours); -+ uptimeMinute = getString("settings.command.uptime.minute", uptimeMinute); -+ uptimeMinutes = getString("settings.command.uptime.minutes", uptimeMinutes); -+ uptimeSecond = getString("settings.command.uptime.second", uptimeSecond); -+ uptimeSeconds = getString("settings.command.uptime.seconds", uptimeSeconds); - } - - public static int barrelRows = 3; -diff --git a/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..4bb475099bcf8f05d5f1474e7fbf29c57c2c40cd ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java -@@ -0,0 +1,55 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.server.MinecraftServer; -+import org.purpurmc.purpur.PurpurConfig; -+ -+import java.util.concurrent.TimeUnit; -+import java.util.function.Function; -+ -+public class UptimeCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("uptime") -+ .requires((listener) -> listener.hasPermission(2, "bukkit.command.uptime")) -+ .executes((context) -> execute(context.getSource())) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender) { -+ Data data = new Data(); -+ -+ data.format = PurpurConfig.uptimeFormat; -+ data.hide = true; -+ data.millis = System.currentTimeMillis() - MinecraftServer.startTimeMillis; -+ -+ process(data, "", PurpurConfig.uptimeDay, PurpurConfig.uptimeDays, TimeUnit.DAYS, TimeUnit.MILLISECONDS::toDays); -+ process(data, "", PurpurConfig.uptimeHour, PurpurConfig.uptimeHours, TimeUnit.HOURS, TimeUnit.MILLISECONDS::toHours); -+ process(data, "", PurpurConfig.uptimeMinute, PurpurConfig.uptimeMinutes, TimeUnit.MINUTES, TimeUnit.MILLISECONDS::toMinutes); -+ data.hide = false; // never hide seconds -+ process(data, "", PurpurConfig.uptimeSecond, PurpurConfig.uptimeSeconds, TimeUnit.SECONDS, TimeUnit.MILLISECONDS::toSeconds); -+ -+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.uptimeCommandOutput, Placeholder.unparsed("uptime", data.format)); -+ sender.sendSuccess(output, false); -+ return 1; -+ } -+ -+ private static void process(Data data, String replace, String singular, String plural, TimeUnit unit, Function func) { -+ if (data.format.contains(replace)) { -+ long val = func.apply(data.millis); -+ if (data.hide) data.hide = val == 0; -+ if (!data.hide) data.millis -= unit.toMillis(val); -+ data.format = data.format.replace(replace, data.hide ? "" : String.format(val == 1 ? singular : plural, val)); -+ } -+ } -+ -+ private static class Data { -+ String format; -+ boolean hide; -+ long millis; -+ } -+} diff --git a/patches/server/0190-Tool-actionable-options.patch b/patches/server/0190-Tool-actionable-options.patch deleted file mode 100644 index 33a73392f..000000000 --- a/patches/server/0190-Tool-actionable-options.patch +++ /dev/null @@ -1,521 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 2 Jul 2021 20:54:29 -0500 -Subject: [PATCH] Tool actionable options - - -diff --git a/src/main/java/net/minecraft/world/item/AxeItem.java b/src/main/java/net/minecraft/world/item/AxeItem.java -index 18898e16ec42f6b694b06e09d9174b60d62450d7..20f33b77b4a9494be227456bc742a029eb0af59b 100644 ---- a/src/main/java/net/minecraft/world/item/AxeItem.java -+++ b/src/main/java/net/minecraft/world/item/AxeItem.java -@@ -33,29 +33,32 @@ public class AxeItem extends DiggerItem { - BlockPos blockPos = context.getClickedPos(); - Player player = context.getPlayer(); - BlockState blockState = level.getBlockState(blockPos); -- Optional optional = this.getStripped(blockState); -- Optional optional2 = WeatheringCopper.getPrevious(blockState); -- Optional optional3 = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(blockState.getBlock())).map((block) -> { -- return block.withPropertiesOf(blockState); -- }); -+ // Purpur start -+ Block clickedBlock = level.getBlockState(blockPos).getBlock(); -+ Optional optional = Optional.ofNullable(level.purpurConfig.axeStrippables.get(blockState.getBlock())); -+ Optional optional2 = Optional.ofNullable(level.purpurConfig.axeWeatherables.get(blockState.getBlock())); -+ Optional optional3 = Optional.ofNullable(level.purpurConfig.axeWaxables.get(blockState.getBlock())); -+ // Purpur end - ItemStack itemStack = context.getItemInHand(); -- Optional optional4 = Optional.empty(); -+ Optional optional4 = Optional.empty(); // Purpur - if (optional.isPresent()) { -- level.playSound(player, blockPos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); -+ if (!STRIPPABLES.containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - optional4 = optional; - } else if (optional2.isPresent()) { -- level.playSound(player, blockPos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); -+ if (!HoneycombItem.WAXABLES.get().containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - level.levelEvent(player, 3005, blockPos, 0); - optional4 = optional2; - } else if (optional3.isPresent()) { -- level.playSound(player, blockPos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); -+ if (!HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - level.levelEvent(player, 3004, blockPos, 0); - optional4 = optional3; - } - - if (optional4.isPresent()) { -+ org.purpurmc.purpur.tool.Actionable actionable = optional4.get(); // Purpur -+ BlockState state = actionable.into().withPropertiesOf(blockState); // Purpur - // Paper start - EntityChangeBlockEvent -- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, optional4.get())) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, state)) { // Purpur - return InteractionResult.PASS; - } - // Paper end -@@ -63,15 +66,22 @@ public class AxeItem extends DiggerItem { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, blockPos, itemStack); - } - -- level.setBlock(blockPos, optional4.get(), 11); -- level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, optional4.get())); -+ // Purpur start -+ level.setBlock(blockPos, state, 11); -+ actionable.drops().forEach((drop, chance) -> { -+ if (level.random.nextDouble() < chance) { -+ Block.popResourceFromFace(level, blockPos, context.getClickedFace(), new ItemStack(drop)); -+ } -+ }); -+ level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, state)); -+ // Purpur end - if (player != null) { - itemStack.hurtAndBreak(1, player, (p) -> { - p.broadcastBreakEvent(context.getHand()); - }); - } - -- return InteractionResult.sidedSuccess(level.isClientSide); -+ return InteractionResult.SUCCESS; // Purpur - force arm swing - } else { - return InteractionResult.PASS; - } -diff --git a/src/main/java/net/minecraft/world/item/HoeItem.java b/src/main/java/net/minecraft/world/item/HoeItem.java -index 180aec596110309aade13d2080f8824d152b07cb..552c31c0f3746dd35388395036e70a925bf00bd4 100644 ---- a/src/main/java/net/minecraft/world/item/HoeItem.java -+++ b/src/main/java/net/minecraft/world/item/HoeItem.java -@@ -34,15 +34,23 @@ public class HoeItem extends DiggerItem { - public InteractionResult useOn(UseOnContext context) { - Level level = context.getLevel(); - BlockPos blockPos = context.getClickedPos(); -- Pair, Consumer> pair = TILLABLES.get(level.getBlockState(blockPos).getBlock()); -- if (pair == null) { -- return InteractionResult.PASS; -- } else { -- Predicate predicate = pair.getFirst(); -- Consumer consumer = pair.getSecond(); -+ // Purpur start -+ Block clickedBlock = level.getBlockState(blockPos).getBlock(); -+ var tillable = level.purpurConfig.hoeTillables.get(clickedBlock); -+ if (tillable == null) { return InteractionResult.PASS; } else { -+ Predicate predicate = tillable.condition().predicate(); -+ Consumer consumer = (ctx) -> { -+ level.setBlock(blockPos, tillable.into().defaultBlockState(), 11); -+ tillable.drops().forEach((drop, chance) -> { -+ if (level.random.nextDouble() < chance) { -+ Block.popResourceFromFace(level, blockPos, ctx.getClickedFace(), new ItemStack(drop)); -+ } -+ }); -+ }; -+ // Purpur end - if (predicate.test(context)) { - Player player = context.getPlayer(); -- level.playSound(player, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); -+ if (!TILLABLES.containsKey(clickedBlock)) level.playSound(null, blockPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - if (!level.isClientSide) { - consumer.accept(context); - if (player != null) { -@@ -52,7 +60,7 @@ public class HoeItem extends DiggerItem { - } - } - -- return InteractionResult.sidedSuccess(level.isClientSide); -+ return InteractionResult.SUCCESS; // Purpur - force arm swing - } else { - return InteractionResult.PASS; - } -diff --git a/src/main/java/net/minecraft/world/item/ShovelItem.java b/src/main/java/net/minecraft/world/item/ShovelItem.java -index 21212462e6b415e96536a27b2c009d1562f18946..193bcb12152347f5f02ce18b01ba918e5e838f30 100644 ---- a/src/main/java/net/minecraft/world/item/ShovelItem.java -+++ b/src/main/java/net/minecraft/world/item/ShovelItem.java -@@ -37,9 +37,12 @@ public class ShovelItem extends DiggerItem { - BlockState blockState2 = FLATTENABLES.get(blockState.getBlock()); - BlockState blockState3 = null; - Runnable afterAction = null; // Paper -- if (blockState2 != null && level.getBlockState(blockPos.above()).isAir()) { -- afterAction = () -> level.playSound(player, blockPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper -- blockState3 = blockState2; -+ // Purpur start -+ var flattenable = level.purpurConfig.shovelFlattenables.get(blockState.getBlock()); -+ if (flattenable != null && level.getBlockState(blockPos.above()).isAir()) { -+ afterAction = () -> {if (!FLATTENABLES.containsKey(blockState.getBlock())) level.playSound(null, blockPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F);}; // Paper -+ blockState3 = flattenable.into().defaultBlockState(); -+ // Purpur end - } else if (blockState.getBlock() instanceof CampfireBlock && blockState.getValue(CampfireBlock.LIT)) { - afterAction = () -> { // Paper - if (!level.isClientSide()) { -@@ -68,7 +71,7 @@ public class ShovelItem extends DiggerItem { - } - } - -- return InteractionResult.sidedSuccess(level.isClientSide); -+ return InteractionResult.SUCCESS; // Purpur - force arm swing - } else { - return InteractionResult.PASS; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1d7510da470ae85a622f6954027e163e9825f07f..773bfaa4a05e86c700089e08a1bdfe637ef98870 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -511,6 +511,206 @@ public class PurpurWorldConfig { - }); - } - -+ public Map axeStrippables = new HashMap<>(); -+ public Map axeWaxables = new HashMap<>(); -+ public Map axeWeatherables = new HashMap<>(); -+ public Map hoeTillables = new HashMap<>(); -+ public Map shovelFlattenables = new HashMap<>(); -+ private void toolSettings() { -+ axeStrippables.clear(); -+ axeWaxables.clear(); -+ axeWeatherables.clear(); -+ hoeTillables.clear(); -+ shovelFlattenables.clear(); -+ if (PurpurConfig.version < 18) { -+ ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + ".tools.hoe.tilling"); -+ if (section != null) { -+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tillables", section); -+ PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tilling", null); -+ } -+ section = PurpurConfig.config.getConfigurationSection("world-settings.default.tools.hoe.tilling"); -+ if (section != null) { -+ PurpurConfig.config.set("world-settings.default.tools.hoe.tillables", section); -+ PurpurConfig.config.set("world-settings.default.tools.hoe.tilling", null); -+ } -+ } -+ if (PurpurConfig.version < 29) { -+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap())); -+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap())); -+ } -+ if (PurpurConfig.version < 32) { -+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap())); -+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap())); -+ PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap())); -+ } -+ if (PurpurConfig.version < 33) { -+ getList("gameplay-mechanics.shovel-turns-block-to-grass-path", new ArrayList(){{ -+ add("minecraft:coarse_dirt"); -+ add("minecraft:dirt"); -+ add("minecraft:grass_block"); -+ add("minecraft:mycelium"); -+ add("minecraft:podzol"); -+ add("minecraft:rooted_dirt"); -+ }}).forEach(key -> { -+ PurpurConfig.config.set("world-settings.default.tools.shovel.flattenables." + key.toString(), Map.of("into", "minecraft:dirt_path", "drops", new HashMap())); -+ }); -+ set("gameplay-mechanics.shovel-turns-block-to-grass-path", null); -+ } -+ getMap("tools.axe.strippables", Map.ofEntries( -+ Map.entry("minecraft:oak_wood", Map.of("into", "minecraft:stripped_oak_wood", "drops", new HashMap())), -+ Map.entry("minecraft:oak_log", Map.of("into", "minecraft:stripped_oak_log", "drops", new HashMap())), -+ Map.entry("minecraft:dark_oak_wood", Map.of("into", "minecraft:stripped_dark_oak_wood", "drops", new HashMap())), -+ Map.entry("minecraft:dark_oak_log", Map.of("into", "minecraft:stripped_dark_oak_log", "drops", new HashMap())), -+ Map.entry("minecraft:acacia_wood", Map.of("into", "minecraft:stripped_acacia_wood", "drops", new HashMap())), -+ Map.entry("minecraft:acacia_log", Map.of("into", "minecraft:stripped_acacia_log", "drops", new HashMap())), -+ Map.entry("minecraft:birch_wood", Map.of("into", "minecraft:stripped_birch_wood", "drops", new HashMap())), -+ Map.entry("minecraft:birch_log", Map.of("into", "minecraft:stripped_birch_log", "drops", new HashMap())), -+ Map.entry("minecraft:jungle_wood", Map.of("into", "minecraft:stripped_jungle_wood", "drops", new HashMap())), -+ Map.entry("minecraft:jungle_log", Map.of("into", "minecraft:stripped_jungle_log", "drops", new HashMap())), -+ Map.entry("minecraft:spruce_wood", Map.of("into", "minecraft:stripped_spruce_wood", "drops", new HashMap())), -+ Map.entry("minecraft:spruce_log", Map.of("into", "minecraft:stripped_spruce_log", "drops", new HashMap())), -+ Map.entry("minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap())), -+ Map.entry("minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap())), -+ Map.entry("minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap())), -+ Map.entry("minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap())), -+ Map.entry("minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap())), -+ Map.entry("minecraft:warped_stem", Map.of("into", "minecraft:stripped_warped_stem", "drops", new HashMap())), -+ Map.entry("minecraft:warped_hyphae", Map.of("into", "minecraft:stripped_warped_hyphae", "drops", new HashMap())), -+ Map.entry("minecraft:crimson_stem", Map.of("into", "minecraft:stripped_crimson_stem", "drops", new HashMap())), -+ Map.entry("minecraft:crimson_hyphae", Map.of("into", "minecraft:stripped_crimson_hyphae", "drops", new HashMap()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables`: " + blockId); return; } -+ if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + "`"); return; } -+ String intoId = (String) map.get("into"); -+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId)); -+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables." + blockId + ".into`: " + intoId); return; } -+ Object dropsObj = map.get("drops"); -+ if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + ".drops`"); return; } -+ Map drops = new HashMap<>(); -+ dropsMap.forEach((itemId, chance) -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString())); -+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.strippables." + blockId + ".drops`: " + itemId); return; } -+ drops.put(item, (double) chance); -+ }); -+ axeStrippables.put(block, new Strippable(into, drops)); -+ }); -+ getMap("tools.axe.waxables", Map.ofEntries( -+ Map.entry("minecraft:waxed_copper_block", Map.of("into", "minecraft:copper_block", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_exposed_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_weathered_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_oxidized_copper", Map.of("into", "minecraft:oxidized_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_exposed_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_weathered_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_oxidized_cut_copper", Map.of("into", "minecraft:oxidized_cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_exposed_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_weathered_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_oxidized_cut_copper_slab", Map.of("into", "minecraft:oxidized_cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_exposed_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_weathered_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap())), -+ Map.entry("minecraft:waxed_oxidized_cut_copper_stairs", Map.of("into", "minecraft:oxidized_cut_copper_stairs", "drops", new HashMap()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables`: " + blockId); return; } -+ if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + "`"); return; } -+ String intoId = (String) map.get("into"); -+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId)); -+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables." + blockId + ".into`: " + intoId); return; } -+ Object dropsObj = map.get("drops"); -+ if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + ".drops`"); return; } -+ Map drops = new HashMap<>(); -+ dropsMap.forEach((itemId, chance) -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString())); -+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.waxables." + blockId + ".drops`: " + itemId); return; } -+ drops.put(item, (double) chance); -+ }); -+ axeWaxables.put(block, new Waxable(into, drops)); -+ }); -+ getMap("tools.axe.weatherables", Map.ofEntries( -+ Map.entry("minecraft:exposed_copper", Map.of("into", "minecraft:copper_block", "drops", new HashMap())), -+ Map.entry("minecraft:weathered_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap())), -+ Map.entry("minecraft:oxidized_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap())), -+ Map.entry("minecraft:exposed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:weathered_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:oxidized_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap())), -+ Map.entry("minecraft:exposed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:weathered_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:oxidized_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap())), -+ Map.entry("minecraft:exposed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap())), -+ Map.entry("minecraft:weathered_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap())), -+ Map.entry("minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables`: " + blockId); return; } -+ if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + "`"); return; } -+ String intoId = (String) map.get("into"); -+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId)); -+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables." + blockId + ".into`: " + intoId); return; } -+ Object dropsObj = map.get("drops"); -+ if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + ".drops`"); return; } -+ Map drops = new HashMap<>(); -+ dropsMap.forEach((itemId, chance) -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString())); -+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.weatherables." + blockId + ".drops`: " + itemId); return; } -+ drops.put(item, (double) chance); -+ }); -+ axeWeatherables.put(block, new Weatherable(into, drops)); -+ }); -+ getMap("tools.hoe.tillables", Map.ofEntries( -+ Map.entry("minecraft:grass_block", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), -+ Map.entry("minecraft:dirt_path", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), -+ Map.entry("minecraft:dirt", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), -+ Map.entry("minecraft:coarse_dirt", Map.of("condition", "air_above", "into", "minecraft:dirt", "drops", new HashMap())), -+ Map.entry("minecraft:rooted_dirt", Map.of("condition", "always", "into", "minecraft:dirt", "drops", Map.of("minecraft:hanging_roots", 1.0D)))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables`: " + blockId); return; } -+ if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + "`"); return; } -+ String conditionId = (String) map.get("condition"); -+ Tillable.Condition condition = Tillable.Condition.get(conditionId); -+ if (condition == null) { PurpurConfig.log(Level.SEVERE, "Invalid condition for `tools.hoe.tillables." + blockId + ".condition`: " + conditionId); return; } -+ String intoId = (String) map.get("into"); -+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId)); -+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables." + blockId + ".into`: " + intoId); return; } -+ Object dropsObj = map.get("drops"); -+ if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + ".drops`"); return; } -+ Map drops = new HashMap<>(); -+ dropsMap.forEach((itemId, chance) -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString())); -+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.hoe.tillables." + blockId + ".drops`: " + itemId); return; } -+ drops.put(item, (double) chance); -+ }); -+ hoeTillables.put(block, new Tillable(condition, into, drops)); -+ }); -+ getMap("tools.shovel.flattenables", Map.ofEntries( -+ Map.entry("minecraft:grass_block", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), -+ Map.entry("minecraft:dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), -+ Map.entry("minecraft:podzol", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), -+ Map.entry("minecraft:coarse_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), -+ Map.entry("minecraft:mycelium", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), -+ Map.entry("minecraft:rooted_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables`: " + blockId); return; } -+ if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + "`"); return; } -+ String intoId = (String) map.get("into"); -+ Block into = BuiltInRegistries.BLOCK.get(new ResourceLocation(intoId)); -+ if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables." + blockId + ".into`: " + intoId); return; } -+ Object dropsObj = map.get("drops"); -+ if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + ".drops`"); return; } -+ Map drops = new HashMap<>(); -+ dropsMap.forEach((itemId, chance) -> { -+ Item item = BuiltInRegistries.ITEM.get(new ResourceLocation(itemId.toString())); -+ if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.shovel.flattenables." + blockId + ".drops`: " + itemId); return; } -+ drops.put(item, (double) chance); -+ }); -+ shovelFlattenables.put(block, new Flattenable(into, drops)); -+ }); -+ } -+ - public boolean anvilAllowColors = false; - public boolean anvilColorsUseMiniMessage; - private void anvilSettings() { -diff --git a/src/main/java/org/purpurmc/purpur/tool/Actionable.java b/src/main/java/org/purpurmc/purpur/tool/Actionable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e18c37f06730da9d3055d5215e813b1477c1e70e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Actionable.java -@@ -0,0 +1,24 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.Map; -+ -+public abstract class Actionable { -+ private final Block into; -+ private final Map drops; -+ -+ public Actionable(Block into, Map drops) { -+ this.into = into; -+ this.drops = drops; -+ } -+ -+ public Block into() { -+ return into; -+ } -+ -+ public Map drops() { -+ return drops; -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/tool/Flattenable.java b/src/main/java/org/purpurmc/purpur/tool/Flattenable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..345d4ee4ff0b78bd1050959711a4f5d16a5e8aee ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Flattenable.java -@@ -0,0 +1,12 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.Map; -+ -+public class Flattenable extends Actionable { -+ public Flattenable(Block into, Map drops) { -+ super(into, drops); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/tool/Strippable.java b/src/main/java/org/purpurmc/purpur/tool/Strippable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bf5402214f41af9c09bd6c5c4f45d330516d742e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Strippable.java -@@ -0,0 +1,12 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.Map; -+ -+public class Strippable extends Actionable { -+ public Strippable(Block into, Map drops) { -+ super(into, drops); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/tool/Tillable.java b/src/main/java/org/purpurmc/purpur/tool/Tillable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..715f6dd44480347eebced43c11bc364e05727498 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Tillable.java -@@ -0,0 +1,50 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.HoeItem; -+import net.minecraft.world.item.Item; -+import net.minecraft.world.item.context.UseOnContext; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.HashMap; -+import java.util.Map; -+import java.util.function.Predicate; -+ -+public class Tillable extends Actionable { -+ private final Condition condition; -+ -+ public Tillable(Condition condition, Block into, Map drops) { -+ super(into, drops); -+ this.condition = condition; -+ } -+ -+ public Condition condition() { -+ return condition; -+ } -+ -+ public enum Condition { -+ AIR_ABOVE(HoeItem::onlyIfAirAbove), -+ ALWAYS((useOnContext) -> true); -+ -+ private final Predicate predicate; -+ -+ Condition(Predicate predicate) { -+ this.predicate = predicate; -+ } -+ -+ public Predicate predicate() { -+ return predicate; -+ } -+ -+ private static final Map BY_NAME = new HashMap<>(); -+ -+ static { -+ for (Condition condition : values()) { -+ BY_NAME.put(condition.name(), condition); -+ } -+ } -+ -+ public static Condition get(String name) { -+ return BY_NAME.get(name.toUpperCase(java.util.Locale.ROOT)); -+ } -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/tool/Waxable.java b/src/main/java/org/purpurmc/purpur/tool/Waxable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..64adb13b29b6757dcf227a55588da70ecabe083f ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Waxable.java -@@ -0,0 +1,12 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.Map; -+ -+public class Waxable extends Actionable { -+ public Waxable(Block into, Map drops) { -+ super(into, drops); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/tool/Weatherable.java b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b7586f494528f30eb0da82420d3bcf5b83a1a902 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/tool/Weatherable.java -@@ -0,0 +1,12 @@ -+package org.purpurmc.purpur.tool; -+ -+import net.minecraft.world.item.Item; -+import net.minecraft.world.level.block.Block; -+ -+import java.util.Map; -+ -+public class Weatherable extends Actionable { -+ public Weatherable(Block into, Map drops) { -+ super(into, drops); -+ } -+} diff --git a/patches/server/0191-Store-placer-on-Block-when-placed.patch b/patches/server/0191-Store-placer-on-Block-when-placed.patch deleted file mode 100644 index 769ca9476..000000000 --- a/patches/server/0191-Store-placer-on-Block-when-placed.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 3 Jul 2021 18:40:32 -0500 -Subject: [PATCH] Store placer on Block when placed - - -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index a7909c60a17ab004ab8f6cd39abb211887bdeb90..8c8f605c4d17e2dd5dec450bd4f9853421b0f44a 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -426,6 +426,7 @@ public final class ItemStack { - world.preventPoiUpdated = true; // CraftBukkit - SPIGOT-5710 - for (BlockState blockstate : blocks) { - blockstate.update(true, false); -+ ((CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur - } - world.preventPoiUpdated = false; - -@@ -455,6 +456,7 @@ public final class ItemStack { - if (!(block.getBlock() instanceof BaseEntityBlock)) { // Containers get placed automatically - block.getBlock().onPlace(block, world, newblockposition, oldBlock, true, context); // Paper - pass context - } -+ block.getBlock().forgetPlacer(); // Purpur - - world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getBlockState(newblockposition), updateFlag, 512); // send null chunk as chunk.k() returns false by this point - } -diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index fdd9c61b7248e92dbcbec91cd6fe4c6310bba237..48682f0ebec49a04e36f96423cda483a6736fd24 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -430,7 +430,17 @@ public class Block extends BlockBehaviour implements ItemLike { - } // Paper - } - -- public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {} -+ // Purpur start -+ @Nullable protected LivingEntity placer = null; -+ -+ public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) { -+ this.placer = placer; -+ } -+ -+ public void forgetPlacer() { -+ this.placer = null; -+ } -+ // Purpur end - - public boolean isPossibleToRespawnInThis(BlockState state) { - return !state.isSolid() && !state.liquid(); diff --git a/patches/server/0192-Summoner-API.patch b/patches/server/0192-Summoner-API.patch deleted file mode 100644 index 702be15e7..000000000 --- a/patches/server/0192-Summoner-API.patch +++ /dev/null @@ -1,255 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 3 Jul 2021 18:40:58 -0500 -Subject: [PATCH] Summoner API - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 1ffb8e1c386fc85796432281ac407a935169b186..c2817324ff57860ea2628dc4ac8e78b76cf67a5a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -60,6 +60,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - private int remainingPersistentAngerTime; - @Nullable - private UUID persistentAngerTarget; -+ @Nullable private UUID summoner; // Purpur - - public IronGolem(EntityType type, Level world) { - super(type, world); -@@ -93,6 +94,15 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - return this.level().purpurConfig.ironGolemTakeDamageFromWater; - } - -+ @Nullable -+ public UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable UUID summoner) { -+ this.summoner = summoner; -+ } -+ - @Override - protected void registerGoals() { - if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur -@@ -170,6 +180,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putBoolean("PlayerCreated", this.isPlayerCreated()); -+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur - this.addPersistentAngerSaveData(nbt); - } - -@@ -177,6 +188,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - public void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); - this.setPlayerCreated(nbt.getBoolean("PlayerCreated")); -+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur - this.readPersistentAngerSaveData(this.level(), nbt); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index a1efde792eaa3f80cd0c7c48e38bbd07e1eb5c9d..20f2fd0edf290d448d06e4772b5947fc7865c20b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -49,6 +49,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - private static final EntityDataAccessor DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE); - private static final byte PUMPKIN_FLAG = 16; - private static final float EYE_HEIGHT = 1.7F; -+ @Nullable private java.util.UUID summoner; // Purpur - - public SnowGolem(EntityType type, Level world) { - super(type, world); -@@ -76,6 +77,15 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snowGolemMaxHealth); - } - -+ @Nullable -+ public java.util.UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable java.util.UUID summoner) { -+ this.summoner = summoner; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -@@ -103,6 +113,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putBoolean("Pumpkin", this.hasPumpkin()); -+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur - } - - @Override -@@ -111,6 +122,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - if (nbt.contains("Pumpkin")) { - this.setPumpkin(nbt.getBoolean("Pumpkin")); - } -+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur - - } - -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 257cd3ea1830b46d1c51242cd639c4f0e21c8774..86b06182616fe07f1a5ef2b1799c434884cba149 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -84,6 +84,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - return entityliving.getMobType() != MobType.UNDEAD && entityliving.attackable(); - }; - private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR); -+ @Nullable private java.util.UUID summoner; // Purpur - private int shootCooldown = 0; // Purpur - // Paper start - private boolean canPortal = false; -@@ -122,6 +123,15 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - return this.level().purpurConfig.witherTakeDamageFromWater; - } - -+ @Nullable -+ public java.util.UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable java.util.UUID summoner) { -+ this.summoner = summoner; -+ } -+ - @Override - protected PathNavigation createNavigation(Level world) { - FlyingPathNavigation navigationflying = new FlyingPathNavigation(this, world); -@@ -256,6 +266,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putInt("Invul", this.getInvulnerableTicks()); -+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur - } - - @Override -@@ -265,6 +276,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - if (this.hasCustomName()) { - this.bossEvent.setName(this.getDisplayName()); - } -+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur - - } - -diff --git a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java -index 23c487e295b3b736d8800f0c884324c9b18a5373..ebeb7caf7fd4f45714bab0856a48b847a544cce7 100644 ---- a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java -@@ -64,7 +64,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - SnowGolem entitysnowman = (SnowGolem) EntityType.SNOW_GOLEM.create(world); - - if (entitysnowman != null) { -- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos()); -+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection, entitysnowman, shapedetector_shapedetectorcollection.getBlock(0, 2, 0).getPos(), this.placer); // Purpur - } - } else { - BlockPattern.BlockPatternMatch shapedetector_shapedetectorcollection1 = this.getOrCreateIronGolemFull().find(world, pos); -@@ -74,7 +74,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - - if (entityirongolem != null) { - entityirongolem.setPlayerCreated(true); -- CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos()); -+ CarvedPumpkinBlock.spawnGolemInWorld(world, shapedetector_shapedetectorcollection1, entityirongolem, shapedetector_shapedetectorcollection1.getBlock(1, 2, 0).getPos(), this.placer); // Purpur - } - } - } -@@ -82,6 +82,16 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - } - - private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos) { -+ // Purpur start -+ spawnGolemInWorld(world, patternResult, entity, pos, null); -+ } -+ private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos, net.minecraft.world.entity.LivingEntity placer) { -+ if (entity instanceof SnowGolem snowGolem) { -+ snowGolem.setSummoner(placer == null ? null : placer.getUUID()); -+ } else if (entity instanceof IronGolem ironGolem) { -+ ironGolem.setSummoner(placer == null ? null : placer.getUUID()); -+ } -+ // Purpur end - // clearPatternBlocks(world, shapedetector_shapedetectorcollection); // CraftBukkit - moved down - entity.moveTo((double) pos.getX() + 0.5D, (double) pos.getY() + 0.05D, (double) pos.getZ() + 0.5D, 0.0F, 0.0F); - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java -index 1aa0e921890d600c9274deb923da04e72b12bcc6..44bd7bee2665a05878fd2df935a700f02cd13a75 100644 ---- a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java -@@ -69,6 +69,7 @@ public class WitherSkullBlock extends SkullBlock { - entitywither.moveTo((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.55D, (double) blockposition1.getZ() + 0.5D, shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F, 0.0F); - entitywither.yBodyRot = shapedetector_shapedetectorcollection.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F; - entitywither.makeInvulnerable(); -+ entitywither.setSummoner(iblockdata.getBlock().placer == null ? null : iblockdata.getBlock().placer.getUUID()); // Purpur - // CraftBukkit start - if (!world.addFreshEntity(entitywither, SpawnReason.BUILD_WITHER)) { - return; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java -index 75d10b5322eb0a62bce2855c04a5151eb857d7de..208018981a2a5666c455eb34614b03f617354165 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java -@@ -27,4 +27,17 @@ public class CraftIronGolem extends CraftGolem implements IronGolem { - public void setPlayerCreated(boolean playerCreated) { - this.getHandle().setPlayerCreated(playerCreated); - } -+ -+ // Purpur start -+ @Override -+ @org.jetbrains.annotations.Nullable -+ public java.util.UUID getSummoner() { -+ return getHandle().getSummoner(); -+ } -+ -+ @Override -+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { -+ getHandle().setSummoner(summoner); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java -index e4a14cdfeb91a3d32e622d27d612605b1bca08e2..898d934aafd6066df45f02fe3406fa83f79b745c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java -@@ -28,4 +28,17 @@ public class CraftSnowman extends CraftGolem implements Snowman, com.destroystok - public String toString() { - return "CraftSnowman"; - } -+ -+ // Purpur start -+ @Override -+ @org.jetbrains.annotations.Nullable -+ public java.util.UUID getSummoner() { -+ return getHandle().getSummoner(); -+ } -+ -+ @Override -+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { -+ getHandle().setSummoner(summoner); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java -index 5a97c92f9b044d8ab7bd3346ceb464455a09046e..e30d8b80734f04b1fa89e8a3cef666116fd7366c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java -@@ -99,4 +99,17 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok - this.getHandle().makeInvulnerable(); - } - // Paper end -+ -+ // Purpur start -+ @Override -+ @org.jetbrains.annotations.Nullable -+ public java.util.UUID getSummoner() { -+ return getHandle().getSummoner(); -+ } -+ -+ @Override -+ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { -+ getHandle().setSummoner(summoner); -+ } -+ // Purpur end - } diff --git a/patches/server/0193-Customizable-sleeping-actionbar-messages.patch b/patches/server/0193-Customizable-sleeping-actionbar-messages.patch deleted file mode 100644 index b0b4fd253..000000000 --- a/patches/server/0193-Customizable-sleeping-actionbar-messages.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 3 Jul 2021 21:52:15 -0500 -Subject: [PATCH] Customizable sleeping actionbar messages - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index fea5481c98e4cbcaecb9f4adef35b7340ad0c9b8..df8efd93ad6ab30974e3025187cd234b37111129 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1185,11 +1185,27 @@ public class ServerLevel extends Level implements WorldGenLevel { - if (this.canSleepThroughNights()) { - if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { - int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); -- MutableComponent ichatmutablecomponent; -+ Component ichatmutablecomponent; - - if (this.sleepStatus.areEnoughSleeping(i)) { -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.isBlank()) { -+ return; -+ } -+ if (!org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.equalsIgnoreCase("default")) { -+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepSkippingNight)); -+ } else - ichatmutablecomponent = Component.translatable("sleep.skipping_night"); - } else { -+ if (org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.isBlank()) { -+ return; -+ } -+ if (!org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.equalsIgnoreCase("default")) { -+ ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent, -+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("count", Integer.toString(this.sleepStatus.amountSleeping())), -+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("total", Integer.toString(this.sleepStatus.sleepersNeeded(i))))); -+ } else -+ // Purpur end - ichatmutablecomponent = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); - } - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index b760d9352d85cd5494009a2388a425b85f8daec4..3edbc1da87608090aa3e764736716cebbfe0c3dd 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1471,7 +1471,19 @@ public class ServerPlayer extends Player { - }); - - if (!this.serverLevel().canSleepThroughNights()) { -- this.displayClientMessage(Component.translatable("sleep.not_possible"), true); -+ // Purpur start -+ Component clientMessage; -+ if (org.purpurmc.purpur.PurpurConfig.sleepNotPossible.isBlank()) { -+ clientMessage = null; -+ } else if (!org.purpurmc.purpur.PurpurConfig.sleepNotPossible.equalsIgnoreCase("default")) { -+ clientMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepNotPossible)); -+ } else { -+ clientMessage = Component.translatable("sleep.not_possible"); -+ } -+ if (clientMessage != null) { -+ this.displayClientMessage(clientMessage, true); -+ } -+ // Purpur end - } - - ((ServerLevel) this.level()).updateSleepingPlayerList(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index c33c31956de911ff03f4782ab811a84ba18241c9..9a9c4826dab3de9e63ba984f8b2e1b3b7aa5a98b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -186,6 +186,9 @@ public class PurpurConfig { - public static String dontRunWithScissors = "Don't run with scissors!"; - public static String uptimeCommandOutput = "Server uptime is "; - public static String unverifiedUsername = "default"; -+ public static String sleepSkippingNight = "default"; -+ public static String sleepingPlayersPercent = "default"; -+ public static String sleepNotPossible = "default"; - private static void messages() { - cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); - afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); -@@ -200,6 +203,9 @@ public class PurpurConfig { - dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); - uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput); - unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername); -+ sleepSkippingNight = getString("settings.messages.sleep-skipping-night", sleepSkippingNight); -+ sleepingPlayersPercent = getString("settings.messages.sleeping-players-percent", sleepingPlayersPercent); -+ sleepNotPossible = getString("settings.messages.sleep-not-possible", sleepNotPossible); - } - - public static String deathMsgRunWithScissors = " slipped and fell on their shears"; diff --git a/patches/server/0194-option-to-disable-shulker-box-items-from-dropping-co.patch b/patches/server/0194-option-to-disable-shulker-box-items-from-dropping-co.patch deleted file mode 100644 index adfe424ad..000000000 --- a/patches/server/0194-option-to-disable-shulker-box-items-from-dropping-co.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 5 Jul 2021 20:23:08 -0500 -Subject: [PATCH] option to disable shulker box items from dropping contents - when destroyed - - -diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java -index 65c69432da4bc042cd975e01fcf62b09843cf202..dc45899a677cef389fca23938e201daeaafaa4a3 100644 ---- a/src/main/java/net/minecraft/world/item/BlockItem.java -+++ b/src/main/java/net/minecraft/world/item/BlockItem.java -@@ -288,7 +288,7 @@ public class BlockItem extends Item { - - @Override - public void onDestroyed(ItemEntity entity) { -- if (this.block instanceof ShulkerBoxBlock) { -+ if (this.block instanceof ShulkerBoxBlock && entity.level().purpurConfig.shulkerBoxItemDropContentsWhenDestroyed) { - ItemStack itemstack = entity.getItem(); - CompoundTag nbttagcompound = BlockItem.getBlockEntityData(itemstack); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 773bfaa4a05e86c700089e08a1bdfe637ef98870..2a51d9aeaf34f3160d55d500566574b139ce975a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -221,6 +221,7 @@ public class PurpurWorldConfig { - public int enderPearlCooldownCreative = 20; - public float enderPearlEndermiteChance = 0.05F; - public int glowBerriesEatGlowDuration = 0; -+ public boolean shulkerBoxItemDropContentsWhenDestroyed = true; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -267,6 +268,7 @@ public class PurpurWorldConfig { - enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative); - enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance); - glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration); -+ shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0195-Big-dripleaf-tilt-delay.patch b/patches/server/0195-Big-dripleaf-tilt-delay.patch deleted file mode 100644 index baf4698d6..000000000 --- a/patches/server/0195-Big-dripleaf-tilt-delay.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 16 Jul 2021 22:47:29 -0500 -Subject: [PATCH] Big dripleaf tilt delay - -Makes the tilt delays configurable. There are only 3 types of tilts used by this setting. When an entity steps on a -big_dripleaf with no tilt it will immediately change to an UNSTABLE tilt. Each change after that is on a tick timer: - -UNSTABLE: big_dripleaf with UNSTABLE tilt will change to PARTIAL tilt after 10 ticks -PARTIAL: big_dripleaf with PARTIAL tilt will change to FULL tilt after 10 ticks -UNSTABLE: big_dripleaf with FULL tilt will change back to no tilt after 100 ticks - -diff --git a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java -index 5921f7ebb56e1d5d3004ae327271873093cff357..c7bb655983d2ee2c6461d23d6fa921def1a9a74c 100644 ---- a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java -@@ -236,7 +236,7 @@ public class BigDripleafBlock extends HorizontalDirectionalBlock implements Bone - BigDripleafBlock.playTiltSound(world, blockposition, soundeffect); - } - -- int i = BigDripleafBlock.DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt); -+ int i = world.purpurConfig.bigDripleafTiltDelay.getOrDefault(tilt, -1); // Purpur - - if (i != -1) { - world.scheduleTick(blockposition, (Block) this, i); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2a51d9aeaf34f3160d55d500566574b139ce975a..cc62c282b33a3cfa1a5328f50702e2cbbeaf1978 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -752,6 +752,22 @@ public class PurpurWorldConfig { - } - } - -+ public Map bigDripleafTiltDelay = new HashMap<>(); -+ private void bigDripleafSettings() { -+ bigDripleafTiltDelay.clear(); -+ getMap("blocks.big_dripleaf.tilt-delay", Map.ofEntries( -+ Map.entry("UNSTABLE", 10), -+ Map.entry("PARTIAL", 10), -+ Map.entry("FULL", 100)) -+ ).forEach((tilt, delay) -> { -+ try { -+ bigDripleafTiltDelay.put(Tilt.valueOf(tilt), (int) delay); -+ } catch (IllegalArgumentException e) { -+ PurpurConfig.log(Level.SEVERE, "Invalid big_dripleaf tilt key: " + tilt); -+ } -+ }); -+ } -+ - public boolean chestOpenWithBlockOnTop = false; - private void chestSettings() { - chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); diff --git a/patches/server/0196-Player-ridable-in-water-option.patch b/patches/server/0196-Player-ridable-in-water-option.patch deleted file mode 100644 index dd19ce97e..000000000 --- a/patches/server/0196-Player-ridable-in-water-option.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 17 Jul 2021 15:55:14 -0500 -Subject: [PATCH] Player ridable in water option - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 528aeec3f322ee2094098093e6016e41ff382cf3..0a9d8b22c7127b87826d9d256a5f36c34f2bfc0f 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -2104,6 +2104,11 @@ public abstract class Player extends LivingEntity { - return this.inventory.armor; - } - -+ @Override -+ public boolean dismountsUnderwater() { -+ return !level().purpurConfig.playerRidableInWater; -+ } -+ - public boolean setEntityOnShoulder(CompoundTag entityNbt) { - if (!this.isPassenger() && this.onGround() && !this.isInWater() && !this.isInPowderSnow) { - if (this.getShoulderEntityLeft().isEmpty()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index cc62c282b33a3cfa1a5328f50702e2cbbeaf1978..633b9f3c4b1477c84241446ae7994a48c59342cf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -398,6 +398,7 @@ public class PurpurWorldConfig { - public boolean playerBurpWhenFull = false; - public int playerPortalWaitTime = 80; - public int playerCreativePortalWaitTime = 1; -+ public boolean playerRidableInWater = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -424,6 +425,7 @@ public class PurpurWorldConfig { - playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull); - playerPortalWaitTime = getInt("gameplay-mechanics.player.portal-wait-time", playerPortalWaitTime); - playerCreativePortalWaitTime = getInt("gameplay-mechanics.player.creative-portal-wait-time", playerCreativePortalWaitTime); -+ playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0197-Config-to-disable-Enderman-teleport-on-projectile-hi.patch b/patches/server/0197-Config-to-disable-Enderman-teleport-on-projectile-hi.patch deleted file mode 100644 index a9df5d984..000000000 --- a/patches/server/0197-Config-to-disable-Enderman-teleport-on-projectile-hi.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Mon, 19 Jul 2021 19:28:17 -0400 -Subject: [PATCH] Config to disable Enderman teleport on projectile hit - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index f4bf81ebcc792c8e04fa700e84b13b3b688dbdac..50e1e1b7b9340689a2310e7100c3fe33e5ad0940 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -448,6 +448,7 @@ public class EnderMan extends Monster implements NeutralMob { - } else { - flag1 = flag && this.hurtWithCleanWater(source, (ThrownPotion) source.getDirectEntity(), amount); - -+ if (!flag1 && this.level().purpurConfig.endermanIgnoreProjectiles) return super.hurt(source, amount); // Purpur - if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper start - for (int i = 0; i < 64; ++i) { - if (this.teleport()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 633b9f3c4b1477c84241446ae7994a48c59342cf..7e2e60fd2fbe7181bfbd1aeacdb0b4b295c0d9c7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1373,6 +1373,7 @@ public class PurpurWorldConfig { - public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false; - public boolean endermanIgnorePlayerDragonHead = false; - public boolean endermanDisableStareAggro = false; -+ public boolean endermanIgnoreProjectiles = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -1395,6 +1396,7 @@ public class PurpurWorldConfig { - endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned); - endermanIgnorePlayerDragonHead = getBoolean("mobs.enderman.ignore-players-wearing-dragon-head", endermanIgnorePlayerDragonHead); - endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro); -+ endermanIgnoreProjectiles = getBoolean("mobs.enderman.ignore-projectiles", endermanIgnoreProjectiles); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0198-Add-compass-command.patch b/patches/server/0198-Add-compass-command.patch deleted file mode 100644 index 1bb3742d8..000000000 --- a/patches/server/0198-Add-compass-command.patch +++ /dev/null @@ -1,248 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 24 Jul 2021 00:07:31 -0500 -Subject: [PATCH] Add compass command - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 7aae9e3c60ba15b8dcd8174a4d70866edebb6cca..5f6cc8b16af6dce3b74f0c2c662b0ecf84ae8d36 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -229,6 +229,7 @@ public class Commands { - org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur -+ org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur - } - - if (environment.includeIntegrated) { -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 3edbc1da87608090aa3e764736716cebbfe0c3dd..cc313235863910b4978f96d594abff0a20c13e18 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -281,6 +281,7 @@ public class ServerPlayer extends Player { - public boolean purpurClient = false; // Purpur - public boolean acceptingResourcePack = false; // Purpur - private boolean tpsBar = false; // Purpur -+ private boolean compassBar = false; // Purpur - - private final java.util.concurrent.atomic.AtomicReference viewDistances = new java.util.concurrent.atomic.AtomicReference<>(new io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.ViewDistances(-1, -1, -1)); - public io.papermc.paper.chunk.system.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -562,6 +563,7 @@ public class ServerPlayer extends Player { - } - - if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur -+ if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur - } - - @Override -@@ -629,6 +631,7 @@ public class ServerPlayer extends Player { - this.getBukkitEntity().setExtraData(nbt); // CraftBukkit - - nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur -+ nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - } - - // CraftBukkit start - World fallback code, either respawn location or global spawn -@@ -2819,5 +2822,13 @@ public class ServerPlayer extends Player { - public void tpsBar(boolean tpsBar) { - this.tpsBar = tpsBar; - } -+ -+ public boolean compassBar() { -+ return this.compassBar; -+ } -+ -+ public void compassBar(boolean compassBar) { -+ this.compassBar = compassBar; -+ } - // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 9a9c4826dab3de9e63ba984f8b2e1b3b7aa5a98b..62fa79eccbb6f679c0460a6dfd457c405b3a8e67 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -255,6 +255,11 @@ public class PurpurConfig { - public static String commandTPSBarTextColorMedium = ""; - public static String commandTPSBarTextColorLow = ""; - public static int commandTPSBarTickInterval = 20; -+ public static String commandCompassBarTitle = "S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 "; -+ public static BossBar.Overlay commandCompassBarProgressOverlay = BossBar.Overlay.PROGRESS; -+ public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE; -+ public static float commandCompassBarProgressPercent = 1.0F; -+ public static int commandCompassBarTickInterval = 5; - public static boolean commandGamemodeRequiresPermission = false; - public static boolean hideHiddenPlayersFromEntitySelector = false; - public static String uptimeFormat = ""; -@@ -277,6 +282,13 @@ public class PurpurConfig { - commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium); - commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); - commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); -+ -+ commandCompassBarTitle = getString("settings.command.compass.title", commandCompassBarTitle); -+ commandCompassBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.compass.overlay", commandCompassBarProgressOverlay.name())); -+ commandCompassBarProgressColor = BossBar.Color.valueOf(getString("settings.command.compass.progress-color", commandCompassBarProgressColor.name())); -+ commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent); -+ commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval); -+ - commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission); - hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector); - uptimeFormat = getString("settings.command.uptime.format", uptimeFormat); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7e2e60fd2fbe7181bfbd1aeacdb0b4b295c0d9c7..29b070ecd5d6d08f3c6127e499ed90e53112624d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -222,6 +222,7 @@ public class PurpurWorldConfig { - public float enderPearlEndermiteChance = 0.05F; - public int glowBerriesEatGlowDuration = 0; - public boolean shulkerBoxItemDropContentsWhenDestroyed = true; -+ public boolean compassItemShowsBossBar = false; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -269,6 +270,7 @@ public class PurpurWorldConfig { - enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance); - glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration); - shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed); -+ compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar); - } - - public double minecartMaxSpeed = 0.4D; -diff --git a/src/main/java/org/purpurmc/purpur/command/CompassCommand.java b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..79b8490832d2a0cc7846ddcb091cb6bcac74ea45 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/CompassCommand.java -@@ -0,0 +1,27 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.task.CompassTask; -+ -+public class CompassCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("compass") -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.compass")) -+ .executes(context -> { -+ ServerPlayer player = context.getSource().getPlayerOrException(); -+ CompassTask task = CompassTask.instance(); -+ if (player.compassBar()) { -+ task.removePlayer(player.getBukkitEntity()); -+ player.compassBar(false); -+ } else { -+ task.addPlayer(player.getBukkitEntity()); -+ player.compassBar(true); -+ } -+ return 1; -+ }) -+ ); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -index d38b3c4a722396cc3b61a9a8ed7e39cea4ae65cb..d333334f323049ca97e756324cff0b23eddacd2a 100644 ---- a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -+++ b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -@@ -90,10 +90,12 @@ public abstract class BossBarTask extends BukkitRunnable { - - public static void startAll() { - TPSBarTask.instance().start(); -+ CompassTask.instance().start(); - } - - public static void stopAll() { - TPSBarTask.instance().stop(); -+ CompassTask.instance().stop(); - } - - public static void addToAll(ServerPlayer player) { -@@ -101,9 +103,13 @@ public abstract class BossBarTask extends BukkitRunnable { - if (player.tpsBar()) { - TPSBarTask.instance().addPlayer(bukkit); - } -+ if (player.compassBar()) { -+ CompassTask.instance().addPlayer(bukkit); -+ } - } - - public static void removeFromAll(Player player) { - TPSBarTask.instance().removePlayer(player); -+ CompassTask.instance().removePlayer(player); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/task/CompassTask.java b/src/main/java/org/purpurmc/purpur/task/CompassTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bece7eefc8ba8822b433835526251d2fb916c025 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/task/CompassTask.java -@@ -0,0 +1,68 @@ -+package org.purpurmc.purpur.task; -+ -+import net.kyori.adventure.bossbar.BossBar; -+import net.kyori.adventure.text.Component; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.item.Items; -+import org.bukkit.entity.Player; -+import org.purpurmc.purpur.PurpurConfig; -+ -+public class CompassTask extends BossBarTask { -+ private static CompassTask instance; -+ -+ private int tick = 0; -+ -+ public static CompassTask instance() { -+ if (instance == null) { -+ instance = new CompassTask(); -+ } -+ return instance; -+ } -+ -+ @Override -+ public void run() { -+ if (++tick < PurpurConfig.commandCompassBarTickInterval) { -+ return; -+ } -+ tick = 0; -+ -+ MinecraftServer.getServer().getAllLevels().forEach((level) -> { -+ if (level.purpurConfig.compassItemShowsBossBar) { -+ level.players().forEach(player -> { -+ if (!player.compassBar()) { -+ if (player.getMainHandItem().getItem() != Items.COMPASS && player.getOffhandItem().getItem() != Items.COMPASS) { -+ removePlayer(player.getBukkitEntity()); -+ } else if (!hasPlayer(player.getUUID())) { -+ addPlayer(player.getBukkitEntity()); -+ } -+ } -+ }); -+ } -+ }); -+ -+ super.run(); -+ } -+ -+ @Override -+ BossBar createBossBar() { -+ return BossBar.bossBar(Component.text(""), PurpurConfig.commandCompassBarProgressPercent, PurpurConfig.commandCompassBarProgressColor, PurpurConfig.commandCompassBarProgressOverlay); -+ } -+ -+ @Override -+ void updateBossBar(BossBar bossbar, Player player) { -+ float yaw = player.getLocation().getYaw(); -+ int length = PurpurConfig.commandCompassBarTitle.length(); -+ int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F)); -+ bossbar.name(Component.text(PurpurConfig.commandCompassBarTitle.substring(pos - 25, pos + 25))); -+ } -+ -+ private float normalize(float yaw) { -+ while (yaw < -180.0F) { -+ yaw += 360.0F; -+ } -+ while (yaw > 180.0F) { -+ yaw -= 360.0F; -+ } -+ return yaw; -+ } -+} diff --git a/patches/server/0199-Toggle-for-kinetic-damage.patch b/patches/server/0199-Toggle-for-kinetic-damage.patch deleted file mode 100644 index 1f01a16bf..000000000 --- a/patches/server/0199-Toggle-for-kinetic-damage.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Wed, 4 Aug 2021 11:44:26 +0200 -Subject: [PATCH] Toggle for kinetic damage - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 972c0e43e3f099706b9ee744551d43d31ffb3fa7..21d6851cb903f214db92109a6c16bb7367e40e1c 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2890,6 +2890,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - if (f3 > 0.0F) { - this.playSound(this.getFallDamageSound((int) f3), 1.0F, 1.0F); -+ if (level().purpurConfig.elytraKineticDamage) // Purpur - this.hurt(this.damageSources().flyIntoWall(), f3); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 29b070ecd5d6d08f3c6127e499ed90e53112624d..08ad56494ce3f99b9f3a2b61d3033aec94e9db7e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -182,12 +182,14 @@ public class PurpurWorldConfig { - public boolean elytraIgnoreUnbreaking = false; - public int elytraDamagePerFireworkBoost = 0; - public int elytraDamagePerTridentBoost = 0; -+ public boolean elytraKineticDamage = true; - private void elytraSettings() { - elytraDamagePerSecond = getInt("gameplay-mechanics.elytra.damage-per-second", elytraDamagePerSecond); - elytraDamageMultiplyBySpeed = getDouble("gameplay-mechanics.elytra.damage-multiplied-by-speed", elytraDamageMultiplyBySpeed); - elytraIgnoreUnbreaking = getBoolean("gameplay-mechanics.elytra.ignore-unbreaking", elytraIgnoreUnbreaking); - elytraDamagePerFireworkBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.firework", elytraDamagePerFireworkBoost); - elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost); -+ elytraKineticDamage = getBoolean("gameplay-mechanics.elytra.kinetic-damage", elytraKineticDamage); - } - - public int entityLifeSpan = 0; diff --git a/patches/server/0200-Add-Option-for-disable-observer-clocks.patch b/patches/server/0200-Add-Option-for-disable-observer-clocks.patch deleted file mode 100644 index b2aceb5a0..000000000 --- a/patches/server/0200-Add-Option-for-disable-observer-clocks.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 5 Jul 2021 06:00:17 +0200 -Subject: [PATCH] Add Option for disable observer clocks - -Allow to disable observer clocks: https://www.spigotmc.org/attachments/observerclock-gif.365936/ - -diff --git a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java -index 7b45d6b9a005036ca5051d089a7be792eb87012f..8806c97ecc6bdd8a64c2d82bb2f58f46ac37c468 100644 ---- a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java -@@ -64,6 +64,7 @@ public class ObserverBlock extends DirectionalBlock { - @Override - public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { - if (state.getValue(ObserverBlock.FACING) == direction && !(Boolean) state.getValue(ObserverBlock.POWERED)) { -+ if (!world.getMinecraftWorld().purpurConfig.disableObserverClocks || !(neighborState.getBlock() instanceof ObserverBlock) || neighborState.getValue(ObserverBlock.FACING).getOpposite() != direction) // Purpur - this.startSignal(world, pos); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 08ad56494ce3f99b9f3a2b61d3033aec94e9db7e..d13188a1207352a6ee5ff9c9ed633e7f834a81c9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -369,6 +369,11 @@ public class PurpurWorldConfig { - villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate); - } - -+ public boolean disableObserverClocks = false; -+ private void observerSettings() { -+ disableObserverClocks = getBoolean("blocks.observer.disable-clock", disableObserverClocks); -+ } -+ - public int playerNetheriteFireResistanceDuration = 0; - public int playerNetheriteFireResistanceAmplifier = 0; - public boolean playerNetheriteFireResistanceAmbient = false; diff --git a/patches/server/0201-Customizeable-Zombie-Villager-curing-times.patch b/patches/server/0201-Customizeable-Zombie-Villager-curing-times.patch deleted file mode 100644 index 918a5737f..000000000 --- a/patches/server/0201-Customizeable-Zombie-Villager-curing-times.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Fri, 6 Aug 2021 22:30:10 +0200 -Subject: [PATCH] Customizeable Zombie Villager curing times - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index c3f220a85b91a25662c943b5ee4508cd7a18c75d..40b3498c57c62f8bdaac50546ca47f4927db7bfa 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -218,7 +218,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - } - - if (!this.level().isClientSide) { -- this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600); -+ this.startConverting(player.getUUID(), this.random.nextInt(level().purpurConfig.zombieVillagerCuringTimeMax - level().purpurConfig.zombieVillagerCuringTimeMin + 1) + level().purpurConfig.zombieVillagerCuringTimeMin); // Purpur - } - - return InteractionResult.SUCCESS; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d13188a1207352a6ee5ff9c9ed633e7f834a81c9..b3a794d7181aecb8f734b98182f619ee82bb3dbf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2774,6 +2774,8 @@ public class PurpurWorldConfig { - public double zombieVillagerJockeyChance = 0.05D; - public boolean zombieVillagerJockeyTryExistingChickens = true; - public boolean zombieVillagerTakeDamageFromWater = false; -+ public int zombieVillagerCuringTimeMin = 3600; -+ public int zombieVillagerCuringTimeMax = 6000; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -2789,6 +2791,8 @@ public class PurpurWorldConfig { - zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance); - zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens); - zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater); -+ zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin); -+ zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax); - } - - public boolean zombifiedPiglinRidable = false; diff --git a/patches/server/0202-Option-for-sponges-to-work-on-lava-and-mud.patch b/patches/server/0202-Option-for-sponges-to-work-on-lava-and-mud.patch deleted file mode 100644 index 4be3824b0..000000000 --- a/patches/server/0202-Option-for-sponges-to-work-on-lava-and-mud.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Sat, 7 Aug 2021 20:23:31 +0200 -Subject: [PATCH] Option for sponges to work on lava and mud - -Co-authored by: granny - -diff --git a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -index 709dd0af07f2439d7c7e8b5cd0677580dc3f6278..2276fed1feb4fea59b5bd49b5e4586d49478b3cc 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -@@ -67,7 +67,7 @@ public class SpongeBlock extends Block { - FluidState fluid = blockList.getFluidState(blockposition1); - // CraftBukkit end - -- if (!fluid.is(FluidTags.WATER)) { -+ if (!fluid.is(FluidTags.WATER) && (!world.purpurConfig.spongeAbsorbsLava || !fluid.is(FluidTags.LAVA)) && (!world.purpurConfig.spongeAbsorbsWaterFromMud || !iblockdata.is(Blocks.MUD))) { // Purpur - return false; - } else { - Block block = iblockdata.getBlock(); -@@ -82,6 +82,10 @@ public class SpongeBlock extends Block { - - if (iblockdata.getBlock() instanceof LiquidBlock) { - blockList.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 3); // CraftBukkit -+ // Purpur start -+ } else if (iblockdata.is(Blocks.MUD)) { -+ blockList.setBlock(blockposition1, Blocks.CLAY.defaultBlockState(), 3); -+ // Purpur end - } else { - if (!iblockdata.is(Blocks.KELP) && !iblockdata.is(Blocks.KELP_PLANT) && !iblockdata.is(Blocks.SEAGRASS) && !iblockdata.is(Blocks.TALL_SEAGRASS)) { - return false; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b3a794d7181aecb8f734b98182f619ee82bb3dbf..309f2360d3faa4e7f6932329623b9b9647cd5f98 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -953,9 +953,13 @@ public class PurpurWorldConfig { - - public int spongeAbsorptionArea = 65; - public int spongeAbsorptionRadius = 6; -+ public boolean spongeAbsorbsLava = false; -+ public boolean spongeAbsorbsWaterFromMud = false; - private void spongeSettings() { - spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea); - spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius); -+ spongeAbsorbsLava = getBoolean("blocks.sponge.absorbs-lava", spongeAbsorbsLava); -+ spongeAbsorbsWaterFromMud = getBoolean("blocks.sponge.absorbs-water-from-mud", spongeAbsorbsWaterFromMud); - } - - public boolean turtleEggsBreakFromExpOrbs = true; diff --git a/patches/server/0203-Toggle-for-Wither-s-spawn-sound.patch b/patches/server/0203-Toggle-for-Wither-s-spawn-sound.patch deleted file mode 100644 index c78df8429..000000000 --- a/patches/server/0203-Toggle-for-Wither-s-spawn-sound.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Sat, 7 Aug 2021 21:27:56 +0200 -Subject: [PATCH] Toggle for Wither's spawn sound - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 86b06182616fe07f1a5ef2b1799c434884cba149..9abc47eb3d0c1a0ee0868ec223431f4de4925c77 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -418,7 +418,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - } - // CraftBukkit end - -- if (!this.isSilent()) { -+ if (!this.isSilent() && level().purpurConfig.witherPlaySpawnSound) { - // CraftBukkit start - Use relative location for far away sounds - // this.level().globalLevelEvent(1023, new BlockPosition(this), 0); - int viewDistance = ((ServerLevel) this.level()).getCraftServer().getViewDistance() * 16; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 309f2360d3faa4e7f6932329623b9b9647cd5f98..21c597f6f010bca75115354b8cf5d567d8d0f474 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2621,6 +2621,7 @@ public class PurpurWorldConfig { - public boolean witherTakeDamageFromWater = false; - public boolean witherCanRideVehicles = false; - public float witherExplosionRadius = 1.0F; -+ public boolean witherPlaySpawnSound = true; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -2642,6 +2643,7 @@ public class PurpurWorldConfig { - witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater); - witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles); - witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius); -+ witherPlaySpawnSound = getBoolean("mobs.wither.play-spawn-sound", witherPlaySpawnSound); - } - - public boolean witherSkeletonRidable = false; diff --git a/patches/server/0204-Cactus-breaks-from-solid-neighbors-config.patch b/patches/server/0204-Cactus-breaks-from-solid-neighbors-config.patch deleted file mode 100644 index 1deb983a7..000000000 --- a/patches/server/0204-Cactus-breaks-from-solid-neighbors-config.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 7 Aug 2021 03:37:56 -0500 -Subject: [PATCH] Cactus breaks from solid neighbors config - - -diff --git a/src/main/java/net/minecraft/world/level/block/CactusBlock.java b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -index 0003fb51ae3a6575575e10b4c86719f3061e2577..c2ca3432a47124d02e1aaf8ffb621f9a2c7d7a62 100644 ---- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -@@ -107,7 +107,7 @@ public class CactusBlock extends Block { - - enumdirection = (Direction) iterator.next(); - iblockdata1 = world.getBlockState(pos.relative(enumdirection)); -- } while (!iblockdata1.isSolid() && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA)); -+ } while ((!world.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors || !iblockdata1.isSolid()) && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA)); // Purpur - - return false; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 21c597f6f010bca75115354b8cf5d567d8d0f474..45d77adb73d0df1e856088d3df6cffb6a4623cd7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -779,6 +779,11 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean cactusBreaksFromSolidNeighbors = true; -+ private void cactusSettings() { -+ cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors); -+ } -+ - public boolean chestOpenWithBlockOnTop = false; - private void chestSettings() { - chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); diff --git a/patches/server/0205-Config-to-remove-curse-of-binding-with-weakness.patch b/patches/server/0205-Config-to-remove-curse-of-binding-with-weakness.patch deleted file mode 100644 index e11afcf53..000000000 --- a/patches/server/0205-Config-to-remove-curse-of-binding-with-weakness.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Sun, 8 Aug 2021 16:59:21 -0400 -Subject: [PATCH] Config to remove curse of binding with weakness - - -diff --git a/src/main/java/net/minecraft/world/inventory/InventoryMenu.java b/src/main/java/net/minecraft/world/inventory/InventoryMenu.java -index c549618421c5d077c3d977d8d2064eca2acc438a..5972fa434847d24fa98b7895fd8386d20a636885 100644 ---- a/src/main/java/net/minecraft/world/inventory/InventoryMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/InventoryMenu.java -@@ -4,6 +4,7 @@ import com.mojang.datafixers.util.Pair; - import net.minecraft.network.chat.Component; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.world.Container; -+import net.minecraft.world.effect.MobEffects; - import net.minecraft.world.entity.EquipmentSlot; - import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.player.Inventory; -@@ -95,7 +96,7 @@ public class InventoryMenu extends RecipeBookMenu { - public boolean mayPickup(Player playerEntity) { - ItemStack itemstack = this.getItem(); - -- return !itemstack.isEmpty() && !playerEntity.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? false : super.mayPickup(playerEntity); -+ return !itemstack.isEmpty() && !playerEntity.isCreative() && EnchantmentHelper.hasBindingCurse(itemstack) ? playerEntity.level().purpurConfig.playerRemoveBindingWithWeakness && playerEntity.hasEffect(MobEffects.WEAKNESS) : super.mayPickup(playerEntity); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 45d77adb73d0df1e856088d3df6cffb6a4623cd7..8e5912f9a1e3d4b97306bf31833afc435cd026e8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -408,6 +408,7 @@ public class PurpurWorldConfig { - public int playerPortalWaitTime = 80; - public int playerCreativePortalWaitTime = 1; - public boolean playerRidableInWater = false; -+ public boolean playerRemoveBindingWithWeakness = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -435,6 +436,7 @@ public class PurpurWorldConfig { - playerPortalWaitTime = getInt("gameplay-mechanics.player.portal-wait-time", playerPortalWaitTime); - playerCreativePortalWaitTime = getInt("gameplay-mechanics.player.creative-portal-wait-time", playerCreativePortalWaitTime); - playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); -+ playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0206-Conduit-behavior-configuration.patch b/patches/server/0206-Conduit-behavior-configuration.patch deleted file mode 100644 index 64be92a49..000000000 --- a/patches/server/0206-Conduit-behavior-configuration.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Sun, 8 Aug 2021 18:14:31 -0400 -Subject: [PATCH] Conduit behavior configuration - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java -index 963a596154091b79ca139af6274aa323518ad1ad..4dcac3899a500d8586580bcfd5b4516e1dcdcd4a 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/ConduitBlockEntity.java -@@ -171,7 +171,7 @@ public class ConduitBlockEntity extends BlockEntity { - if ((l > 1 || i1 > 1 || j1 > 1) && (i == 0 && (i1 == 2 || j1 == 2) || j == 0 && (l == 2 || j1 == 2) || k == 0 && (l == 2 || i1 == 2))) { - BlockPos blockposition2 = pos.offset(i, j, k); - BlockState iblockdata = world.getBlockState(blockposition2); -- Block[] ablock = ConduitBlockEntity.VALID_BLOCKS; -+ Block[] ablock = world.purpurConfig.conduitBlocks; // Purpur - int k1 = ablock.length; - - for (int l1 = 0; l1 < k1; ++l1) { -@@ -191,7 +191,7 @@ public class ConduitBlockEntity extends BlockEntity { - - private static void applyEffects(Level world, BlockPos pos, List activatingBlocks) { - int i = activatingBlocks.size(); -- int j = i / 7 * 16; -+ int j = i / 7 * world.purpurConfig.conduitDistance; // Purpur - int k = pos.getX(); - int l = pos.getY(); - int i1 = pos.getZ(); -@@ -222,21 +222,21 @@ public class ConduitBlockEntity extends BlockEntity { - blockEntity.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, pos, blockEntity.destroyTargetUUID); - blockEntity.destroyTargetUUID = null; - } else if (blockEntity.destroyTarget == null) { -- List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving1) -> { -+ List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos, world), (entityliving1) -> { // Purpur - return entityliving1 instanceof Enemy && entityliving1.isInWaterOrRain(); - }); - - if (!list1.isEmpty()) { - blockEntity.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size())); - } -- } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), 8.0D)) { -+ } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), world.purpurConfig.conduitDamageDistance)) { // Purpur - blockEntity.destroyTarget = null; - } - - if (blockEntity.destroyTarget != null) { - // CraftBukkit start - CraftEventFactory.blockDamage = CraftBlock.at(world, pos); -- if (blockEntity.destroyTarget.hurt(world.damageSources().magic(), 4.0F)) { -+ if (blockEntity.destroyTarget.hurt(world.damageSources().magic(), world.purpurConfig.conduitDamageAmount)) { // Purpur - world.playSound((Player) null, blockEntity.destroyTarget.getX(), blockEntity.destroyTarget.getY(), blockEntity.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F); - } - CraftEventFactory.blockDamage = null; -@@ -262,16 +262,22 @@ public class ConduitBlockEntity extends BlockEntity { - } - - private static AABB getDestroyRangeAABB(BlockPos pos) { -+ // Purpur start -+ return getDestroyRangeAABB(pos, null); -+ } -+ -+ private static AABB getDestroyRangeAABB(BlockPos pos, Level level) { -+ // Purpur end - int i = pos.getX(); - int j = pos.getY(); - int k = pos.getZ(); - -- return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(8.0D); -+ return (new AABB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).inflate(level == null ? 8.0D : level.purpurConfig.conduitDamageDistance); // Purpur - } - - @Nullable - private static LivingEntity findDestroyTarget(Level world, BlockPos pos, UUID uuid) { -- List list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving) -> { -+ List list = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos, world), (entityliving) -> { // Purpur - return entityliving.getUUID().equals(uuid); - }); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8e5912f9a1e3d4b97306bf31833afc435cd026e8..97afdc6199fd2b3c2033c8b3a27d2e5059f780cb 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2840,4 +2840,27 @@ public class PurpurWorldConfig { - private void hungerSettings() { - hungerStarvationDamage = (float) getDouble("hunger.starvation-damage", hungerStarvationDamage); - } -+ -+ public int conduitDistance = 16; -+ public double conduitDamageDistance = 8; -+ public float conduitDamageAmount = 4; -+ public Block[] conduitBlocks; -+ private void conduitSettings() { -+ conduitDistance = getInt("blocks.conduit.effect-distance", conduitDistance); -+ conduitDamageDistance = getDouble("blocks.conduit.mob-damage.distance", conduitDamageDistance); -+ conduitDamageAmount = (float) getDouble("blocks.conduit.mob-damage.damage-amount", conduitDamageAmount); -+ List conduitBlockList = new ArrayList<>(); -+ getList("blocks.conduit.valid-ring-blocks", new ArrayList(){{ -+ add("minecraft:prismarine"); -+ add("minecraft:prismarine_bricks"); -+ add("minecraft:sea_lantern"); -+ add("minecraft:dark_prismarine"); -+ }}).forEach(key -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(key.toString())); -+ if (!block.defaultBlockState().isAir()) { -+ conduitBlockList.add(block); -+ } -+ }); -+ conduitBlocks = conduitBlockList.toArray(Block[]::new); -+ } - } diff --git a/patches/server/0207-Cauldron-fill-chances.patch b/patches/server/0207-Cauldron-fill-chances.patch deleted file mode 100644 index 069f93aff..000000000 --- a/patches/server/0207-Cauldron-fill-chances.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Sun, 8 Aug 2021 18:38:44 -0400 -Subject: [PATCH] Cauldron fill chances - - -diff --git a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java -index 2f85b893dd0abc39fcedec65acc89e1567faf6f0..3ee012a9ef8cada0b2203e53b2f731f60f697cb1 100644 ---- a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java -@@ -29,7 +29,7 @@ public class CauldronBlock extends AbstractCauldronBlock { - } - - protected static boolean shouldHandlePrecipitation(Level world, Biome.Precipitation precipitation) { -- return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < 0.05F : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < 0.1F : false); -+ return precipitation == Biome.Precipitation.RAIN ? world.getRandom().nextFloat() < world.purpurConfig.cauldronRainChance : (precipitation == Biome.Precipitation.SNOW ? world.getRandom().nextFloat() < world.purpurConfig.cauldronPowderSnowChance : false); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java -index cd943997f11f5ea5c600fdc6db96043fb0fa713c..4adeda49a2e422e11f885bffb311653d99159bf4 100644 ---- a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java -@@ -186,7 +186,7 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate - - @VisibleForTesting - public static void maybeTransferFluid(BlockState state, ServerLevel world, BlockPos pos, float dripChance) { -- if (dripChance <= 0.17578125F || dripChance <= 0.05859375F) { -+ if (dripChance <= world.purpurConfig.cauldronDripstoneWaterFillChance || dripChance <= world.purpurConfig.cauldronDripstoneLavaFillChance) { // Purpur - if (PointedDripstoneBlock.isStalactiteStartPos(state, world, pos)) { - Optional optional = PointedDripstoneBlock.getFluidAboveStalactite(world, pos, state); - -@@ -195,13 +195,13 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate - float f1; - - if (fluidtype == Fluids.WATER) { -- f1 = 0.17578125F; -+ f1 = world.purpurConfig.cauldronDripstoneWaterFillChance; // Purpur - } else { - if (fluidtype != Fluids.LAVA) { - return; - } - -- f1 = 0.05859375F; -+ f1 = world.purpurConfig.cauldronDripstoneLavaFillChance; // Purpur - } - - if (dripChance < f1) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 97afdc6199fd2b3c2033c8b3a27d2e5059f780cb..389ed035a2fb98675f6779628a620ad0680823f8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2863,4 +2863,15 @@ public class PurpurWorldConfig { - }); - conduitBlocks = conduitBlockList.toArray(Block[]::new); - } -+ -+ public float cauldronRainChance = 0.05F; -+ public float cauldronPowderSnowChance = 0.1F; -+ public float cauldronDripstoneWaterFillChance = 0.17578125F; -+ public float cauldronDripstoneLavaFillChance = 0.05859375F; -+ private void cauldronSettings() { -+ cauldronRainChance = (float) getDouble("blocks.cauldron.fill-chances.rain", cauldronRainChance); -+ cauldronPowderSnowChance = (float) getDouble("blocks.cauldron.fill-chances.powder-snow", cauldronPowderSnowChance); -+ cauldronDripstoneWaterFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-water", cauldronDripstoneWaterFillChance); -+ cauldronDripstoneLavaFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-lava", cauldronDripstoneLavaFillChance); -+ } - } diff --git a/patches/server/0208-Config-to-allow-mobs-to-pathfind-over-rails.patch b/patches/server/0208-Config-to-allow-mobs-to-pathfind-over-rails.patch deleted file mode 100644 index d15dff7e6..000000000 --- a/patches/server/0208-Config-to-allow-mobs-to-pathfind-over-rails.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Sun, 8 Aug 2021 22:50:23 -0400 -Subject: [PATCH] Config to allow mobs to pathfind over rails - - -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -index 3583fcf5284bc5883308876dbd9886664b391e28..d976a6df54c1e817def2d588692abe25a03ee0fa 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -241,7 +241,7 @@ public class WalkNodeEvaluator extends NodeEvaluator { - } - - if (blockPathTypes != BlockPathTypes.WALKABLE && (!this.isAmphibious() || blockPathTypes != BlockPathTypes.WATER)) { -- if ((node == null || node.costMalus < 0.0F) && maxYStep > 0 && (blockPathTypes != BlockPathTypes.FENCE || this.canWalkOverFences()) && blockPathTypes != BlockPathTypes.UNPASSABLE_RAIL && blockPathTypes != BlockPathTypes.TRAPDOOR && blockPathTypes != BlockPathTypes.POWDER_SNOW) { -+ if ((node == null || node.costMalus < 0.0F) && maxYStep > 0 && (blockPathTypes != BlockPathTypes.FENCE || this.canWalkOverFences()) && (this.mob.level().purpurConfig.mobsIgnoreRails || blockPathTypes != BlockPathTypes.UNPASSABLE_RAIL) && blockPathTypes != BlockPathTypes.TRAPDOOR && blockPathTypes != BlockPathTypes.POWDER_SNOW) { // Purpur - node = this.findAcceptedNode(x, y + 1, z, maxYStep - 1, prevFeetY, direction, nodeType); - if (node != null && (node.type == BlockPathTypes.OPEN || node.type == BlockPathTypes.WALKABLE) && this.mob.getBbWidth() < 1.0F) { - double g = (double)(x - direction.getStepX()) + 0.5D; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 389ed035a2fb98675f6779628a620ad0680823f8..14a9bbb6bda6ba245a84cfe7ed80dd591ae41dd7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -138,6 +138,7 @@ public class PurpurWorldConfig { - public double voidDamageDealt = 4.0D; - public int raidCooldownSeconds = 0; - public int animalBreedingCooldownSeconds = 0; -+ public boolean mobsIgnoreRails = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -159,6 +160,7 @@ public class PurpurWorldConfig { - voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt); - raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds); - animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds); -+ mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0209-Shulker-change-color-with-dye.patch b/patches/server/0209-Shulker-change-color-with-dye.patch deleted file mode 100644 index dbdb7316b..000000000 --- a/patches/server/0209-Shulker-change-color-with-dye.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 21 Aug 2021 00:07:39 -0500 -Subject: [PATCH] Shulker change color with dye - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index a0b19ffbdb64c40d8b92ed82104dde323b466190..714adaf017813eb64bb3a9a60b5ff45737e4d95d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -22,6 +22,8 @@ import net.minecraft.tags.DamageTypeTags; - import net.minecraft.util.Mth; - import net.minecraft.world.Difficulty; - import net.minecraft.world.DifficultyInstance; -+import net.minecraft.world.InteractionHand; -+import net.minecraft.world.InteractionResult; - import net.minecraft.world.damagesource.DamageSource; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.EntityDimensions; -@@ -50,6 +52,8 @@ import net.minecraft.world.entity.projectile.AbstractArrow; - import net.minecraft.world.entity.projectile.ShulkerBullet; - import net.minecraft.world.entity.vehicle.Boat; - import net.minecraft.world.item.DyeColor; -+import net.minecraft.world.item.DyeItem; -+import net.minecraft.world.item.ItemStack; - import net.minecraft.world.level.Level; - import net.minecraft.world.level.ServerLevelAccessor; - import net.minecraft.world.level.block.Blocks; -@@ -125,6 +129,19 @@ public class Shulker extends AbstractGolem implements VariantHolder -Date: Mon, 9 Aug 2021 13:22:20 +0200 -Subject: [PATCH] Added the ability to add combustible items - - -diff --git a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java -index 35b41bceeba72c3896c91c2605bac3b0bf9c54e9..5c028f957661089ff502109c996692856b12ee27 100644 ---- a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java -@@ -145,7 +145,13 @@ public abstract class AbstractFurnaceMenu extends RecipeBookMenu { - } else if (slot != 1 && slot != 0) { - if (this.canSmelt(itemstack1)) { - if (!this.moveItemStackTo(itemstack1, 0, 1, false)) { -- return ItemStack.EMPTY; -+ // Purpur start - fix #625 -+ if (this.isFuel(itemstack1)) { -+ if (!this.moveItemStackTo(itemstack1, 1, 2, false)) { -+ return ItemStack.EMPTY; -+ } -+ } -+ // Purpur end - } - } else if (this.isFuel(itemstack1)) { - if (!this.moveItemStackTo(itemstack1, 1, 2, false)) { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -index ca2052804ad829a1528a9c5a0a792275beead113..997d0fab71eacc6466ffe3bc8f6349e5813d6d49 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -@@ -208,6 +208,22 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit - // Paper end - } - -+ // Purpur start -+ public static void addFuel(ItemStack itemStack, Integer burnTime) { -+ Map map = Maps.newLinkedHashMap(); -+ map.putAll(getFuel()); -+ map.put(itemStack.getItem(), burnTime); -+ cachedBurnDurations = com.google.common.collect.ImmutableMap.copyOf(map); -+ } -+ -+ public static void removeFuel(ItemStack itemStack) { -+ Map map = Maps.newLinkedHashMap(); -+ map.putAll(getFuel()); -+ map.remove(itemStack.getItem()); -+ cachedBurnDurations = com.google.common.collect.ImmutableMap.copyOf(map); -+ } -+ // Purpur End -+ - // CraftBukkit start - add fields and methods - private int maxStack = MAX_STACK; - public List transaction = new java.util.ArrayList(); -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index bf22768f8115e5d8a215a2d0bdbb57254f1fc13c..790bf8b0a87c00a45fac2737b6148b2144f838e7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1555,6 +1555,19 @@ public final class CraftServer implements Server { - return true; - } - -+ // Purpur Start -+ @Override -+ public void addFuel(org.bukkit.Material material, int burnTime) { -+ Preconditions.checkArgument(burnTime > 0, "BurnTime must be greater than 0"); -+ net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.addFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)), burnTime); -+ } -+ -+ @Override -+ public void removeFuel(org.bukkit.Material material) { -+ net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.removeFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material))); -+ } -+ // Purpur End -+ - @Override - public List getRecipesFor(ItemStack result) { - Preconditions.checkArgument(result != null, "ItemStack cannot be null"); diff --git a/patches/server/0212-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch b/patches/server/0212-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch deleted file mode 100644 index 511492c34..000000000 --- a/patches/server/0212-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Mon, 23 Aug 2021 17:59:29 +0200 -Subject: [PATCH] Option for if rain and thunder should stop on sleep - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index df8efd93ad6ab30974e3025187cd234b37111129..8cbb36b2dbcb9a1470c95746d7d481a142419164 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1344,6 +1344,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - - private void resetWeatherCycle() { - // CraftBukkit start -+ if (this.purpurConfig.rainStopsAfterSleep) // Purpur - this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - when passing the night - // If we stop due to everyone sleeping we should reset the weather duration to some other random value. - // Not that everyone ever manages to get the whole server to sleep at the same time.... -@@ -1351,6 +1352,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.serverLevelData.setRainTime(0); - } - // CraftBukkit end -+ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur - this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - when passing the night - // CraftBukkit start - // If we stop due to everyone sleeping we should reset the weather duration to some other random value. -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 36bf7feb12b83f97612fa221a48fcffb61f9de88..8d8248e00a9235abffbf381398a13ff2bb9f2b08 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -139,6 +139,8 @@ public class PurpurWorldConfig { - public int raidCooldownSeconds = 0; - public int animalBreedingCooldownSeconds = 0; - public boolean mobsIgnoreRails = false; -+ public boolean rainStopsAfterSleep = true; -+ public boolean thunderStopsAfterSleep = true; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -161,6 +163,8 @@ public class PurpurWorldConfig { - raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds); - animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds); - mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails); -+ rainStopsAfterSleep = getBoolean("gameplay-mechanics.rain-stops-after-sleep", rainStopsAfterSleep); -+ thunderStopsAfterSleep = getBoolean("gameplay-mechanics.thunder-stops-after-sleep", thunderStopsAfterSleep); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0213-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch b/patches/server/0213-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch deleted file mode 100644 index 4ffc844e9..000000000 --- a/patches/server/0213-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 23 Aug 2021 20:57:04 -0500 -Subject: [PATCH] Chance for azalea blocks to grow into trees naturally - - -diff --git a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java -index 95b53450a807fccfa55b59852da52785b8cf3e3d..c69f1d23979a0759472d22760a18d986b2d979b6 100644 ---- a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java -@@ -43,6 +43,20 @@ public class AzaleaBlock extends BushBlock implements BonemealableBlock { - - @Override - public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { -+ // Purpur start -+ growTree(world, random, pos, state); -+ } -+ -+ @Override -+ public void randomTick(net.minecraft.world.level.block.state.BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { -+ double chance = state.getBlock() == Blocks.FLOWERING_AZALEA ? world.purpurConfig.floweringAzaleaGrowthChance : world.purpurConfig.azaleaGrowthChance; -+ if (chance > 0.0D && world.getMaxLocalRawBrightness(pos.above()) > 9 && random.nextDouble() < chance) { -+ growTree(world, random, pos, state); -+ } -+ } -+ -+ private void growTree(ServerLevel world, RandomSource random, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) { -+ // Purpur end - TREE_GROWER.growTree(world, world.getChunkSource().getGenerator(), pos, state, random); - } - -diff --git a/src/main/java/net/minecraft/world/level/block/Blocks.java b/src/main/java/net/minecraft/world/level/block/Blocks.java -index 0453397c157c8c7968947445f41bc46b68b111e8..ed35878fdb9dffcd46c27d26ee8379401207cef5 100644 ---- a/src/main/java/net/minecraft/world/level/block/Blocks.java -+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java -@@ -1093,8 +1093,8 @@ public class Blocks { - public static final Block CAVE_VINES = register("cave_vines", new CaveVinesBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().lightLevel(CaveVines.emission(14)).instabreak().sound(SoundType.CAVE_VINES).pushReaction(PushReaction.DESTROY))); - public static final Block CAVE_VINES_PLANT = register("cave_vines_plant", new CaveVinesPlantBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().lightLevel(CaveVines.emission(14)).instabreak().sound(SoundType.CAVE_VINES).pushReaction(PushReaction.DESTROY))); - public static final Block SPORE_BLOSSOM = register("spore_blossom", new SporeBlossomBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instabreak().noCollission().sound(SoundType.SPORE_BLOSSOM).pushReaction(PushReaction.DESTROY))); -- public static final Block AZALEA = register("azalea", new AzaleaBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).forceSolidOff().instabreak().sound(SoundType.AZALEA).noOcclusion().pushReaction(PushReaction.DESTROY))); -- public static final Block FLOWERING_AZALEA = register("flowering_azalea", new AzaleaBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).forceSolidOff().instabreak().sound(SoundType.FLOWERING_AZALEA).noOcclusion().pushReaction(PushReaction.DESTROY))); -+ public static final Block AZALEA = register("azalea", new AzaleaBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).forceSolidOff().randomTicks().instabreak().sound(SoundType.AZALEA).noOcclusion().pushReaction(PushReaction.DESTROY))); // Purpur -+ public static final Block FLOWERING_AZALEA = register("flowering_azalea", new AzaleaBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).forceSolidOff().randomTicks().instabreak().sound(SoundType.FLOWERING_AZALEA).noOcclusion().pushReaction(PushReaction.DESTROY))); // Purpur - public static final Block MOSS_CARPET = register("moss_carpet", new CarpetBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GREEN).strength(0.1F).sound(SoundType.MOSS_CARPET).pushReaction(PushReaction.DESTROY))); - public static final Block PINK_PETALS = register("pink_petals", new PinkPetalsBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().sound(SoundType.PINK_PETALS).pushReaction(PushReaction.DESTROY))); - public static final Block MOSS_BLOCK = register("moss_block", new MossBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GREEN).strength(0.1F).sound(SoundType.MOSS).pushReaction(PushReaction.DESTROY))); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8d8248e00a9235abffbf381398a13ff2bb9f2b08..9e7c090c44edc98b3399046ad76c5ca144f5bb2d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -739,6 +739,11 @@ public class PurpurWorldConfig { - anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage); - } - -+ public double azaleaGrowthChance = 0.0D; -+ private void azaleaSettings() { -+ azaleaGrowthChance = getDouble("blocks.azalea.growth-chance", azaleaGrowthChance); -+ } -+ - public int beaconLevelOne = 20; - public int beaconLevelTwo = 30; - public int beaconLevelThree = 40; -@@ -876,6 +881,11 @@ public class PurpurWorldConfig { - farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling); - } - -+ public double floweringAzaleaGrowthChance = 0.0D; -+ private void floweringAzaleaSettings() { -+ floweringAzaleaGrowthChance = getDouble("blocks.flowering_azalea.growth-chance", floweringAzaleaGrowthChance); -+ } -+ - public boolean furnaceUseLavaFromUnderneath = false; - private void furnaceSettings() { - if (PurpurConfig.version < 17) { diff --git a/patches/server/0214-Shift-right-click-to-use-exp-for-mending.patch b/patches/server/0214-Shift-right-click-to-use-exp-for-mending.patch deleted file mode 100644 index cd149434e..000000000 --- a/patches/server/0214-Shift-right-click-to-use-exp-for-mending.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Tue, 24 Aug 2021 16:48:35 -0500 -Subject: [PATCH] Shift right click to use exp for mending - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -index 1e0d07949e8b4450a400625a23d386024192eef1..f3bac087856f420aa6c14707e90288f4962967bf 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -518,6 +518,7 @@ public class ServerPlayerGameMode { - public InteractionHand interactHand; - public ItemStack interactItemStack; - public InteractionResult useItemOn(ServerPlayer player, Level world, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) { -+ if (shiftClickMended(stack)) return InteractionResult.SUCCESS; // Purpur - BlockPos blockposition = hitResult.getBlockPos(); - BlockState iblockdata = world.getBlockState(blockposition); - InteractionResult enuminteractionresult = InteractionResult.PASS; -@@ -620,4 +621,18 @@ public class ServerPlayerGameMode { - public void setLevel(ServerLevel world) { - this.level = world; - } -+ -+ // Purpur start -+ public boolean shiftClickMended(ItemStack itemstack) { -+ if (this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints > 0 && this.player.isShiftKeyDown() && this.player.getBukkitEntity().hasPermission("purpur.mending_shift_click")) { -+ int points = Math.min(this.player.totalExperience, this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints); -+ if (points > 0 && itemstack.isDamaged() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MENDING, itemstack) > 0) { -+ this.player.giveExperiencePoints(-points); -+ this.player.level().addFreshEntity(new net.minecraft.world.entity.ExperienceOrb(this.player.level(), this.player.getX(), this.player.getY(), this.player.getZ(), points, org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN, this.player, this.player)); -+ return true; -+ } -+ } -+ return false; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 7ac39b9f7a0afabe4a6fde954a85f0d4f53a01df..cb1851fca9290edb502662c100bb2ac6fa4d70d1 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2144,6 +2144,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - - boolean cancelled; - if (movingobjectposition == null || movingobjectposition.getType() != HitResult.Type.BLOCK) { -+ if (this.player.gameMode.shiftClickMended(itemstack)) return; // Purpur - org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack, enumhand); - cancelled = event.useItemInHand() == Event.Result.DENY; - } else { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9e7c090c44edc98b3399046ad76c5ca144f5bb2d..8d59047a6ab10af7a4cc418345ff7b17ce5d6355 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -415,6 +415,7 @@ public class PurpurWorldConfig { - public int playerCreativePortalWaitTime = 1; - public boolean playerRidableInWater = false; - public boolean playerRemoveBindingWithWeakness = false; -+ public int shiftRightClickRepairsMendingPoints = 0; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -443,6 +444,7 @@ public class PurpurWorldConfig { - playerCreativePortalWaitTime = getInt("gameplay-mechanics.player.creative-portal-wait-time", playerCreativePortalWaitTime); - playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); - playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness); -+ shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0215-Dolphins-naturally-aggressive-to-players-chance.patch b/patches/server/0215-Dolphins-naturally-aggressive-to-players-chance.patch deleted file mode 100644 index 1a144d8de..000000000 --- a/patches/server/0215-Dolphins-naturally-aggressive-to-players-chance.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 30 Aug 2021 22:14:39 -0500 -Subject: [PATCH] Dolphins naturally aggressive to players chance - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index 77746eeffdc612793a6c907f222753bce5cd0ed4..b6342637f1b42b6368dcda17eea4b39a22ec3b79 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -82,6 +82,7 @@ public class Dolphin extends WaterAnimal { - public static final Predicate ALLOWED_ITEMS = (entityitem) -> { - return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater(); - }; -+ private boolean isNaturallyAggressiveToPlayers; // Purpur - private int spitCooldown; // Purpur - - public Dolphin(EntityType type, Level world) { -@@ -173,6 +174,7 @@ public class Dolphin extends WaterAnimal { - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { - this.setAirSupply(this.getMaxAirSupply()); - this.setXRot(0.0F); -+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance; // Purpur - return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); - } - -@@ -242,6 +244,7 @@ public class Dolphin extends WaterAnimal { - protected void registerGoals() { - this.goalSelector.addGoal(0, new BreathAirGoal(this)); - this.goalSelector.addGoal(0, new TryFindWaterGoal(this)); -+ this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this)); - this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0D)); -@@ -249,12 +252,13 @@ public class Dolphin extends WaterAnimal { - this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); - this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(5, new DolphinJumpGoal(this, 10)); -- this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true)); -+ //this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - moved up - this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal()); - this.goalSelector.addGoal(8, new FollowBoatGoal(this)); - this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0D, 1.0D)); - this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers()); -+ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, target -> isNaturallyAggressiveToPlayers)); // Purpur - } - - public static AttributeSupplier.Builder createAttributes() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8d59047a6ab10af7a4cc418345ff7b17ce5d6355..80f7ae88760e6de74445271712929b3bdc2dda3c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1281,6 +1281,7 @@ public class PurpurWorldConfig { - public double dolphinMaxHealth = 10.0D; - public boolean dolphinDisableTreasureSearching = false; - public boolean dolphinTakeDamageFromWater = false; -+ public double dolphinNaturallyAggressiveToPlayersChance = 0.0D; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -1295,6 +1296,7 @@ public class PurpurWorldConfig { - dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth); - dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching); - dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater); -+ dolphinNaturallyAggressiveToPlayersChance = getDouble("mobs.dolphin.naturally-aggressive-to-players-chance", dolphinNaturallyAggressiveToPlayersChance); - } - - public boolean donkeyRidableInWater = false; diff --git a/patches/server/0216-Cows-naturally-aggressive-to-players-chance.patch b/patches/server/0216-Cows-naturally-aggressive-to-players-chance.patch deleted file mode 100644 index 18108ca64..000000000 --- a/patches/server/0216-Cows-naturally-aggressive-to-players-chance.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 30 Aug 2021 22:49:53 -0500 -Subject: [PATCH] Cows naturally aggressive to players chance - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 8d714c029cf3b62af1ff8140a82ed3ae463e0e18..37f073e821a1b0f8efe89a7d88ce732cda402dff 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -39,6 +39,7 @@ import org.bukkit.event.player.PlayerBucketFillEvent; - // CraftBukkit end - - public class Cow extends Animal { -+ private boolean isNaturallyAggressiveToPlayers; // Purpur - - public Cow(EntityType type, Level world) { - super(type, world); -@@ -64,6 +65,7 @@ public class Cow extends Animal { - @Override - public void initAttributes() { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); -+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur - } - - @Override -@@ -76,11 +78,18 @@ public class Cow extends Animal { - return this.level().purpurConfig.cowTakeDamageFromWater; - } - -+ @Override -+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.MobSpawnType spawnReason, net.minecraft.world.entity.SpawnGroupData entityData, net.minecraft.nbt.CompoundTag entityNbt) { -+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance; -+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D)); -+ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - if (level().purpurConfig.cowFeedMushrooms > 0) this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT, Blocks.RED_MUSHROOM.asItem(), Blocks.BROWN_MUSHROOM.asItem()), false)); else // Purpur - this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.of(Items.WHEAT), false)); -@@ -88,10 +97,11 @@ public class Cow extends Animal { - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D)); - this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); - this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); -+ this.targetSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, target -> isNaturallyAggressiveToPlayers)); // Purpur - } - - public static AttributeSupplier.Builder createAttributes() { -- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D); -+ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 80f7ae88760e6de74445271712929b3bdc2dda3c..1913ace7da1ba4beda5373d51a63ca7d3da7a564 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1230,7 +1230,14 @@ public class PurpurWorldConfig { - public int cowFeedMushrooms = 0; - public int cowBreedingTicks = 6000; - public boolean cowTakeDamageFromWater = false; -+ public double cowNaturallyAggressiveToPlayersChance = 0.0D; -+ public double cowNaturallyAggressiveToPlayersDamage = 2.0D; - private void cowSettings() { -+ if (PurpurConfig.version < 22) { -+ double oldValue = getDouble("mobs.cow.naturally-aggressive-to-players-chance", cowNaturallyAggressiveToPlayersChance); -+ set("mobs.cow.naturally-aggressive-to-players-chance", null); -+ set("mobs.cow.naturally-aggressive-to-players.chance", oldValue); -+ } - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); - cowControllable = getBoolean("mobs.cow.controllable", cowControllable); -@@ -1243,6 +1250,8 @@ public class PurpurWorldConfig { - cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); - cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks); - cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater); -+ cowNaturallyAggressiveToPlayersChance = getDouble("mobs.cow.naturally-aggressive-to-players.chance", cowNaturallyAggressiveToPlayersChance); -+ cowNaturallyAggressiveToPlayersDamage = getDouble("mobs.cow.naturally-aggressive-to-players.damage", cowNaturallyAggressiveToPlayersDamage); - } - - public boolean creeperRidable = false; diff --git a/patches/server/0217-Option-for-beds-to-explode-on-villager-sleep.patch b/patches/server/0217-Option-for-beds-to-explode-on-villager-sleep.patch deleted file mode 100644 index 50e65cc9a..000000000 --- a/patches/server/0217-Option-for-beds-to-explode-on-villager-sleep.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Tue, 31 Aug 2021 16:48:29 +0200 -Subject: [PATCH] Option for beds to explode on villager sleep - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 444dbab9a1c95d1f8bc97e165e5302473a29c7e8..2ffceda6ce3cfe515f9348dafc28b04689d5a460 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -1110,6 +1110,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - - @Override - public void startSleeping(BlockPos pos) { -+ // Purpur start -+ if (level().purpurConfig.bedExplodeOnVillagerSleep && this.level().getBlockState(pos).getBlock() instanceof net.minecraft.world.level.block.BedBlock) { -+ this.level().explode(null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (float) this.level().purpurConfig.bedExplosionPower, this.level().purpurConfig.bedExplosionFire, this.level().purpurConfig.bedExplosionEffect); -+ return; -+ } -+ // Purpur end - super.startSleeping(pos); - this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime()); // CraftBukkit - decompile error - this.brain.eraseMemory(MemoryModuleType.WALK_TARGET); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1913ace7da1ba4beda5373d51a63ca7d3da7a564..af78db690b3d3437aedcb72bca76b01a4689921d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -758,6 +758,7 @@ public class PurpurWorldConfig { - } - - public boolean bedExplode = true; -+ public boolean bedExplodeOnVillagerSleep = false; - public double bedExplosionPower = 5.0D; - public boolean bedExplosionFire = true; - public net.minecraft.world.level.Level.ExplosionInteraction bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -@@ -768,6 +769,7 @@ public class PurpurWorldConfig { - } - } - bedExplode = getBoolean("blocks.bed.explode", bedExplode); -+ bedExplodeOnVillagerSleep = getBoolean("blocks.bed.explode-on-villager-sleep", bedExplodeOnVillagerSleep); - bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower); - bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire); - try { diff --git a/patches/server/0218-Halloween-options-and-optimizations.patch b/patches/server/0218-Halloween-options-and-optimizations.patch deleted file mode 100644 index 7f1c82875..000000000 --- a/patches/server/0218-Halloween-options-and-optimizations.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Mon, 13 Sep 2021 04:48:21 +0200 -Subject: [PATCH] Halloween options and optimizations - - -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index c3d1d77a5703db4e1e3eb38fdd0b8903f691bf25..c103c3da165ec4a9717400fdf278de2a667edd2a 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -300,7 +300,7 @@ public class Bat extends AmbientCreature { - int i = world.getMaxLocalRawBrightness(pos); - byte b0 = 4; - -- if (Bat.isHalloween()) { -+ if (Bat.isHalloweenSeason(world.getMinecraftWorld())) { // Purpur - b0 = 7; - } else if (random.nextBoolean()) { - return false; -@@ -314,6 +314,7 @@ public class Bat extends AmbientCreature { - private static boolean isSpookySeason = false; - private static final int ONE_HOUR = 20 * 60 * 60; - private static int lastSpookyCheck = -ONE_HOUR; -+ public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur - private static boolean isHalloween() { - if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { - LocalDate localdate = LocalDate.now(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -index 892ae3a9ac2395896ff7881c49eaece8d46e831e..41376b705748e14c1c4174e07732ce09ad8e581f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -143,11 +143,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - this.reassessWeaponGoal(); - this.setCanPickUpLoot(this.level().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper - if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { -- LocalDate localdate = LocalDate.now(); -- int i = localdate.get(ChronoField.DAY_OF_MONTH); -- int j = localdate.get(ChronoField.MONTH_OF_YEAR); -- -- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) { -+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur - this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); - this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F; - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 0bc90b6d5c5a3cb3477d41336a9bb1130ff32fa1..a8d2f8654ee370e98bb1c4e7e1111debd62a7429 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -593,11 +593,7 @@ public class Zombie extends Monster { - } - - if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { -- LocalDate localdate = LocalDate.now(); -- int i = localdate.get(ChronoField.DAY_OF_MONTH); -- int j = localdate.get(ChronoField.MONTH_OF_YEAR); -- -- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) { -+ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < this.level().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur - this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); - this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index af78db690b3d3437aedcb72bca76b01a4689921d..af6de9d4d6dace3f26b7012606d399f5c398ef0a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1623,6 +1623,13 @@ public class PurpurWorldConfig { - guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater); - } - -+ public boolean forceHalloweenSeason = false; -+ public float chanceHeadHalloweenOnEntity = 0.25F; -+ private void halloweenSetting() { -+ forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason); -+ chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity); -+ } -+ - public boolean hoglinRidable = false; - public boolean hoglinRidableInWater = true; - public boolean hoglinControllable = true; diff --git a/patches/server/0219-Config-for-grindstones.patch b/patches/server/0219-Config-for-grindstones.patch deleted file mode 100644 index 864f86f7b..000000000 --- a/patches/server/0219-Config-for-grindstones.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Wed, 29 Sep 2021 13:37:57 -0400 -Subject: [PATCH] Config for grindstones - - -diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -index b56766ff0e61691294b40ea8c2370940c0e8b640..6860e3467bf785e9d017fc98fce1e3cf71f9b6ee 100644 ---- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -@@ -130,7 +130,7 @@ public class GrindstoneMenu extends AbstractContainerMenu { - Enchantment enchantment = (Enchantment) entry.getKey(); - Integer integer = (Integer) entry.getValue(); - -- if (!enchantment.isCurse()) { -+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchantment)) { // Purpur - j += enchantment.getMinCost(integer); - } - } -@@ -230,7 +230,7 @@ public class GrindstoneMenu extends AbstractContainerMenu { - Entry entry = (Entry) iterator.next(); - Enchantment enchantment = (Enchantment) entry.getKey(); - -- if (!enchantment.isCurse() || EnchantmentHelper.getItemEnchantmentLevel(enchantment, itemstack2) == 0) { -+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(enchantment) || EnchantmentHelper.getItemEnchantmentLevel(enchantment, itemstack2) == 0) { // Purpur - itemstack2.enchant(enchantment, (Integer) entry.getValue()); - } - } -@@ -250,7 +250,7 @@ public class GrindstoneMenu extends AbstractContainerMenu { - } - - Map map = (Map) EnchantmentHelper.getEnchantments(item).entrySet().stream().filter((entry) -> { -- return ((Enchantment) entry.getKey()).isCurse(); -+ return org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains((Enchantment) entry.getKey()); // Purpur - }).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - EnchantmentHelper.setEnchantments(map, itemstack1); -@@ -266,6 +266,20 @@ public class GrindstoneMenu extends AbstractContainerMenu { - itemstack1.setRepairCost(AnvilMenu.calculateIncreasedRepairCost(itemstack1.getBaseRepairCost())); - } - -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveAttributes && itemstack1.getTag() != null) { -+ for (String key : itemstack1.getTag().getAllKeys()) { -+ if (!key.equals("display")) { -+ itemstack1.getTag().remove(key); -+ } -+ } -+ } -+ -+ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveDisplay && itemstack1.getTag() != null) { -+ itemstack1.getTag().remove("display"); -+ } -+ // Purpur end -+ - return itemstack1; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 853cf54077d760f1118ce494b401d286d789e5fc..19313ea70f105e71019947e34669384472b0aaab 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -309,6 +309,9 @@ public class PurpurConfig { - public static int beeInsideBeeHive = 3; - public static boolean anvilCumulativeCost = true; - public static int lightningRodRange = 128; -+ public static Set grindstoneIgnoredEnchants = new HashSet<>(); -+ public static boolean grindstoneRemoveAttributes = false; -+ public static boolean grindstoneRemoveDisplay = false; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -343,6 +346,19 @@ public class PurpurConfig { - beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); - anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); - lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange); -+ ArrayList defaultCurses = new ArrayList<>(){{ -+ add("minecraft:binding_curse"); -+ add("minecraft:vanishing_curse"); -+ }}; -+ if (version < 24 && !getBoolean("settings.blocks.grindstone.ignore-curses", true)) { -+ defaultCurses.clear(); -+ } -+ getList("settings.blocks.grindstone.ignored-enchants", defaultCurses).forEach(key -> { -+ Enchantment enchantment = BuiltInRegistries.ENCHANTMENT.get(new ResourceLocation(key.toString())); -+ grindstoneIgnoredEnchants.add(enchantment); -+ }); -+ grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes); -+ grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay); - } - - public static boolean allowInfinityMending = false; diff --git a/patches/server/0220-UPnP-Port-Forwarding.patch b/patches/server/0220-UPnP-Port-Forwarding.patch deleted file mode 100644 index 62cac0574..000000000 --- a/patches/server/0220-UPnP-Port-Forwarding.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 22 Jan 2020 20:13:40 -0600 -Subject: [PATCH] UPnP Port Forwarding - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index f814bb61cb6981202db1a04ef3c114afb0aa55ee..cf165386904e4f084ff69463e29fe10fd9af6812 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -309,6 +309,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Thu, 14 Oct 2021 01:53:20 -0700 -Subject: [PATCH] Campfire option for lit when placed - - -diff --git a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java -index 219c87dcf065e86512f330fbeec59e55f4675083..f8fd3b320494d1c1e8ee3d170f2feebd152230fa 100644 ---- a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java -@@ -122,7 +122,7 @@ public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedB - BlockPos blockposition = ctx.getClickedPos(); - boolean flag = world.getFluidState(blockposition).getType() == Fluids.WATER; - -- return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, !flag)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection()); -+ return (BlockState) ((BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(CampfireBlock.WATERLOGGED, flag)).setValue(CampfireBlock.SIGNAL_FIRE, this.isSmokeSource(world.getBlockState(blockposition.below())))).setValue(CampfireBlock.LIT, world.purpurConfig.campFireLitWhenPlaced ? !flag : world.purpurConfig.campFireLitWhenPlaced)).setValue(CampfireBlock.FACING, ctx.getHorizontalDirection()); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index af6de9d4d6dace3f26b7012606d399f5c398ef0a..002074c521209190fcdf9f3d7ebbbfdc2fa8c8f5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -801,6 +801,11 @@ public class PurpurWorldConfig { - cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors); - } - -+ public boolean campFireLitWhenPlaced = true; -+ private void campFireSettings() { -+ campFireLitWhenPlaced = getBoolean("blocks.campfire.lit-when-placed", campFireLitWhenPlaced); -+ } -+ - public boolean chestOpenWithBlockOnTop = false; - private void chestSettings() { - chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); diff --git a/patches/server/0222-options-to-extinguish-fire-blocks-with-snowballs.patch b/patches/server/0222-options-to-extinguish-fire-blocks-with-snowballs.patch deleted file mode 100644 index 5d4d1ea08..000000000 --- a/patches/server/0222-options-to-extinguish-fire-blocks-with-snowballs.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Thu, 14 Oct 2021 02:05:52 -0700 -Subject: [PATCH] options to extinguish fire blocks with snowballs - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -index b90cedad282e95a067aca176fafa9f72a726f520..440d3d72d8b2dac14f83a83caa5ae9dbf3e979b6 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -@@ -57,6 +57,36 @@ public class Snowball extends ThrowableItemProjectile { - entity.hurt(this.damageSources().thrown(this, this.getOwner()), (float)i); - } - -+ // Purpur start - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire -+ @Override -+ protected void onHitBlock(net.minecraft.world.phys.BlockHitResult blockHitResult) { -+ super.onHitBlock(blockHitResult); -+ -+ if (!this.level().isClientSide) { -+ net.minecraft.core.BlockPos blockposition = blockHitResult.getBlockPos(); -+ net.minecraft.core.BlockPos blockposition1 = blockposition.relative(blockHitResult.getDirection()); -+ -+ net.minecraft.world.level.block.state.BlockState iblockdata = this.level().getBlockState(blockposition); -+ -+ if (this.level().purpurConfig.snowballExtinguishesFire && this.level().getBlockState(blockposition1).is(net.minecraft.world.level.block.Blocks.FIRE)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition1, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) { -+ this.level().removeBlock(blockposition1, false); -+ } -+ } else if (this.level().purpurConfig.snowballExtinguishesCandles && net.minecraft.world.level.block.AbstractCandleBlock.isLit(iblockdata)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.AbstractCandleBlock.LIT, false))) { -+ net.minecraft.world.level.block.AbstractCandleBlock.extinguish(null, iblockdata, this.level(), blockposition); -+ } -+ } else if (this.level().purpurConfig.snowballExtinguishesCampfires && net.minecraft.world.level.block.CampfireBlock.isLitCampfire(iblockdata)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false))) { -+ this.level().levelEvent(null, 1009, blockposition, 0); -+ net.minecraft.world.level.block.CampfireBlock.dowse(this.getOwner(), this.level(), blockposition, iblockdata); -+ this.level().setBlockAndUpdate(blockposition, iblockdata.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false)); -+ } -+ } -+ } -+ } -+ // Purpur end -+ - @Override - protected void onHit(HitResult hitResult) { - super.onHit(hitResult); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 002074c521209190fcdf9f3d7ebbbfdc2fa8c8f5..dc4e59fab1f89379dfce02d6090ed8e700aaa145 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -231,6 +231,9 @@ public class PurpurWorldConfig { - public int glowBerriesEatGlowDuration = 0; - public boolean shulkerBoxItemDropContentsWhenDestroyed = true; - public boolean compassItemShowsBossBar = false; -+ public boolean snowballExtinguishesFire = false; -+ public boolean snowballExtinguishesCandles = false; -+ public boolean snowballExtinguishesCampfires = false; - private void itemSettings() { - itemImmuneToCactus.clear(); - getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { -@@ -279,6 +282,9 @@ public class PurpurWorldConfig { - glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration); - shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed); - compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar); -+ snowballExtinguishesFire = getBoolean("gameplay-mechanics.item.snowball.extinguish.fire", snowballExtinguishesFire); -+ snowballExtinguishesCandles = getBoolean("gameplay-mechanics.item.snowball.extinguish.candles", snowballExtinguishesCandles); -+ snowballExtinguishesCampfires = getBoolean("gameplay-mechanics.item.snowball.extinguish.campfires", snowballExtinguishesCampfires); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0223-Add-option-to-disable-zombie-villagers-cure.patch b/patches/server/0223-Add-option-to-disable-zombie-villagers-cure.patch deleted file mode 100644 index ca01c51b5..000000000 --- a/patches/server/0223-Add-option-to-disable-zombie-villagers-cure.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: rafael59r2 <12960698+rafael59r2@users.noreply.github.com> -Date: Tue, 19 Oct 2021 13:10:44 +0100 -Subject: [PATCH] Add option to disable zombie villagers cure - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 40b3498c57c62f8bdaac50546ca47f4927db7bfa..b8e0ae0714fd2b77831fb714b1bad901e7081cf4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -212,7 +212,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - ItemStack itemstack = player.getItemInHand(hand); - - if (itemstack.is(Items.GOLDEN_APPLE)) { -- if (this.hasEffect(MobEffects.WEAKNESS)) { -+ if (this.hasEffect(MobEffects.WEAKNESS) && level().purpurConfig.zombieVillagerCureEnabled) { // Purpur - if (!player.getAbilities().instabuild) { - itemstack.shrink(1); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index dc4e59fab1f89379dfce02d6090ed8e700aaa145..ad07b4769487f8f4709bea1691b95738cd58aa41 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2840,6 +2840,7 @@ public class PurpurWorldConfig { - public boolean zombieVillagerTakeDamageFromWater = false; - public int zombieVillagerCuringTimeMin = 3600; - public int zombieVillagerCuringTimeMax = 6000; -+ public boolean zombieVillagerCureEnabled = true; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -2857,6 +2858,7 @@ public class PurpurWorldConfig { - zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater); - zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin); - zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax); -+ zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled); - } - - public boolean zombifiedPiglinRidable = false; diff --git a/patches/server/0224-Persistent-BlockEntity-Lore-and-DisplayName.patch b/patches/server/0224-Persistent-BlockEntity-Lore-and-DisplayName.patch deleted file mode 100644 index 473369260..000000000 --- a/patches/server/0224-Persistent-BlockEntity-Lore-and-DisplayName.patch +++ /dev/null @@ -1,216 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> -Date: Wed, 30 Sep 2020 14:32:46 -0700 -Subject: [PATCH] Persistent BlockEntity Lore and DisplayName - -Makes it so that when a BlockEntity is placed in the world and then broken, -the dropped ItemStack retains any original custom display name/lore. - -diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java -index dc45899a677cef389fca23938e201daeaafaa4a3..3802025f4739b7cb58bd039f52ce2744ebe6f345 100644 ---- a/src/main/java/net/minecraft/world/item/BlockItem.java -+++ b/src/main/java/net/minecraft/world/item/BlockItem.java -@@ -153,7 +153,24 @@ public class BlockItem extends Item { - } - - protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) { -- return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack); -+ // Purpur start -+ boolean handled = updateCustomBlockEntityTag(world, player, pos, stack); -+ if (world.purpurConfig.persistentTileEntityDisplayNames && stack.hasTag()) { -+ CompoundTag display = stack.getTagElement("display"); -+ if (display != null) { -+ BlockEntity blockEntity = world.getBlockEntity(pos); -+ if (blockEntity != null) { -+ if (display.contains("Name", 8)) { -+ blockEntity.setPersistentDisplayName(display.getString("Name")); -+ } -+ if (display.contains("Lore", 9)) { -+ blockEntity.setPersistentLore(display.getList("Lore", 8)); -+ } -+ } -+ } -+ } -+ return handled; -+ // Purpur end - } - - @Nullable -diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 48682f0ebec49a04e36f96423cda483a6736fd24..69c78ee2d012344505063c9b297c9c550bde80c4 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -62,6 +62,13 @@ import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - import org.slf4j.Logger; - -+// Purpur start -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.ListTag; -+import net.minecraft.nbt.StringTag; -+import net.minecraft.world.Nameable; -+// Purpur end -+ - public class Block extends BlockBehaviour implements ItemLike { - - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -313,7 +320,7 @@ public class Block extends BlockBehaviour implements ItemLike { - public static void dropResources(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockEntity blockEntity) { - if (world instanceof ServerLevel) { - Block.getDrops(state, (ServerLevel) world, pos, blockEntity).forEach((itemstack) -> { -- Block.popResource((ServerLevel) world, pos, itemstack); -+ Block.popResource((ServerLevel) world, pos, applyDisplayNameAndLoreFromTile(itemstack, blockEntity)); // Purpur - }); - state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true); - } -@@ -329,7 +336,7 @@ public class Block extends BlockBehaviour implements ItemLike { - io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), org.bukkit.craftbukkit.block.CraftBlock.at(world, source), items); - event.callEvent(); - for (var drop : event.getDrops()) { -- popResource(world.getMinecraftWorld(), pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop)); -+ popResource(world.getMinecraftWorld(), pos, applyDisplayNameAndLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur - } - state.spawnAfterBreak(world.getMinecraftWorld(), pos, ItemStack.EMPTY, true); - } -@@ -340,13 +347,53 @@ public class Block extends BlockBehaviour implements ItemLike { - public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) { - if (world instanceof ServerLevel) { - Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> { -- Block.popResource(world, pos, itemstack1); -+ Block.popResource(world, pos, applyDisplayNameAndLoreFromTile(itemstack1, blockEntity)); // Purpur - }); - state.spawnAfterBreak((ServerLevel) world, pos, tool, true); - } - - } - -+ // Purpur start -+ private static ItemStack applyDisplayNameAndLoreFromTile(ItemStack stack, BlockEntity blockEntity) { -+ if (stack.getItem() instanceof BlockItem) { -+ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel && blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayNames) { -+ String name = blockEntity.getPersistentDisplayName(); -+ ListTag lore = blockEntity.getPersistentLore(); -+ if (blockEntity instanceof Nameable) { -+ Nameable namedTile = (Nameable) blockEntity; -+ if (namedTile.hasCustomName()) { -+ name = Component.Serializer.toJson(namedTile.getCustomName()); -+ } -+ } -+ -+ if (name != null || lore != null) { -+ CompoundTag display = stack.getTagElement("display"); -+ if (display == null) { -+ display = new CompoundTag(); -+ } -+ -+ if (name != null) { -+ display.put("Name", StringTag.valueOf(name)); -+ } -+ if (lore != null) { -+ display.put("Lore", lore); -+ } -+ -+ CompoundTag tag = stack.getTag(); -+ if (tag == null) { -+ tag = new CompoundTag(); -+ } -+ tag.put("display", display); -+ -+ stack.setTag(tag); -+ } -+ } -+ } -+ return stack; -+ } -+ // Purpur end -+ - public static void popResource(Level world, BlockPos pos, ItemStack stack) { - double d0 = (double) EntityType.ITEM.getHeight() / 2.0D; - double d1 = (double) pos.getX() + 0.5D + Mth.nextDouble(world.random, -0.25D, 0.25D); -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -index 370a25d2deb54f10a35ee24d9e7e92fbfde60edf..2f19f6ac5de454845f5d13a3ebb93af625b2afc8 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -@@ -6,6 +6,8 @@ import net.minecraft.CrashReportCategory; - import net.minecraft.core.BlockPos; - import net.minecraft.core.registries.BuiltInRegistries; - import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.ListTag; -+import net.minecraft.nbt.StringTag; - import net.minecraft.network.protocol.Packet; - import net.minecraft.network.protocol.game.ClientGamePacketListener; - import net.minecraft.resources.ResourceLocation; -@@ -73,10 +75,27 @@ public abstract class BlockEntity { - if (persistentDataTag instanceof CompoundTag) { - this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); - } -+ // Purpur start -+ if (nbt.contains("Purpur.persistentDisplayName")) { -+ this.persistentDisplayName = nbt.getString("Purpur.persistentDisplayName"); -+ } -+ if (nbt.contains("Purpur.persistentLore")) { -+ this.persistentLore = nbt.getList("Purpur.persistentLore", 8); -+ } -+ // Purpur end - } - // CraftBukkit end - -- protected void saveAdditional(CompoundTag nbt) {} -+ protected void saveAdditional(CompoundTag nbt) { -+ // Purpur start -+ if (this.persistentDisplayName != null) { -+ nbt.put("Purpur.persistentDisplayName", StringTag.valueOf(this.persistentDisplayName)); -+ } -+ if (this.persistentLore != null) { -+ nbt.put("Purpur.persistentLore", this.persistentLore); -+ } -+ // Purpur end -+ } - - public final CompoundTag saveWithFullMetadata() { - CompoundTag nbttagcompound = this.saveWithoutMetadata(); -@@ -263,4 +282,24 @@ public abstract class BlockEntity { - } - // Paper end - -+ // Purpur start -+ private String persistentDisplayName = null; -+ private ListTag persistentLore = null; -+ -+ public void setPersistentDisplayName(String json) { -+ this.persistentDisplayName = json; -+ } -+ -+ public void setPersistentLore(ListTag lore) { -+ this.persistentLore = lore; -+ } -+ -+ public String getPersistentDisplayName() { -+ return this.persistentDisplayName; -+ } -+ -+ public ListTag getPersistentLore() { -+ return this.persistentLore; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ad07b4769487f8f4709bea1691b95738cd58aa41..33f0396c06e1063acb11b8e283b9cb6e6509b821 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -130,6 +130,7 @@ public class PurpurWorldConfig { - public boolean milkCuresBadOmen = true; - public boolean noteBlockIgnoreAbove = false; - public boolean persistentDroppableEntityDisplayNames = true; -+ public boolean persistentTileEntityDisplayNames = false; - public boolean projectilesBypassMobGriefing = false; - public boolean tickFluids = true; - public double mobsBlindnessMultiplier = 1; -@@ -153,6 +154,7 @@ public class PurpurWorldConfig { - imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); -+ persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids); diff --git a/patches/server/0225-Signs-allow-color-codes.patch b/patches/server/0225-Signs-allow-color-codes.patch deleted file mode 100644 index 7729e5626..000000000 --- a/patches/server/0225-Signs-allow-color-codes.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 6 Jun 2019 17:40:30 -0500 -Subject: [PATCH] Signs allow color codes - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index cc313235863910b4978f96d594abff0a20c13e18..01f001c39581c4af5765548e221e254465fe0195 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1583,6 +1583,7 @@ public class ServerPlayer extends Player { - - @Override - public void openTextEdit(SignBlockEntity sign, boolean front) { -+ if (level().purpurConfig.signAllowColors) this.connection.send(sign.getTranslatedUpdatePacket(textFilteringEnabled, front)); // Purpur - this.connection.send(new ClientboundBlockUpdatePacket(this.level(), sign.getBlockPos())); - this.connection.send(new ClientboundOpenSignEditorPacket(sign.getBlockPos(), front)); - } -diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -index 38cde466714e5663cd416b6afd5d2558e139ec09..2d625f18f2ba42ee5a1ebeea78ca395ad6f88b37 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -@@ -202,16 +202,31 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C - return this.setText((SignText) textChanger.apply(signtext), front); - } - -+ // Purpur start -+ private Component translateColors(org.bukkit.entity.Player player, String line, Style style) { -+ if (level.purpurConfig.signAllowColors) { -+ if (player.hasPermission("purpur.sign.color")) line = line.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1"); -+ if (player.hasPermission("purpur.sign.style")) line = line.replaceAll("(?i)&([l-or])", "\u00a7$1"); -+ if (player.hasPermission("purpur.sign.magic")) line = line.replaceAll("(?i)&([kr])", "\u00a7$1"); -+ -+ return io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(line)); -+ } else { -+ return Component.literal(line).setStyle(style); -+ } -+ } -+ // Purpur end -+ - private SignText setMessages(net.minecraft.world.entity.player.Player entityhuman, List list, SignText signtext, boolean front) { // CraftBukkit - SignText originalText = signtext; // CraftBukkit - for (int i = 0; i < list.size(); ++i) { - FilteredText filteredtext = (FilteredText) list.get(i); - Style chatmodifier = signtext.getMessage(i, entityhuman.isTextFilteringEnabled()).getStyle(); - -+ org.bukkit.entity.Player player = (org.bukkit.craftbukkit.entity.CraftPlayer) entityhuman.getBukkitEntity(); // Purpur - if (entityhuman.isTextFilteringEnabled()) { -- signtext = signtext.setMessage(i, Component.literal(net.minecraft.SharedConstants.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only -+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.SharedConstants.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur - } else { -- signtext = signtext.setMessage(i, Component.literal(net.minecraft.SharedConstants.filterText(filteredtext.raw())).setStyle(chatmodifier), Component.literal(net.minecraft.SharedConstants.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only -+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.SharedConstants.filterText(filteredtext.raw()), chatmodifier), translateColors(player, net.minecraft.SharedConstants.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur - } - } - -@@ -351,6 +366,28 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C - return ClientboundBlockEntityDataPacket.create(this); - } - -+ // Purpur start -+ public ClientboundBlockEntityDataPacket getTranslatedUpdatePacket(boolean filtered, boolean front) { -+ final CompoundTag nbt = new CompoundTag(); -+ this.saveAdditional(nbt); -+ final Component[] lines = front ? frontText.getMessages(filtered) : backText.getMessages(filtered); -+ final String side = front ? "front_text" : "back_text"; -+ for (int i = 0; i < 4; i++) { -+ final var component = io.papermc.paper.adventure.PaperAdventure.asAdventure(lines[i]); -+ final String line = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().serialize(component); -+ final var text = net.kyori.adventure.text.Component.text(line); -+ final String json = net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serialize(text); -+ if (!nbt.contains(side)) nbt.put(side, new CompoundTag()); -+ final CompoundTag sideNbt = nbt.getCompound(side); -+ if (!sideNbt.contains("messages")) sideNbt.put("messages", new net.minecraft.nbt.ListTag()); -+ final net.minecraft.nbt.ListTag messagesNbt = sideNbt.getList("messages", Tag.TAG_STRING); -+ messagesNbt.set(i, net.minecraft.nbt.StringTag.valueOf(json)); -+ } -+ nbt.putString("PurpurEditor", "true"); -+ return ClientboundBlockEntityDataPacket.create(this, entity -> nbt); -+ } -+ // Purpur end -+ - @Override - public CompoundTag getUpdateTag() { - return this.saveWithoutMetadata(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 33f0396c06e1063acb11b8e283b9cb6e6509b821..0a43e97fa389cdfd3a542c25878f28afb88d15b7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -981,6 +981,11 @@ public class PurpurWorldConfig { - shulkerBoxAllowOversizedStacks = getBoolean("blocks.shulker_box.allow-oversized-stacks", shulkerBoxAllowOversizedStacks); - } - -+ public boolean signAllowColors = false; -+ private void signSettings() { -+ signAllowColors = getBoolean("blocks.sign.allow-colors", signAllowColors); -+ } -+ - public boolean slabHalfBreak = false; - private void slabSettings() { - slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak); diff --git a/patches/server/0226-Kelp-cave-weeping-and-twisting-vines-configurable-ma.patch b/patches/server/0226-Kelp-cave-weeping-and-twisting-vines-configurable-ma.patch deleted file mode 100644 index 7e314c3e8..000000000 --- a/patches/server/0226-Kelp-cave-weeping-and-twisting-vines-configurable-ma.patch +++ /dev/null @@ -1,182 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 22 Nov 2020 20:13:27 -0600 -Subject: [PATCH] Kelp, cave, weeping, and twisting vines configurable max - growth age - - -diff --git a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java -index 18b5bce1138d50be32e5da013221be69dc47e21f..58b4a0d97af37f7164db86ef821f04102c6c5ddd 100644 ---- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java -@@ -88,4 +88,11 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements Bonemealabl - public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { - world.setBlock(pos, state.setValue(BERRIES, Boolean.valueOf(true)), 2); - } -+ -+ // Purpur start -+ @Override -+ public int getMaxGrowthAge() { -+ return org.purpurmc.purpur.PurpurConfig.caveVinesMaxGrowthAge; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -index 3a1aa4e2405090ccebefb7f5944f36462929e221..f3cf9f06de40054720d1847c1869a9d82592134d 100644 ---- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -@@ -30,12 +30,12 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - - @Override - public BlockState getStateForPlacement(LevelAccessor world) { -- return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, world.getRandom().nextInt(25)); -+ return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, world.getRandom().nextInt(getMaxGrowthAge())); // Purpur - } - - @Override - public boolean isRandomlyTicking(BlockState state) { -- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25; -+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge(); // Purpur - } - - @Override -@@ -51,7 +51,7 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - } else { - modifier = world.spigotConfig.caveVinesModifier; - } -- if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution -+ if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge() && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution // Purpur - // Spigot end - BlockPos blockposition1 = pos.relative(this.growthDirection); - -@@ -73,11 +73,11 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - } - - public BlockState getMaxAgeState(BlockState state) { -- return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, 25); -+ return (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, getMaxGrowthAge()); // Purpur - } - - public boolean isMaxAge(BlockState state) { -- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) == 25; -+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) >= getMaxGrowthAge(); // Purpur - } - - protected BlockState updateBodyAfterConvertedFromHead(BlockState from, BlockState to) { -@@ -119,13 +119,13 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - @Override - public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { - BlockPos blockposition1 = pos.relative(this.growthDirection); -- int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, 25); -+ int i = Math.min((Integer) state.getValue(GrowingPlantHeadBlock.AGE) + 1, getMaxGrowthAge()); // Purpur - int j = this.getBlocksToGrowWhenBonemealed(random); - - for (int k = 0; k < j && this.canGrowInto(world.getBlockState(blockposition1)); ++k) { - world.setBlockAndUpdate(blockposition1, (BlockState) state.setValue(GrowingPlantHeadBlock.AGE, i)); - blockposition1 = blockposition1.relative(this.growthDirection); -- i = Math.min(i + 1, 25); -+ i = Math.min(i + 1, getMaxGrowthAge()); // Purpur - } - - } -@@ -138,4 +138,6 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - protected GrowingPlantHeadBlock getHeadBlock() { - return this; - } -+ -+ public abstract int getMaxGrowthAge(); // Purpur - } -diff --git a/src/main/java/net/minecraft/world/level/block/KelpBlock.java b/src/main/java/net/minecraft/world/level/block/KelpBlock.java -index bc66fa91ec3e13431d5d9b6e17935cab73066be7..0f16b5ed2e249f3d8f583dc941e32066d354cf95 100644 ---- a/src/main/java/net/minecraft/world/level/block/KelpBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/KelpBlock.java -@@ -64,4 +64,11 @@ public class KelpBlock extends GrowingPlantHeadBlock implements LiquidBlockConta - public FluidState getFluidState(BlockState state) { - return Fluids.WATER.getSource(false); - } -+ -+ // Purpur start -+ @Override -+ public int getMaxGrowthAge() { -+ return org.purpurmc.purpur.PurpurConfig.kelpMaxGrowthAge; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java -index 6866605c7ef5361b21130a19a59c3fa3660dfb19..dee5d76d29da13f8639ab5d392cd0143201e71ba 100644 ---- a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java -@@ -27,4 +27,11 @@ public class TwistingVinesBlock extends GrowingPlantHeadBlock { - protected boolean canGrowInto(BlockState state) { - return NetherVines.isValidGrowthState(state); - } -+ -+ // Purpur start -+ @Override -+ public int getMaxGrowthAge() { -+ return org.purpurmc.purpur.PurpurConfig.twistingVinesMaxGrowthAge; -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java -index e5c135ec059746b75fe58516809584221285cdbe..713c7e6e31a3e1097b612c77a4fce147c9252e0b 100644 ---- a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java -@@ -27,4 +27,11 @@ public class WeepingVinesBlock extends GrowingPlantHeadBlock { - protected boolean canGrowInto(BlockState state) { - return NetherVines.isValidGrowthState(state); - } -+ -+ // Purpur start -+ @Override -+ public int getMaxGrowthAge() { -+ return org.purpurmc.purpur.PurpurConfig.weepingVinesMaxGrowthAge; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 37764d22804fecf704963d3208039834f1525b0d..b359b106f51e68d8a1e59d40dff75d656cd3b6e4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -312,6 +312,10 @@ public class PurpurConfig { - public static Set grindstoneIgnoredEnchants = new HashSet<>(); - public static boolean grindstoneRemoveAttributes = false; - public static boolean grindstoneRemoveDisplay = false; -+ public static int caveVinesMaxGrowthAge = 25; -+ public static int kelpMaxGrowthAge = 25; -+ public static int twistingVinesMaxGrowthAge = 25; -+ public static int weepingVinesMaxGrowthAge = 25; - private static void blockSettings() { - if (version < 3) { - boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); -@@ -359,6 +363,30 @@ public class PurpurConfig { - }); - grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes); - grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay); -+ caveVinesMaxGrowthAge = getInt("settings.blocks.cave_vines.max-growth-age", caveVinesMaxGrowthAge); -+ if (caveVinesMaxGrowthAge > 25) { -+ caveVinesMaxGrowthAge = 25; -+ log(Level.WARNING, "blocks.cave_vines.max-growth-age is set to above maximum allowed value of 25"); -+ log(Level.WARNING, "Using value of 25 to prevent issues"); -+ } -+ kelpMaxGrowthAge = getInt("settings.blocks.kelp.max-growth-age", kelpMaxGrowthAge); -+ if (kelpMaxGrowthAge > 25) { -+ kelpMaxGrowthAge = 25; -+ log(Level.WARNING, "blocks.kelp.max-growth-age is set to above maximum allowed value of 25"); -+ log(Level.WARNING, "Using value of 25 to prevent issues"); -+ } -+ twistingVinesMaxGrowthAge = getInt("settings.blocks.twisting_vines.max-growth-age", twistingVinesMaxGrowthAge); -+ if (twistingVinesMaxGrowthAge > 25) { -+ twistingVinesMaxGrowthAge = 25; -+ log(Level.WARNING, "blocks.twisting_vines.max-growth-age is set to above maximum allowed value of 25"); -+ log(Level.WARNING, "Using value of 25 to prevent issues"); -+ } -+ weepingVinesMaxGrowthAge = getInt("settings.blocks.weeping_vines.max-growth-age", weepingVinesMaxGrowthAge); -+ if (weepingVinesMaxGrowthAge > 25) { -+ weepingVinesMaxGrowthAge = 25; -+ log(Level.WARNING, "blocks.weeping_vines.max-growth-age is set to above maximum allowed value of 25"); -+ log(Level.WARNING, "Using value of 25 to prevent issues"); -+ } - } - - public static boolean allowInfinityMending = false; diff --git a/patches/server/0227-Mobs-always-drop-experience.patch b/patches/server/0227-Mobs-always-drop-experience.patch deleted file mode 100644 index 061951b83..000000000 --- a/patches/server/0227-Mobs-always-drop-experience.patch +++ /dev/null @@ -1,2306 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Tue, 21 Dec 2021 20:40:42 -0600 -Subject: [PATCH] Mobs always drop experience - - -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index 1a67f46b57e398d23fbc495ee81ae62e0d84d3dc..43cdda0cb26c5d5cc9025199eb71673d71c2abea 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -51,6 +51,11 @@ public class GlowSquid extends Squid { - return this.level().purpurConfig.glowSquidTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.glowSquidAlwaysDropExp; -+ } -+ - @Override - protected ParticleOptions getInkParticle() { - return ParticleTypes.GLOW_SQUID_INK; -diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index c103c3da165ec4a9717400fdf278de2a667edd2a..1eab1393a2636c4a247f25dae317cea60cc7021c 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -281,6 +281,11 @@ public class Bat extends AmbientCreature { - return this.level().purpurConfig.batTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.batAlwaysDropExp; -+ } -+ - @Override - public void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index 0ee62562d36292d48226a3b20ac54aafe6d12394..ac4ca4de2be18a08268b24dfe259cfd136b1a4da 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -486,6 +486,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return this.level().purpurConfig.beeTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.beeAlwaysDropExp; -+ } -+ - @Override - public int getRemainingPersistentAngerTime() { - return (Integer) this.entityData.get(Bee.DATA_REMAINING_ANGER_TIME); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 7f1763b820a0c5dc69bdf6cdf14a97a283ba5916..3347b39fa1bc3308aa3b70b4169523885b91a047 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -137,6 +137,11 @@ public class Cat extends TamableAnimal implements VariantHolder { - return this.level().purpurConfig.catTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.catAlwaysDropExp; -+ } -+ - public ResourceLocation getResourceLocation() { - return this.getVariant().texture(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -index 6fe3e62ba4534e821937baa14d728186f8c6439e..00bdb2b75221349cb40da2b5fc1563393fd17ef6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -89,6 +89,11 @@ public class Chicken extends Animal { - return this.level().purpurConfig.chickenTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.chickenAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index 2343325fa9a771de7b9445cda24a2bcd7a7c1761..f0b6118a9995bb41836685bbf94d2e7fb15761eb 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -35,6 +35,11 @@ public class Cod extends AbstractSchoolingFish { - return this.level().purpurConfig.codTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.codAlwaysDropExp; -+ } -+ - @Override - public ItemStack getBucketItemStack() { - return new ItemStack(Items.COD_BUCKET); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 37f073e821a1b0f8efe89a7d88ce732cda402dff..3cee7d5656b2df2997ceaee3489c02ce881e6875 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -84,6 +84,11 @@ public class Cow extends Animal { - return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.cowAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -index b6342637f1b42b6368dcda17eea4b39a22ec3b79..4c9c07d470357c35bd5ba3c21437a2c44c52811e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -169,6 +169,11 @@ public class Dolphin extends WaterAnimal { - return this.level().purpurConfig.dolphinTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.dolphinAlwaysDropExp; -+ } -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index ed7d057bc12ca0e3d808320a008c902ebb27e1ca..ca3096abbd54745af591101c7b306ed87f212954 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -196,6 +196,11 @@ public class Fox extends Animal implements VariantHolder { - return this.level().purpurConfig.foxTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.foxAlwaysDropExp; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index c2817324ff57860ea2628dc4ac8e78b76cf67a5a..64aba511e615983988cdb6a0fd45b7d9d4f2f16d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -103,6 +103,11 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - this.summoner = summoner; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.ironGolemAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 56fb35206af7e04a78eba489d444135176188a20..1b0a0e36baa0f664f06b2d166aa907b320066b6e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -95,6 +95,11 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - public boolean isSensitiveToWater() { - return this.level().purpurConfig.rabbitTakeDamageFromWater; - } -+ -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.rabbitAlwaysDropExp; -+ } - // Purpur end - - @Override -diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index e101c3bf425902908c43ffa18867fb83a5e1f16e..382e47f26ee94506cb76463a677351b9bdcf8040 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -35,6 +35,11 @@ public class Salmon extends AbstractSchoolingFish { - return this.level().purpurConfig.salmonTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.salmonAlwaysDropExp; -+ } -+ - @Override - public int getMaxSchoolSize() { - return 5; -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index 35decea07efa8ca9f7ed896be9b8f3eb5afbf082..a1b323ecba25910e97f154e487acc94943117e0c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -149,6 +149,11 @@ public class Sheep extends Animal implements Shearable { - return this.level().purpurConfig.sheepTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.sheepAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.eatBlockGoal = new EatBlockGoal(this); -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index 20f2fd0edf290d448d06e4772b5947fc7865c20b..fc8526af7e1df15794b4560b58e7f6a47508aa08 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -86,6 +86,11 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - this.summoner = summoner; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.snowGolemAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index 57b37ac40b4ffe6c5e27548755aaa15da89a0f0b..88c238e492b1081d1a64a3b6f05d7baa17e5d8c9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -95,6 +95,11 @@ public class Squid extends WaterAnimal { - return this.level().purpurConfig.squidTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.squidAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -index b16d075581a352714f86f1b87805f24c9e336aa3..87b6f6b10ba6e3d9c6a42298a2019a526a183d90 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -@@ -64,6 +64,11 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder - return this.level().purpurConfig.tropicalFishTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.tropicalFishAlwaysDropExp; -+ } -+ - public static String getPredefinedName(int variant) { - return "entity.minecraft.tropical_fish.predefined." + variant; - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index df9a7975959393ab2e81fa7c3878afd034ef7f90..1502f13acd0a104efe470e605826213ea92af191 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -115,6 +115,11 @@ public class Turtle extends Animal { - return this.level().purpurConfig.turtleTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.turtleAlwaysDropExp; -+ } -+ - public void setHomePos(BlockPos pos) { - this.entityData.set(Turtle.HOME_POS, pos.immutable()); // Paper - called with mutablepos... - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -index c967302542eeac0bad33c3b53b7d5b2aea7defbb..64bceae4d06b35fcbecb0daca2496ba30e39d995 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -215,6 +215,11 @@ public class Wolf extends TamableAnimal implements NeutralMob { - return this.level().purpurConfig.wolfTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.wolfAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new FloatGoal(this)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index 6aaf20501558f1710be0adca9c14441448698541..51b475191113cf3cae5e776b0dfbcd0236a92808 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -130,6 +130,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder getModelRotationValues() { - return this.modelRotationValues; -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 83a3a87caba237b382e4409339e98b6b99a005b7..0f8af63a88e425e5e66f68133b6c604caefc6977 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -117,6 +117,11 @@ public class Goat extends Animal { - return this.level().purpurConfig.goatTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.goatAlwaysDropExp; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -index af4bac165cbc39fb6959983a1116a6fb65af0ecb..72ad12175325091397459e06743875cce6df8d94 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java -@@ -47,6 +47,11 @@ public class Donkey extends AbstractChestedHorse { - return this.level().purpurConfig.donkeyTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.donkeyAlwaysDropExp; -+ } -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.DONKEY_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -index f8dbea402f723bf38d7ab3f2468d1b02b7124560..3b1faa63e46a48e83ea672cf6da444a1d7e13270 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java -@@ -72,6 +72,11 @@ public class Horse extends AbstractHorse implements VariantHolder { - return this.level().purpurConfig.horseTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.horseAlwaysDropExp; -+ } -+ - @Override - protected void randomizeAttributes(RandomSource random) { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)generateMaxHealth(random::nextInt)); -diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -index fac46dd905f9a634cff393494f6ff0404eb17fb7..a9f36af3e07f74b3d11697d5fad9f0a469844779 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java -@@ -148,6 +148,11 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (spawnReason == MobSpawnType.SPAWNER || world.canSeeSky(pos)); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -index 91294d87521c884c402485b923691f9fd8985353..f9cddf46d1dbcabc738842ba039daa76bb6f3eb5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -88,6 +88,11 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - return this.level().purpurConfig.illusionerTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.illusionerAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index e0ebc4c2d8dd718ce78d981a1d099e7482221f1f..1ad97267394d3717b1871336193cdc91f3ffb276 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -72,6 +72,11 @@ public class MagmaCube extends Slime { - return this.level().purpurConfig.magmaCubeTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.magmaCubeAlwaysDropExp; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, (double)0.2F); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 8605dfe152a09f31492226b1eb2a5eb39db9fcbc..f71cbaabfff370f019f124203fb947ea7a817d95 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -143,6 +143,11 @@ public class Phantom extends FlyingMob implements Enemy { - return this.level().purpurConfig.phantomTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.phantomAlwaysDropExp; -+ } -+ - @Override - public boolean isFlapping() { - return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -index 1f64dc8442de75447c17ae4fd5483345d4b7fd3e..06d52d8b61abc4dbbdc953bfed2e688be377b3cc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -89,6 +89,11 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - return this.level().purpurConfig.pillagerTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.pillagerAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index 1de85b0a3aafb725452197de245e884cacdb4e3b..0211b50ce4b48a26698f1ece8b593747d170178e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -101,6 +101,11 @@ public class Ravager extends Raider { - return this.level().purpurConfig.ravagerTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.ravagerAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 714adaf017813eb64bb3a9a60b5ff45737e4d95d..3e2c60a301d4c66c85cd8ff812331005a667dc4e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -142,6 +142,11 @@ public class Shulker extends AbstractGolem implements VariantHolder type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos blockPos = pos; - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index 88fc9317aeac79f3352fc8955f8c4f6b3f87eddc..cfa603c7ccaaf1940aa89fa7cd8fafba29529075 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -127,6 +127,11 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - return this.level().purpurConfig.striderBreedingTicks; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.striderAlwaysDropExp; -+ } -+ - public static boolean checkStriderSpawnRules(EntityType type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java -index 3d79723a6b1d5e099d70bb82917ee551d9f2df7e..5db70031c8c9ba901a360c758d51222efe911fcc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -117,6 +117,11 @@ public class Vex extends Monster implements TraceableEntity { - return this.level().purpurConfig.vexTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.vexAlwaysDropExp; -+ } -+ - @Override - protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { - return dimensions.height - 0.28125F; -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index 34a6105c04d107278c18f3b2829282b227c5c5c5..f1e410b602bf0d5520f8e9340f7b8b9e25ca4d39 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -85,6 +85,11 @@ public class Vindicator extends AbstractIllager { - return this.level().purpurConfig.vindicatorTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.vindicatorAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index d49813eeaf417806a16ff7d9b7ef8a9c30cfe8d6..80e90202eae1e12d5261d0bb77ecfadcc9ac4599 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -84,6 +84,11 @@ public class Witch extends Raider implements RangedAttackMob { - return this.level().purpurConfig.witchTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.witchAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - super.registerGoals(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index c6f36715de37c8694860fc3834f203664c264138..2b3decb90aeb99bef42b36f3b8df06cb1172b413 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -62,6 +62,11 @@ public class WitherSkeleton extends AbstractSkeleton { - return this.level().purpurConfig.witherSkeletonTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.witherSkeletonAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 651c4f4b3d9349b8e9d6601a37ca5da192898a61..fc5272e8671ff4bda3bd17abe5c31cc0ff4114f8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -94,6 +94,11 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - return this.level().purpurConfig.zoglinTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.zoglinAlwaysDropExp; -+ } -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index a8d2f8654ee370e98bb1c4e7e1111debd62a7429..7c5efb5582f43d9f333f926ad7dba1ed9920333a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -146,6 +146,11 @@ public class Zombie extends Monster { - return this.level().purpurConfig.zombieTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.zombieAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index b8e0ae0714fd2b77831fb714b1bad901e7081cf4..be2a35f703310f699ee31bd2ec1c938af281a577 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -126,6 +126,11 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - return level().purpurConfig.zombieVillagerJockeyTryExistingChickens; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.zombieVillagerAlwaysDropExp; -+ } -+ - @Override - protected void defineSynchedData() { - super.defineSynchedData(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 21ba6ea58dfbfd9a7c0bf7d518d7f12730841a08..7bb99d7fd8e05805e0cac7bec0b2771990057f58 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -105,6 +105,11 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - return level().purpurConfig.zombifiedPiglinJockeyTryExistingChickens; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.zombifiedPiglinAlwaysDropExp; -+ } -+ - @Override - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 1fd65b47b1bd56bc09902324b1dd22dad2e0bc2d..0ddcff8d4cd22cc01462c412c3db39238237ca49 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -99,6 +99,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return this.level().purpurConfig.hoglinTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.hoglinAlwaysDropExp; -+ } -+ - @Override - public boolean canBeLeashed(Player player) { - return !this.isLeashed(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index 83e9873138fb178511119effbe21449c303a5063..9fb7f6d0532a7419674cc20e4fc6cb4b08c73514 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -123,6 +123,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - return this.level().purpurConfig.piglinTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.piglinAlwaysDropExp; -+ } -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index b63d87df0f48dd63c89118f91b31dc4e3622e3b8..61e8f2d030fc50840c3f80dfb6fc810797ec440f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -68,6 +68,11 @@ public class PiglinBrute extends AbstractPiglin { - return this.level().purpurConfig.piglinBruteTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.piglinBruteAlwaysDropExp; -+ } -+ - public static AttributeSupplier.Builder createAttributes() { - return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 50.0D).add(Attributes.MOVEMENT_SPEED, (double)0.35F).add(Attributes.ATTACK_DAMAGE, 7.0D); - } -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 2ffceda6ce3cfe515f9348dafc28b04689d5a460..903016cc402bc688f4fd92f339b242db6dfe3841 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -194,6 +194,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return this.level().purpurConfig.villagerTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.villagerAlwaysDropExp; -+ } -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 68d263a7647e395d981014f7cc761fe76fc74cb6..970b1f6eaeeddf9928ba61239248ecd2234eda9a 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -98,6 +98,11 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return this.level().purpurConfig.wanderingTraderTakeDamageFromWater; - } - -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.wanderingTraderAlwaysDropExp; -+ } -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0a43e97fa389cdfd3a542c25878f28afb88d15b7..e184a95a4ff3028ffa05fb4dad68c0047d22a638 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1048,12 +1048,14 @@ public class PurpurWorldConfig { - public double axolotlMaxHealth = 14.0D; - public int axolotlBreedingTicks = 6000; - public boolean axolotlTakeDamageFromWater = false; -+ public boolean axolotlAlwaysDropExp = false; - private void axolotlSettings() { - axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); - axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); - axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth); - axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks); - axolotlTakeDamageFromWater = getBoolean("mobs.axolotl.takes-damage-from-water", axolotlTakeDamageFromWater); -+ axolotlAlwaysDropExp = getBoolean("mobs.axolotl.always-drop-exp", axolotlAlwaysDropExp); - } - - public boolean batRidable = false; -@@ -1069,6 +1071,7 @@ public class PurpurWorldConfig { - public double batArmorToughness = 0.0D; - public double batAttackKnockback = 0.0D; - public boolean batTakeDamageFromWater = false; -+ public boolean batAlwaysDropExp = false; - private void batSettings() { - batRidable = getBoolean("mobs.bat.ridable", batRidable); - batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater); -@@ -1088,6 +1091,7 @@ public class PurpurWorldConfig { - batArmorToughness = getDouble("mobs.bat.attributes.armor_toughness", batArmorToughness); - batAttackKnockback = getDouble("mobs.bat.attributes.attack_knockback", batAttackKnockback); - batTakeDamageFromWater = getBoolean("mobs.bat.takes-damage-from-water", batTakeDamageFromWater); -+ batAlwaysDropExp = getBoolean("mobs.bat.always-drop-exp", batAlwaysDropExp); - } - - public boolean beeRidable = false; -@@ -1099,6 +1103,7 @@ public class PurpurWorldConfig { - public boolean beeTakeDamageFromWater = false; - public boolean beeCanWorkAtNight = false; - public boolean beeCanWorkInRain = false; -+ public boolean beeAlwaysDropExp = false; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -1114,6 +1119,7 @@ public class PurpurWorldConfig { - beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater); - beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight); - beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain); -+ beeAlwaysDropExp = getBoolean("mobs.bee.always-drop-exp", beeAlwaysDropExp); - } - - public boolean blazeRidable = false; -@@ -1122,6 +1128,7 @@ public class PurpurWorldConfig { - public double blazeMaxY = 320D; - public double blazeMaxHealth = 20.0D; - public boolean blazeTakeDamageFromWater = true; -+ public boolean blazeAlwaysDropExp = false; - private void blazeSettings() { - blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); - blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); -@@ -1134,6 +1141,7 @@ public class PurpurWorldConfig { - } - blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth); - blazeTakeDamageFromWater = getBoolean("mobs.blaze.takes-damage-from-water", blazeTakeDamageFromWater); -+ blazeAlwaysDropExp = getBoolean("mobs.blaze.always-drop-exp", blazeAlwaysDropExp); - } - - public boolean camelRidableInWater = false; -@@ -1165,6 +1173,7 @@ public class PurpurWorldConfig { - public int catBreedingTicks = 6000; - public DyeColor catDefaultCollarColor = DyeColor.RED; - public boolean catTakeDamageFromWater = false; -+ public boolean catAlwaysDropExp = false; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -1185,6 +1194,7 @@ public class PurpurWorldConfig { - catDefaultCollarColor = DyeColor.RED; - } - catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater); -+ catAlwaysDropExp = getBoolean("mobs.cat.always-drop-exp", catAlwaysDropExp); - } - - public boolean caveSpiderRidable = false; -@@ -1192,6 +1202,7 @@ public class PurpurWorldConfig { - public boolean caveSpiderControllable = true; - public double caveSpiderMaxHealth = 12.0D; - public boolean caveSpiderTakeDamageFromWater = false; -+ public boolean caveSpiderAlwaysDropExp = false; - private void caveSpiderSettings() { - caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); - caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); -@@ -1203,6 +1214,7 @@ public class PurpurWorldConfig { - } - caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth); - caveSpiderTakeDamageFromWater = getBoolean("mobs.cave_spider.takes-damage-from-water", caveSpiderTakeDamageFromWater); -+ caveSpiderAlwaysDropExp = getBoolean("mobs.cave_spider.always-drop-exp", caveSpiderAlwaysDropExp); - } - - public boolean chickenRidable = false; -@@ -1212,6 +1224,7 @@ public class PurpurWorldConfig { - public boolean chickenRetaliate = false; - public int chickenBreedingTicks = 6000; - public boolean chickenTakeDamageFromWater = false; -+ public boolean chickenAlwaysDropExp = false; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -@@ -1225,12 +1238,14 @@ public class PurpurWorldConfig { - chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate); - chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks); - chickenTakeDamageFromWater = getBoolean("mobs.chicken.takes-damage-from-water", chickenTakeDamageFromWater); -+ chickenAlwaysDropExp = getBoolean("mobs.chicken.always-drop-exp", chickenAlwaysDropExp); - } - - public boolean codRidable = false; - public boolean codControllable = true; - public double codMaxHealth = 3.0D; - public boolean codTakeDamageFromWater = false; -+ public boolean codAlwaysDropExp = false; - private void codSettings() { - codRidable = getBoolean("mobs.cod.ridable", codRidable); - codControllable = getBoolean("mobs.cod.controllable", codControllable); -@@ -1241,6 +1256,7 @@ public class PurpurWorldConfig { - } - codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth); - codTakeDamageFromWater = getBoolean("mobs.cod.takes-damage-from-water", codTakeDamageFromWater); -+ codAlwaysDropExp = getBoolean("mobs.cod.always-drop-exp", codAlwaysDropExp); - } - - public boolean cowRidable = false; -@@ -1252,6 +1268,7 @@ public class PurpurWorldConfig { - public boolean cowTakeDamageFromWater = false; - public double cowNaturallyAggressiveToPlayersChance = 0.0D; - public double cowNaturallyAggressiveToPlayersDamage = 2.0D; -+ public boolean cowAlwaysDropExp = false; - private void cowSettings() { - if (PurpurConfig.version < 22) { - double oldValue = getDouble("mobs.cow.naturally-aggressive-to-players-chance", cowNaturallyAggressiveToPlayersChance); -@@ -1272,6 +1289,7 @@ public class PurpurWorldConfig { - cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater); - cowNaturallyAggressiveToPlayersChance = getDouble("mobs.cow.naturally-aggressive-to-players.chance", cowNaturallyAggressiveToPlayersChance); - cowNaturallyAggressiveToPlayersDamage = getDouble("mobs.cow.naturally-aggressive-to-players.damage", cowNaturallyAggressiveToPlayersDamage); -+ cowAlwaysDropExp = getBoolean("mobs.cow.always-drop-exp", cowAlwaysDropExp); - } - - public boolean creeperRidable = false; -@@ -1284,6 +1302,7 @@ public class PurpurWorldConfig { - public boolean creeperTakeDamageFromWater = false; - public boolean creeperExplodeWhenKilled = false; - public boolean creeperHealthRadius = false; -+ public boolean creeperAlwaysDropExp = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -1300,6 +1319,7 @@ public class PurpurWorldConfig { - creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater); - creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled); - creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius); -+ creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp); - } - - public boolean dolphinRidable = false; -@@ -1311,6 +1331,7 @@ public class PurpurWorldConfig { - public boolean dolphinDisableTreasureSearching = false; - public boolean dolphinTakeDamageFromWater = false; - public double dolphinNaturallyAggressiveToPlayersChance = 0.0D; -+ public boolean dolphinAlwaysDropExp = false; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -1326,6 +1347,7 @@ public class PurpurWorldConfig { - dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching); - dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater); - dolphinNaturallyAggressiveToPlayersChance = getDouble("mobs.dolphin.naturally-aggressive-to-players-chance", dolphinNaturallyAggressiveToPlayersChance); -+ dolphinAlwaysDropExp = getBoolean("mobs.dolphin.always-drop-exp", dolphinAlwaysDropExp); - } - - public boolean donkeyRidableInWater = false; -@@ -1337,6 +1359,7 @@ public class PurpurWorldConfig { - public double donkeyMovementSpeedMax = 0.175D; - public int donkeyBreedingTicks = 6000; - public boolean donkeyTakeDamageFromWater = false; -+ public boolean donkeyAlwaysDropExp = false; - private void donkeySettings() { - donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1354,6 +1377,7 @@ public class PurpurWorldConfig { - donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax); - donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks); - donkeyTakeDamageFromWater = getBoolean("mobs.donkey.takes-damage-from-water", donkeyTakeDamageFromWater); -+ donkeyAlwaysDropExp = getBoolean("mobs.donkey.always-drop-exp", donkeyAlwaysDropExp); - } - - public boolean drownedRidable = false; -@@ -1366,6 +1390,7 @@ public class PurpurWorldConfig { - public boolean drownedJockeyTryExistingChickens = true; - public boolean drownedTakeDamageFromWater = false; - public boolean drownedBreakDoors = false; -+ public boolean drownedAlwaysDropExp = false; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -@@ -1382,12 +1407,14 @@ public class PurpurWorldConfig { - drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens); - drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater); - drownedBreakDoors = getBoolean("mobs.drowned.can-break-doors", drownedBreakDoors); -+ drownedAlwaysDropExp = getBoolean("mobs.drowned.always-drop-exp", drownedAlwaysDropExp); - } - - public boolean elderGuardianRidable = false; - public boolean elderGuardianControllable = true; - public double elderGuardianMaxHealth = 80.0D; - public boolean elderGuardianTakeDamageFromWater = false; -+ public boolean elderGuardianAlwaysDropExp = false; - private void elderGuardianSettings() { - elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); - elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); -@@ -1398,6 +1425,7 @@ public class PurpurWorldConfig { - } - elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth); - elderGuardianTakeDamageFromWater = getBoolean("mobs.elder_guardian.takes-damage-from-water", elderGuardianTakeDamageFromWater); -+ elderGuardianAlwaysDropExp = getBoolean("mobs.elder_guardian.always-drop-exp", elderGuardianAlwaysDropExp); - } - - public boolean enderDragonRidable = false; -@@ -1443,6 +1471,7 @@ public class PurpurWorldConfig { - public boolean endermanIgnorePlayerDragonHead = false; - public boolean endermanDisableStareAggro = false; - public boolean endermanIgnoreProjectiles = false; -+ public boolean endermanAlwaysDropExp = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -1466,6 +1495,7 @@ public class PurpurWorldConfig { - endermanIgnorePlayerDragonHead = getBoolean("mobs.enderman.ignore-players-wearing-dragon-head", endermanIgnorePlayerDragonHead); - endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro); - endermanIgnoreProjectiles = getBoolean("mobs.enderman.ignore-projectiles", endermanIgnoreProjectiles); -+ endermanAlwaysDropExp = getBoolean("mobs.enderman.always-drop-exp", endermanAlwaysDropExp); - } - - public boolean endermiteRidable = false; -@@ -1473,6 +1503,7 @@ public class PurpurWorldConfig { - public boolean endermiteControllable = true; - public double endermiteMaxHealth = 8.0D; - public boolean endermiteTakeDamageFromWater = false; -+ public boolean endermiteAlwaysDropExp = false; - private void endermiteSettings() { - endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); - endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); -@@ -1484,6 +1515,7 @@ public class PurpurWorldConfig { - } - endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth); - endermiteTakeDamageFromWater = getBoolean("mobs.endermite.takes-damage-from-water", endermiteTakeDamageFromWater); -+ endermiteAlwaysDropExp = getBoolean("mobs.endermite.always-drop-exp", endermiteAlwaysDropExp); - } - - public boolean evokerRidable = false; -@@ -1492,6 +1524,7 @@ public class PurpurWorldConfig { - public double evokerMaxHealth = 24.0D; - public boolean evokerBypassMobGriefing = false; - public boolean evokerTakeDamageFromWater = false; -+ public boolean evokerAlwaysDropExp = false; - private void evokerSettings() { - evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); - evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); -@@ -1504,6 +1537,7 @@ public class PurpurWorldConfig { - evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); - evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing); - evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater); -+ evokerAlwaysDropExp = getBoolean("mobs.evoker.always-drop-exp", evokerAlwaysDropExp); - } - - public boolean foxRidable = false; -@@ -1514,6 +1548,7 @@ public class PurpurWorldConfig { - public int foxBreedingTicks = 6000; - public boolean foxBypassMobGriefing = false; - public boolean foxTakeDamageFromWater = false; -+ public boolean foxAlwaysDropExp = false; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -1528,6 +1563,7 @@ public class PurpurWorldConfig { - foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks); - foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing); - foxTakeDamageFromWater = getBoolean("mobs.fox.takes-damage-from-water", foxTakeDamageFromWater); -+ foxAlwaysDropExp = getBoolean("mobs.fox.always-drop-exp", foxAlwaysDropExp); - } - - public boolean frogRidable = false; -@@ -1549,6 +1585,7 @@ public class PurpurWorldConfig { - public double ghastMaxY = 320D; - public double ghastMaxHealth = 10.0D; - public boolean ghastTakeDamageFromWater = false; -+ public boolean ghastAlwaysDropExp = false; - private void ghastSettings() { - ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); - ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); -@@ -1561,6 +1598,7 @@ public class PurpurWorldConfig { - } - ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth); - ghastTakeDamageFromWater = getBoolean("mobs.ghast.takes-damage-from-water", ghastTakeDamageFromWater); -+ ghastAlwaysDropExp = getBoolean("mobs.ghast.always-drop-exp", ghastAlwaysDropExp); - } - - public boolean giantRidable = false; -@@ -1574,6 +1612,7 @@ public class PurpurWorldConfig { - public boolean giantHaveAI = false; - public boolean giantHaveHostileAI = false; - public boolean giantTakeDamageFromWater = false; -+ public boolean giantAlwaysDropExp = false; - private void giantSettings() { - giantRidable = getBoolean("mobs.giant.ridable", giantRidable); - giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); -@@ -1595,6 +1634,7 @@ public class PurpurWorldConfig { - giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI); - giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI); - giantTakeDamageFromWater = getBoolean("mobs.giant.takes-damage-from-water", giantTakeDamageFromWater); -+ giantAlwaysDropExp = getBoolean("mobs.giant.always-drop-exp", giantAlwaysDropExp); - } - - public boolean glowSquidRidable = false; -@@ -1602,12 +1642,14 @@ public class PurpurWorldConfig { - public double glowSquidMaxHealth = 10.0D; - public boolean glowSquidsCanFly = false; - public boolean glowSquidTakeDamageFromWater = false; -+ public boolean glowSquidAlwaysDropExp = false; - private void glowSquidSettings() { - glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); - glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); - glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth); - glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly); - glowSquidTakeDamageFromWater = getBoolean("mobs.glow_squid.takes-damage-from-water", glowSquidTakeDamageFromWater); -+ glowSquidAlwaysDropExp = getBoolean("mobs.glow_squid.always-drop-exp", glowSquidAlwaysDropExp); - } - - public boolean goatRidable = false; -@@ -1616,6 +1658,7 @@ public class PurpurWorldConfig { - public double goatMaxHealth = 10.0D; - public int goatBreedingTicks = 6000; - public boolean goatTakeDamageFromWater = false; -+ public boolean goatAlwaysDropExp = false; - private void goatSettings() { - goatRidable = getBoolean("mobs.goat.ridable", goatRidable); - goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); -@@ -1623,12 +1666,14 @@ public class PurpurWorldConfig { - goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth); - goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks); - goatTakeDamageFromWater = getBoolean("mobs.goat.takes-damage-from-water", goatTakeDamageFromWater); -+ goatAlwaysDropExp = getBoolean("mobs.goat.always-drop-exp", goatAlwaysDropExp); - } - - public boolean guardianRidable = false; - public boolean guardianControllable = true; - public double guardianMaxHealth = 30.0D; - public boolean guardianTakeDamageFromWater = false; -+ public boolean guardianAlwaysDropExp = false; - private void guardianSettings() { - guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); - guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); -@@ -1639,6 +1684,7 @@ public class PurpurWorldConfig { - } - guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth); - guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater); -+ guardianAlwaysDropExp = getBoolean("mobs.guardian.always-drop-exp", guardianAlwaysDropExp); - } - - public boolean forceHalloweenSeason = false; -@@ -1654,6 +1700,7 @@ public class PurpurWorldConfig { - public double hoglinMaxHealth = 40.0D; - public int hoglinBreedingTicks = 6000; - public boolean hoglinTakeDamageFromWater = false; -+ public boolean hoglinAlwaysDropExp = false; - private void hoglinSettings() { - hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); - hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); -@@ -1666,6 +1713,7 @@ public class PurpurWorldConfig { - hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth); - hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks); - hoglinTakeDamageFromWater = getBoolean("mobs.hoglin.takes-damage-from-water", hoglinTakeDamageFromWater); -+ hoglinAlwaysDropExp = getBoolean("mobs.hoglin.always-drop-exp", hoglinAlwaysDropExp); - } - - public boolean horseRidableInWater = false; -@@ -1677,6 +1725,7 @@ public class PurpurWorldConfig { - public double horseMovementSpeedMax = 0.3375D; - public int horseBreedingTicks = 6000; - public boolean horseTakeDamageFromWater = false; -+ public boolean horseAlwaysDropExp = false; - private void horseSettings() { - horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1694,6 +1743,7 @@ public class PurpurWorldConfig { - horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax); - horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks); - horseTakeDamageFromWater = getBoolean("mobs.horse.takes-damage-from-water", horseTakeDamageFromWater); -+ horseAlwaysDropExp = getBoolean("mobs.horse.always-drop-exp", horseAlwaysDropExp); - } - - public boolean huskRidable = false; -@@ -1705,6 +1755,7 @@ public class PurpurWorldConfig { - public double huskJockeyChance = 0.05D; - public boolean huskJockeyTryExistingChickens = true; - public boolean huskTakeDamageFromWater = false; -+ public boolean huskAlwaysDropExp = false; - private void huskSettings() { - huskRidable = getBoolean("mobs.husk.ridable", huskRidable); - huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); -@@ -1720,6 +1771,7 @@ public class PurpurWorldConfig { - huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance); - huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens); - huskTakeDamageFromWater = getBoolean("mobs.husk.takes-damage-from-water", huskTakeDamageFromWater); -+ huskAlwaysDropExp = getBoolean("mobs.husk.always-drop-exp", huskAlwaysDropExp); - } - - public boolean illusionerRidable = false; -@@ -1729,6 +1781,7 @@ public class PurpurWorldConfig { - public double illusionerFollowRange = 18.0D; - public double illusionerMaxHealth = 32.0D; - public boolean illusionerTakeDamageFromWater = false; -+ public boolean illusionerAlwaysDropExp = false; - private void illusionerSettings() { - illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); - illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); -@@ -1746,6 +1799,7 @@ public class PurpurWorldConfig { - } - illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth); - illusionerTakeDamageFromWater = getBoolean("mobs.illusioner.takes-damage-from-water", illusionerTakeDamageFromWater); -+ illusionerAlwaysDropExp = getBoolean("mobs.illusioner.always-drop-exp", illusionerAlwaysDropExp); - } - - public boolean ironGolemRidable = false; -@@ -1756,6 +1810,7 @@ public class PurpurWorldConfig { - public boolean ironGolemTakeDamageFromWater = false; - public boolean ironGolemPoppyCalm = false; - public boolean ironGolemHealCalm = false; -+ public boolean ironGolemAlwaysDropExp = false; - private void ironGolemSettings() { - ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); - ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); -@@ -1770,6 +1825,7 @@ public class PurpurWorldConfig { - ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater); - ironGolemPoppyCalm = getBoolean("mobs.iron_golem.poppy-calms-anger", ironGolemPoppyCalm); - ironGolemHealCalm = getBoolean("mobs.iron_golem.healing-calms-anger", ironGolemHealCalm); -+ ironGolemAlwaysDropExp = getBoolean("mobs.iron_golem.always-drop-exp", ironGolemAlwaysDropExp); - } - - public boolean llamaRidable = false; -@@ -1784,6 +1840,7 @@ public class PurpurWorldConfig { - public int llamaBreedingTicks = 6000; - public boolean llamaTakeDamageFromWater = false; - public boolean llamaJoinCaravans = true; -+ public boolean llamaAlwaysDropExp = false; - private void llamaSettings() { - llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); - llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); -@@ -1804,6 +1861,7 @@ public class PurpurWorldConfig { - llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks); - llamaTakeDamageFromWater = getBoolean("mobs.llama.takes-damage-from-water", llamaTakeDamageFromWater); - llamaJoinCaravans = getBoolean("mobs.llama.join-caravans", llamaJoinCaravans); -+ llamaAlwaysDropExp = getBoolean("mobs.llama.always-drop-exp", llamaAlwaysDropExp); - } - - public boolean magmaCubeRidable = false; -@@ -1814,6 +1872,7 @@ public class PurpurWorldConfig { - public Map magmaCubeMaxHealthCache = new HashMap<>(); - public Map magmaCubeAttackDamageCache = new HashMap<>(); - public boolean magmaCubeTakeDamageFromWater = false; -+ public boolean magmaCubeAlwaysDropExp = false; - private void magmaCubeSettings() { - magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable); - magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater); -@@ -1828,6 +1887,7 @@ public class PurpurWorldConfig { - magmaCubeMaxHealthCache.clear(); - magmaCubeAttackDamageCache.clear(); - magmaCubeTakeDamageFromWater = getBoolean("mobs.magma_cube.takes-damage-from-water", magmaCubeTakeDamageFromWater); -+ magmaCubeAlwaysDropExp = getBoolean("mobs.magma_cube.always-drop-exp", magmaCubeAlwaysDropExp); - } - - public boolean mooshroomRidable = false; -@@ -1836,6 +1896,7 @@ public class PurpurWorldConfig { - public double mooshroomMaxHealth = 10.0D; - public int mooshroomBreedingTicks = 6000; - public boolean mooshroomTakeDamageFromWater = false; -+ public boolean mooshroomAlwaysDropExp = false; - private void mooshroomSettings() { - mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); - mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); -@@ -1848,6 +1909,7 @@ public class PurpurWorldConfig { - mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth); - mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks); - mooshroomTakeDamageFromWater = getBoolean("mobs.mooshroom.takes-damage-from-water", mooshroomTakeDamageFromWater); -+ mooshroomAlwaysDropExp = getBoolean("mobs.mooshroom.always-drop-exp", mooshroomAlwaysDropExp); - } - - public boolean muleRidableInWater = false; -@@ -1859,6 +1921,7 @@ public class PurpurWorldConfig { - public double muleMovementSpeedMax = 0.175D; - public int muleBreedingTicks = 6000; - public boolean muleTakeDamageFromWater = false; -+ public boolean muleAlwaysDropExp = false; - private void muleSettings() { - muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); - if (PurpurConfig.version < 10) { -@@ -1876,6 +1939,7 @@ public class PurpurWorldConfig { - muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax); - muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks); - muleTakeDamageFromWater = getBoolean("mobs.mule.takes-damage-from-water", muleTakeDamageFromWater); -+ muleAlwaysDropExp = getBoolean("mobs.mule.always-drop-exp", muleAlwaysDropExp); - } - - public boolean ocelotRidable = false; -@@ -1884,6 +1948,7 @@ public class PurpurWorldConfig { - public double ocelotMaxHealth = 10.0D; - public int ocelotBreedingTicks = 6000; - public boolean ocelotTakeDamageFromWater = false; -+ public boolean ocelotAlwaysDropExp = false; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -@@ -1896,6 +1961,7 @@ public class PurpurWorldConfig { - ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth); - ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks); - ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater); -+ ocelotAlwaysDropExp = getBoolean("mobs.ocelot.always-drop-exp", ocelotAlwaysDropExp); - } - - public boolean pandaRidable = false; -@@ -1904,6 +1970,7 @@ public class PurpurWorldConfig { - public double pandaMaxHealth = 20.0D; - public int pandaBreedingTicks = 6000; - public boolean pandaTakeDamageFromWater = false; -+ public boolean pandaAlwaysDropExp = false; - private void pandaSettings() { - pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); - pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); -@@ -1916,6 +1983,7 @@ public class PurpurWorldConfig { - pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth); - pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks); - pandaTakeDamageFromWater = getBoolean("mobs.panda.takes-damage-from-water", pandaTakeDamageFromWater); -+ pandaAlwaysDropExp = getBoolean("mobs.panda.always-drop-exp", pandaAlwaysDropExp); - } - - public boolean parrotRidable = false; -@@ -1925,6 +1993,7 @@ public class PurpurWorldConfig { - public double parrotMaxHealth = 6.0D; - public boolean parrotTakeDamageFromWater = false; - public boolean parrotBreedable = false; -+ public boolean parrotAlwaysDropExp = false; - private void parrotSettings() { - parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); - parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); -@@ -1938,6 +2007,7 @@ public class PurpurWorldConfig { - parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth); - parrotTakeDamageFromWater = getBoolean("mobs.parrot.takes-damage-from-water", parrotTakeDamageFromWater); - parrotBreedable = getBoolean("mobs.parrot.can-breed", parrotBreedable); -+ parrotAlwaysDropExp = getBoolean("mobs.parrot.always-drop-exp", parrotAlwaysDropExp); - } - - public boolean phantomRidable = false; -@@ -1965,6 +2035,7 @@ public class PurpurWorldConfig { - public boolean phantomBurnInDaylight = true; - public boolean phantomFlamesOnSwoop = false; - public boolean phantomTakeDamageFromWater = false; -+ public boolean phantomAlwaysDropExp = false; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -2000,6 +2071,7 @@ public class PurpurWorldConfig { - phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch); - phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop); - phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater); -+ phantomAlwaysDropExp = getBoolean("mobs.phantom.always-drop-exp", phantomAlwaysDropExp); - } - - public boolean pigRidable = false; -@@ -2009,6 +2081,7 @@ public class PurpurWorldConfig { - public boolean pigGiveSaddleBack = false; - public int pigBreedingTicks = 6000; - public boolean pigTakeDamageFromWater = false; -+ public boolean pigAlwaysDropExp = false; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -@@ -2022,6 +2095,7 @@ public class PurpurWorldConfig { - pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack); - pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks); - pigTakeDamageFromWater = getBoolean("mobs.pig.takes-damage-from-water", pigTakeDamageFromWater); -+ pigAlwaysDropExp = getBoolean("mobs.pig.always-drop-exp", pigAlwaysDropExp); - } - - public boolean piglinRidable = false; -@@ -2031,6 +2105,7 @@ public class PurpurWorldConfig { - public boolean piglinBypassMobGriefing = false; - public boolean piglinTakeDamageFromWater = false; - public int piglinPortalSpawnModifier = 2000; -+ public boolean piglinAlwaysDropExp = false; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -2044,6 +2119,7 @@ public class PurpurWorldConfig { - piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); - piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); - piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier); -+ piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp); - } - - public boolean piglinBruteRidable = false; -@@ -2051,6 +2127,7 @@ public class PurpurWorldConfig { - public boolean piglinBruteControllable = true; - public double piglinBruteMaxHealth = 50.0D; - public boolean piglinBruteTakeDamageFromWater = false; -+ public boolean piglinBruteAlwaysDropExp = false; - private void piglinBruteSettings() { - piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); - piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); -@@ -2062,6 +2139,7 @@ public class PurpurWorldConfig { - } - piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth); - piglinBruteTakeDamageFromWater = getBoolean("mobs.piglin_brute.takes-damage-from-water", piglinBruteTakeDamageFromWater); -+ piglinBruteAlwaysDropExp = getBoolean("mobs.piglin_brute.always-drop-exp", piglinBruteAlwaysDropExp); - } - - public boolean pillagerRidable = false; -@@ -2070,6 +2148,7 @@ public class PurpurWorldConfig { - public double pillagerMaxHealth = 24.0D; - public boolean pillagerBypassMobGriefing = false; - public boolean pillagerTakeDamageFromWater = false; -+ public boolean pillagerAlwaysDropExp = false; - private void pillagerSettings() { - pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); - pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); -@@ -2082,6 +2161,7 @@ public class PurpurWorldConfig { - pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); - pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing); - pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater); -+ pillagerAlwaysDropExp = getBoolean("mobs.pillager.always-drop-exp", pillagerAlwaysDropExp); - } - - public boolean polarBearRidable = false; -@@ -2092,6 +2172,7 @@ public class PurpurWorldConfig { - public Item polarBearBreedableItem = null; - public int polarBearBreedingTicks = 6000; - public boolean polarBearTakeDamageFromWater = false; -+ public boolean polarBearAlwaysDropExp = false; - private void polarBearSettings() { - polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); - polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); -@@ -2107,12 +2188,14 @@ public class PurpurWorldConfig { - if (item != Items.AIR) polarBearBreedableItem = item; - polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks); - polarBearTakeDamageFromWater = getBoolean("mobs.polar_bear.takes-damage-from-water", polarBearTakeDamageFromWater); -+ polarBearAlwaysDropExp = getBoolean("mobs.polar_bear.always-drop-exp", polarBearAlwaysDropExp); - } - - public boolean pufferfishRidable = false; - public boolean pufferfishControllable = true; - public double pufferfishMaxHealth = 3.0D; - public boolean pufferfishTakeDamageFromWater = false; -+ public boolean pufferfishAlwaysDropExp = false; - private void pufferfishSettings() { - pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); - pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); -@@ -2123,6 +2206,7 @@ public class PurpurWorldConfig { - } - pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth); - pufferfishTakeDamageFromWater = getBoolean("mobs.pufferfish.takes-damage-from-water", pufferfishTakeDamageFromWater); -+ pufferfishAlwaysDropExp = getBoolean("mobs.pufferfish.always-drop-exp", pufferfishAlwaysDropExp); - } - - public boolean rabbitRidable = false; -@@ -2134,6 +2218,7 @@ public class PurpurWorldConfig { - public int rabbitBreedingTicks = 6000; - public boolean rabbitBypassMobGriefing = false; - public boolean rabbitTakeDamageFromWater = false; -+ public boolean rabbitAlwaysDropExp = false; - private void rabbitSettings() { - rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); - rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); -@@ -2149,6 +2234,7 @@ public class PurpurWorldConfig { - rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks); - rabbitBypassMobGriefing = getBoolean("mobs.rabbit.bypass-mob-griefing", rabbitBypassMobGriefing); - rabbitTakeDamageFromWater = getBoolean("mobs.rabbit.takes-damage-from-water", rabbitTakeDamageFromWater); -+ rabbitAlwaysDropExp = getBoolean("mobs.rabbit.always-drop-exp", rabbitAlwaysDropExp); - } - - public boolean ravagerRidable = false; -@@ -2158,6 +2244,7 @@ public class PurpurWorldConfig { - public boolean ravagerBypassMobGriefing = false; - public boolean ravagerTakeDamageFromWater = false; - public List ravagerGriefableBlocks = new ArrayList<>(); -+ public boolean ravagerAlwaysDropExp = false; - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -@@ -2187,12 +2274,14 @@ public class PurpurWorldConfig { - ravagerGriefableBlocks.add(block); - } - }); -+ ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp); - } - - public boolean salmonRidable = false; - public boolean salmonControllable = true; - public double salmonMaxHealth = 3.0D; - public boolean salmonTakeDamageFromWater = false; -+ public boolean salmonAlwaysDropExp = false; - private void salmonSettings() { - salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); - salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); -@@ -2203,6 +2292,7 @@ public class PurpurWorldConfig { - } - salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth); - salmonTakeDamageFromWater = getBoolean("mobs.salmon.takes-damage-from-water", salmonTakeDamageFromWater); -+ salmonAlwaysDropExp = getBoolean("mobs.salmon.always-drop-exp", salmonAlwaysDropExp); - } - - public boolean sheepRidable = false; -@@ -2212,6 +2302,7 @@ public class PurpurWorldConfig { - public int sheepBreedingTicks = 6000; - public boolean sheepBypassMobGriefing = false; - public boolean sheepTakeDamageFromWater = false; -+ public boolean sheepAlwaysDropExp = false; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -2225,6 +2316,7 @@ public class PurpurWorldConfig { - sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks); - sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing); - sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater); -+ sheepAlwaysDropExp = getBoolean("mobs.sheep.always-drop-exp", sheepAlwaysDropExp); - } - - public boolean shulkerRidable = false; -@@ -2238,6 +2330,7 @@ public class PurpurWorldConfig { - public String shulkerSpawnFromBulletNearbyEquation = "(nearby - 1) / 5.0"; - public boolean shulkerSpawnFromBulletRandomColor = false; - public boolean shulkerChangeColorWithDye = false; -+ public boolean shulkerAlwaysDropExp = false; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -@@ -2255,6 +2348,7 @@ public class PurpurWorldConfig { - shulkerSpawnFromBulletNearbyEquation = getString("mobs.shulker.spawn-from-bullet.nearby-equation", shulkerSpawnFromBulletNearbyEquation); - shulkerSpawnFromBulletRandomColor = getBoolean("mobs.shulker.spawn-from-bullet.random-color", shulkerSpawnFromBulletRandomColor); - shulkerChangeColorWithDye = getBoolean("mobs.shulker.change-color-with-dye", shulkerChangeColorWithDye); -+ shulkerAlwaysDropExp = getBoolean("mobs.shulker.always-drop-exp", shulkerAlwaysDropExp); - } - - public boolean silverfishRidable = false; -@@ -2263,6 +2357,7 @@ public class PurpurWorldConfig { - public double silverfishMaxHealth = 8.0D; - public boolean silverfishBypassMobGriefing = false; - public boolean silverfishTakeDamageFromWater = false; -+ public boolean silverfishAlwaysDropExp = false; - private void silverfishSettings() { - silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); - silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); -@@ -2275,6 +2370,7 @@ public class PurpurWorldConfig { - silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth); - silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing); - silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater); -+ silverfishAlwaysDropExp = getBoolean("mobs.silverfish.always-drop-exp", silverfishAlwaysDropExp); - } - - public boolean skeletonRidable = false; -@@ -2282,6 +2378,7 @@ public class PurpurWorldConfig { - public boolean skeletonControllable = true; - public double skeletonMaxHealth = 20.0D; - public boolean skeletonTakeDamageFromWater = false; -+ public boolean skeletonAlwaysDropExp = false; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -2293,6 +2390,7 @@ public class PurpurWorldConfig { - } - skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth); - skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater); -+ skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp); - } - - public boolean skeletonHorseRidableInWater = true; -@@ -2304,6 +2402,7 @@ public class PurpurWorldConfig { - public double skeletonHorseMovementSpeedMin = 0.2D; - public double skeletonHorseMovementSpeedMax = 0.2D; - public boolean skeletonHorseTakeDamageFromWater = false; -+ public boolean skeletonHorseAlwaysDropExp = false; - private void skeletonHorseSettings() { - skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); - skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim); -@@ -2320,6 +2419,7 @@ public class PurpurWorldConfig { - skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin); - skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax); - skeletonHorseTakeDamageFromWater = getBoolean("mobs.skeleton_horse.takes-damage-from-water", skeletonHorseTakeDamageFromWater); -+ skeletonHorseAlwaysDropExp = getBoolean("mobs.skeleton_horse.always-drop-exp", skeletonHorseAlwaysDropExp); - } - - public boolean slimeRidable = false; -@@ -2330,6 +2430,7 @@ public class PurpurWorldConfig { - public Map slimeMaxHealthCache = new HashMap<>(); - public Map slimeAttackDamageCache = new HashMap<>(); - public boolean slimeTakeDamageFromWater = false; -+ public boolean slimeAlwaysDropExp = false; - private void slimeSettings() { - slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable); - slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater); -@@ -2344,6 +2445,7 @@ public class PurpurWorldConfig { - slimeMaxHealthCache.clear(); - slimeAttackDamageCache.clear(); - slimeTakeDamageFromWater = getBoolean("mobs.slime.takes-damage-from-water", slimeTakeDamageFromWater); -+ slimeAlwaysDropExp = getBoolean("mobs.slime.always-drop-exp", slimeAlwaysDropExp); - } - - public boolean snowGolemRidable = false; -@@ -2359,6 +2461,7 @@ public class PurpurWorldConfig { - public double snowGolemAttackDistance = 1.25D; - public boolean snowGolemBypassMobGriefing = false; - public boolean snowGolemTakeDamageFromWater = true; -+ public boolean snowGolemAlwaysDropExp = false; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -2378,6 +2481,7 @@ public class PurpurWorldConfig { - snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance); - snowGolemBypassMobGriefing = getBoolean("mobs.snow_golem.bypass-mob-griefing", snowGolemBypassMobGriefing); - snowGolemTakeDamageFromWater = getBoolean("mobs.snow_golem.takes-damage-from-water", snowGolemTakeDamageFromWater); -+ snowGolemAlwaysDropExp = getBoolean("mobs.snow_golem.always-drop-exp", snowGolemAlwaysDropExp); - } - - public boolean snifferRidable = false; -@@ -2400,6 +2504,7 @@ public class PurpurWorldConfig { - public double squidOffsetWaterCheck = 0.0D; - public boolean squidsCanFly = false; - public boolean squidTakeDamageFromWater = false; -+ public boolean squidAlwaysDropExp = false; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -2413,6 +2518,7 @@ public class PurpurWorldConfig { - squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck); - squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly); - squidTakeDamageFromWater = getBoolean("mobs.squid.takes-damage-from-water", squidTakeDamageFromWater); -+ squidAlwaysDropExp = getBoolean("mobs.squid.always-drop-exp", squidAlwaysDropExp); - } - - public boolean spiderRidable = false; -@@ -2420,6 +2526,7 @@ public class PurpurWorldConfig { - public boolean spiderControllable = true; - public double spiderMaxHealth = 16.0D; - public boolean spiderTakeDamageFromWater = false; -+ public boolean spiderAlwaysDropExp = false; - private void spiderSettings() { - spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); - spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); -@@ -2431,6 +2538,7 @@ public class PurpurWorldConfig { - } - spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth); - spiderTakeDamageFromWater = getBoolean("mobs.spider.takes-damage-from-water", spiderTakeDamageFromWater); -+ spiderAlwaysDropExp = getBoolean("mobs.spider.always-drop-exp", spiderAlwaysDropExp); - } - - public boolean strayRidable = false; -@@ -2438,6 +2546,7 @@ public class PurpurWorldConfig { - public boolean strayControllable = true; - public double strayMaxHealth = 20.0D; - public boolean strayTakeDamageFromWater = false; -+ public boolean strayAlwaysDropExp = false; - private void straySettings() { - strayRidable = getBoolean("mobs.stray.ridable", strayRidable); - strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); -@@ -2449,6 +2558,7 @@ public class PurpurWorldConfig { - } - strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth); - strayTakeDamageFromWater = getBoolean("mobs.stray.takes-damage-from-water", strayTakeDamageFromWater); -+ strayAlwaysDropExp = getBoolean("mobs.stray.always-drop-exp", strayAlwaysDropExp); - } - - public boolean striderRidable = false; -@@ -2458,6 +2568,7 @@ public class PurpurWorldConfig { - public int striderBreedingTicks = 6000; - public boolean striderGiveSaddleBack = false; - public boolean striderTakeDamageFromWater = true; -+ public boolean striderAlwaysDropExp = false; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -@@ -2471,6 +2582,7 @@ public class PurpurWorldConfig { - striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks); - striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack); - striderTakeDamageFromWater = getBoolean("mobs.strider.takes-damage-from-water", striderTakeDamageFromWater); -+ striderAlwaysDropExp = getBoolean("mobs.strider.always-drop-exp", striderAlwaysDropExp); - } - - public boolean tadpoleRidable = false; -@@ -2493,6 +2605,7 @@ public class PurpurWorldConfig { - public double traderLlamaMovementSpeedMax = 0.175D; - public int traderLlamaBreedingTicks = 6000; - public boolean traderLlamaTakeDamageFromWater = false; -+ public boolean traderLlamaAlwaysDropExp = false; - private void traderLlamaSettings() { - traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); - traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); -@@ -2512,12 +2625,14 @@ public class PurpurWorldConfig { - traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax); - traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks); - traderLlamaTakeDamageFromWater = getBoolean("mobs.trader_llama.takes-damage-from-water", traderLlamaTakeDamageFromWater); -+ traderLlamaAlwaysDropExp = getBoolean("mobs.trader_llama.always-drop-exp", traderLlamaAlwaysDropExp); - } - - public boolean tropicalFishRidable = false; - public boolean tropicalFishControllable = true; - public double tropicalFishMaxHealth = 3.0D; - public boolean tropicalFishTakeDamageFromWater = false; -+ public boolean tropicalFishAlwaysDropExp = false; - private void tropicalFishSettings() { - tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); - tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); -@@ -2528,6 +2643,7 @@ public class PurpurWorldConfig { - } - tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth); - tropicalFishTakeDamageFromWater = getBoolean("mobs.tropical_fish.takes-damage-from-water", tropicalFishTakeDamageFromWater); -+ tropicalFishAlwaysDropExp = getBoolean("mobs.tropical_fish.always-drop-exp", tropicalFishAlwaysDropExp); - } - - public boolean turtleRidable = false; -@@ -2536,6 +2652,7 @@ public class PurpurWorldConfig { - public double turtleMaxHealth = 30.0D; - public int turtleBreedingTicks = 6000; - public boolean turtleTakeDamageFromWater = false; -+ public boolean turtleAlwaysDropExp = false; - private void turtleSettings() { - turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); - turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); -@@ -2548,6 +2665,7 @@ public class PurpurWorldConfig { - turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth); - turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks); - turtleTakeDamageFromWater = getBoolean("mobs.turtle.takes-damage-from-water", turtleTakeDamageFromWater); -+ turtleAlwaysDropExp = getBoolean("mobs.turtle.always-drop-exp", turtleAlwaysDropExp); - } - - public boolean vexRidable = false; -@@ -2556,6 +2674,7 @@ public class PurpurWorldConfig { - public double vexMaxY = 320D; - public double vexMaxHealth = 14.0D; - public boolean vexTakeDamageFromWater = false; -+ public boolean vexAlwaysDropExp = false; - private void vexSettings() { - vexRidable = getBoolean("mobs.vex.ridable", vexRidable); - vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); -@@ -2568,6 +2687,7 @@ public class PurpurWorldConfig { - } - vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth); - vexTakeDamageFromWater = getBoolean("mobs.vex.takes-damage-from-water", vexTakeDamageFromWater); -+ vexAlwaysDropExp = getBoolean("mobs.vex.always-drop-exp", vexAlwaysDropExp); - } - - public boolean villagerRidable = false; -@@ -2583,6 +2703,7 @@ public class PurpurWorldConfig { - public boolean villagerBypassMobGriefing = false; - public boolean villagerTakeDamageFromWater = false; - public boolean villagerAllowTrading = true; -+ public boolean villagerAlwaysDropExp = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2602,6 +2723,7 @@ public class PurpurWorldConfig { - villagerBypassMobGriefing = getBoolean("mobs.villager.bypass-mob-griefing", villagerBypassMobGriefing); - villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater); - villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); -+ villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp); - } - - public boolean vindicatorRidable = false; -@@ -2610,6 +2732,7 @@ public class PurpurWorldConfig { - public double vindicatorMaxHealth = 24.0D; - public double vindicatorJohnnySpawnChance = 0D; - public boolean vindicatorTakeDamageFromWater = false; -+ public boolean vindicatorAlwaysDropExp = false; - private void vindicatorSettings() { - vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); - vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); -@@ -2622,6 +2745,7 @@ public class PurpurWorldConfig { - vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth); - vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance); - vindicatorTakeDamageFromWater = getBoolean("mobs.vindicator.takes-damage-from-water", vindicatorTakeDamageFromWater); -+ vindicatorAlwaysDropExp = getBoolean("mobs.vindicator.always-drop-exp", vindicatorAlwaysDropExp); - } - - public boolean wanderingTraderRidable = false; -@@ -2632,6 +2756,7 @@ public class PurpurWorldConfig { - public boolean wanderingTraderCanBeLeashed = false; - public boolean wanderingTraderTakeDamageFromWater = false; - public boolean wanderingTraderAllowTrading = true; -+ public boolean wanderingTraderAlwaysDropExp = false; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -2646,6 +2771,7 @@ public class PurpurWorldConfig { - wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed); - wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater); - wanderingTraderAllowTrading = getBoolean("mobs.wandering_trader.allow-trading", wanderingTraderAllowTrading); -+ wanderingTraderAlwaysDropExp = getBoolean("mobs.wandering_trader.always-drop-exp", wanderingTraderAlwaysDropExp); - } - - public boolean wardenRidable = false; -@@ -2662,6 +2788,7 @@ public class PurpurWorldConfig { - public boolean witchControllable = true; - public double witchMaxHealth = 26.0D; - public boolean witchTakeDamageFromWater = false; -+ public boolean witchAlwaysDropExp = false; - private void witchSettings() { - witchRidable = getBoolean("mobs.witch.ridable", witchRidable); - witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); -@@ -2673,6 +2800,7 @@ public class PurpurWorldConfig { - } - witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth); - witchTakeDamageFromWater = getBoolean("mobs.witch.takes-damage-from-water", witchTakeDamageFromWater); -+ witchAlwaysDropExp = getBoolean("mobs.witch.always-drop-exp", witchAlwaysDropExp); - } - - public boolean witherRidable = false; -@@ -2687,6 +2815,7 @@ public class PurpurWorldConfig { - public boolean witherCanRideVehicles = false; - public float witherExplosionRadius = 1.0F; - public boolean witherPlaySpawnSound = true; -+ public boolean witherAlwaysDropExp = false; - private void witherSettings() { - witherRidable = getBoolean("mobs.wither.ridable", witherRidable); - witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); -@@ -2709,6 +2838,7 @@ public class PurpurWorldConfig { - witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles); - witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius); - witherPlaySpawnSound = getBoolean("mobs.wither.play-spawn-sound", witherPlaySpawnSound); -+ witherAlwaysDropExp = getBoolean("mobs.wither.always-drop-exp", witherAlwaysDropExp); - } - - public boolean witherSkeletonRidable = false; -@@ -2716,6 +2846,7 @@ public class PurpurWorldConfig { - public boolean witherSkeletonControllable = true; - public double witherSkeletonMaxHealth = 20.0D; - public boolean witherSkeletonTakeDamageFromWater = false; -+ public boolean witherSkeletonAlwaysDropExp = false; - private void witherSkeletonSettings() { - witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); - witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); -@@ -2727,6 +2858,7 @@ public class PurpurWorldConfig { - } - witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth); - witherSkeletonTakeDamageFromWater = getBoolean("mobs.wither_skeleton.takes-damage-from-water", witherSkeletonTakeDamageFromWater); -+ witherSkeletonAlwaysDropExp = getBoolean("mobs.wither_skeleton.always-drop-exp", witherSkeletonAlwaysDropExp); - } - - public boolean wolfRidable = false; -@@ -2738,6 +2870,7 @@ public class PurpurWorldConfig { - public double wolfNaturalRabid = 0.0D; - public int wolfBreedingTicks = 6000; - public boolean wolfTakeDamageFromWater = false; -+ public boolean wolfAlwaysDropExp = false; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); - wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); -@@ -2757,6 +2890,7 @@ public class PurpurWorldConfig { - wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid); - wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); - wolfTakeDamageFromWater = getBoolean("mobs.wolf.takes-damage-from-water", wolfTakeDamageFromWater); -+ wolfAlwaysDropExp = getBoolean("mobs.wolf.always-drop-exp", wolfAlwaysDropExp); - } - - public boolean zoglinRidable = false; -@@ -2764,6 +2898,7 @@ public class PurpurWorldConfig { - public boolean zoglinControllable = true; - public double zoglinMaxHealth = 40.0D; - public boolean zoglinTakeDamageFromWater = false; -+ public boolean zoglinAlwaysDropExp = false; - private void zoglinSettings() { - zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); - zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); -@@ -2775,6 +2910,7 @@ public class PurpurWorldConfig { - } - zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth); - zoglinTakeDamageFromWater = getBoolean("mobs.zoglin.takes-damage-from-water", zoglinTakeDamageFromWater); -+ zoglinAlwaysDropExp = getBoolean("mobs.zoglin.always-drop-exp", zoglinAlwaysDropExp); - } - - public boolean zombieRidable = false; -@@ -2788,6 +2924,7 @@ public class PurpurWorldConfig { - public boolean zombieAggressiveTowardsVillagerWhenLagging = true; - public boolean zombieBypassMobGriefing = false; - public boolean zombieTakeDamageFromWater = false; -+ public boolean zombieAlwaysDropExp = false; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -2805,6 +2942,7 @@ public class PurpurWorldConfig { - zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging); - zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing); - zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater); -+ zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp); - } - - public boolean zombieHorseRidableInWater = false; -@@ -2817,6 +2955,7 @@ public class PurpurWorldConfig { - public double zombieHorseMovementSpeedMax = 0.2D; - public double zombieHorseSpawnChance = 0.0D; - public boolean zombieHorseTakeDamageFromWater = false; -+ public boolean zombieHorseAlwaysDropExp = false; - private void zombieHorseSettings() { - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); - zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); -@@ -2834,6 +2973,7 @@ public class PurpurWorldConfig { - zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax); - zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance); - zombieHorseTakeDamageFromWater = getBoolean("mobs.zombie_horse.takes-damage-from-water", zombieHorseTakeDamageFromWater); -+ zombieHorseAlwaysDropExp = getBoolean("mobs.zombie_horse.always-drop-exp", zombieHorseAlwaysDropExp); - } - - public boolean zombieVillagerRidable = false; -@@ -2848,6 +2988,7 @@ public class PurpurWorldConfig { - public int zombieVillagerCuringTimeMin = 3600; - public int zombieVillagerCuringTimeMax = 6000; - public boolean zombieVillagerCureEnabled = true; -+ public boolean zombieVillagerAlwaysDropExp = false; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -2866,6 +3007,7 @@ public class PurpurWorldConfig { - zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin); - zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax); - zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled); -+ zombieVillagerAlwaysDropExp = getBoolean("mobs.zombie_villager.always-drop-exp", zombieVillagerAlwaysDropExp); - } - - public boolean zombifiedPiglinRidable = false; -@@ -2878,6 +3020,7 @@ public class PurpurWorldConfig { - public boolean zombifiedPiglinJockeyTryExistingChickens = true; - public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = true; - public boolean zombifiedPiglinTakeDamageFromWater = false; -+ public boolean zombifiedPiglinAlwaysDropExp = false; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -@@ -2894,6 +3037,7 @@ public class PurpurWorldConfig { - zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens); - zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry); - zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater); -+ zombifiedPiglinAlwaysDropExp = getBoolean("mobs.zombified_piglin.always-drop-exp", zombifiedPiglinAlwaysDropExp); - } - - public float hungerStarvationDamage = 1.0F; diff --git a/patches/server/0228-Potion-NamespacedKey.patch b/patches/server/0228-Potion-NamespacedKey.patch deleted file mode 100644 index 7e9785d9f..000000000 --- a/patches/server/0228-Potion-NamespacedKey.patch +++ /dev/null @@ -1,310 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Racci -Date: Sat, 4 Dec 2021 00:07:05 +1100 -Subject: [PATCH] Potion NamespacedKey - - -diff --git a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java -index 14fab63346d56c72cd7534a04760efd10eef4295..745e792482f61c571e2efbd4200dd1bdaef6e474 100644 ---- a/src/main/java/net/minecraft/world/effect/MobEffectInstance.java -+++ b/src/main/java/net/minecraft/world/effect/MobEffectInstance.java -@@ -14,6 +14,7 @@ import net.minecraft.util.ExtraCodecs; - import net.minecraft.util.Mth; - import net.minecraft.world.entity.LivingEntity; - import org.slf4j.Logger; -+import org.bukkit.NamespacedKey; - - public class MobEffectInstance implements Comparable { - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -25,6 +26,7 @@ public class MobEffectInstance implements Comparable { - private boolean visible; - private boolean showIcon; - @Nullable -+ private NamespacedKey key; // Purpur - add key - private MobEffectInstance hiddenEffect; - private final Optional factorData; - -@@ -44,17 +46,36 @@ public class MobEffectInstance implements Comparable { - this(type, duration, amplifier, ambient, visible, visible); - } - -+ // Purpur start -+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean visible, @Nullable NamespacedKey key) { -+ this(type, duration, amplifier, ambient, visible, visible, key); -+ } -+ // Purpur end -+ - public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon) { -- this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData()); -+ // Purpur start -+ this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData(), (NamespacedKey)null); -+ } -+ -+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable NamespacedKey key) { -+ this(type, duration, amplifier, ambient, showParticles, showIcon, (MobEffectInstance)null, type.createFactorData(), key); -+ // Purpur end - } - - public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable MobEffectInstance hiddenEffect, Optional factorCalculationData) { -+ // Purpur start -+ this(type, duration, amplifier, ambient, showParticles, showIcon, hiddenEffect, factorCalculationData, (NamespacedKey) null); -+ } -+ -+ public MobEffectInstance(MobEffect type, int duration, int amplifier, boolean ambient, boolean showParticles, boolean showIcon, @Nullable MobEffectInstance hiddenEffect, Optional factorCalculationData, @Nullable NamespacedKey key) { -+ // Purpur end - this.effect = type; - this.duration = duration; - this.amplifier = amplifier; - this.ambient = ambient; - this.visible = showParticles; - this.showIcon = showIcon; -+ this.key = key; // Purpur - add key - this.hiddenEffect = hiddenEffect; - this.factorData = factorCalculationData; - } -@@ -75,6 +96,7 @@ public class MobEffectInstance implements Comparable { - this.ambient = that.ambient; - this.visible = that.visible; - this.showIcon = that.showIcon; -+ this.key = that.key; // Purpur - add key - } - - public boolean update(MobEffectInstance that) { -@@ -120,6 +142,13 @@ public class MobEffectInstance implements Comparable { - bl = true; - } - -+ // Purpur start -+ if (that.key != this.key) { -+ this.key = that.key; -+ bl = true; -+ } -+ // Purpur end -+ - return bl; - } - -@@ -163,6 +192,17 @@ public class MobEffectInstance implements Comparable { - return this.showIcon; - } - -+ // Purpur start -+ public boolean hasKey() { -+ return this.key != null; -+ } -+ -+ @Nullable -+ public NamespacedKey getKey() { -+ return this.key; -+ } -+ // Purpur end -+ - public boolean tick(LivingEntity entity, Runnable overwriteCallback) { - if (this.hasRemainingDuration()) { - int i = this.isInfiniteDuration() ? entity.tickCount : this.duration; -@@ -226,6 +266,12 @@ public class MobEffectInstance implements Comparable { - string = string + ", Show Icon: false"; - } - -+ // Purpur start -+ if (this.hasKey()) { -+ string = string + ", Key: " + this.key; -+ } -+ // Purpur end -+ - return string; - } - -@@ -241,7 +287,7 @@ public class MobEffectInstance implements Comparable { - return false; - } else { - MobEffectInstance mobEffectInstance = (MobEffectInstance)object; -- return this.duration == mobEffectInstance.duration && this.amplifier == mobEffectInstance.amplifier && this.ambient == mobEffectInstance.ambient && this.effect.equals(mobEffectInstance.effect); -+ return this.duration == mobEffectInstance.duration && this.amplifier == mobEffectInstance.amplifier && this.ambient == mobEffectInstance.ambient && this.effect.equals(mobEffectInstance.effect) && this.key == mobEffectInstance.key; // Purpur - add key - } - } - -@@ -265,6 +311,11 @@ public class MobEffectInstance implements Comparable { - nbt.putBoolean("Ambient", this.isAmbient()); - nbt.putBoolean("ShowParticles", this.isVisible()); - nbt.putBoolean("ShowIcon", this.showIcon()); -+ // Purpur start -+ if (this.key != null) { -+ nbt.putString("Key", this.key.toString()); -+ } -+ // Purpur end - if (this.hiddenEffect != null) { - CompoundTag compoundTag = new CompoundTag(); - this.hiddenEffect.save(compoundTag); -@@ -299,6 +350,13 @@ public class MobEffectInstance implements Comparable { - bl3 = nbt.getBoolean("ShowIcon"); - } - -+ // Purpur start -+ NamespacedKey key = null; -+ if (nbt.contains("Key")) { -+ key = NamespacedKey.fromString(nbt.getString("Key")); -+ } -+ // Purpur end -+ - MobEffectInstance mobEffectInstance = null; - if (nbt.contains("HiddenEffect", 10)) { - mobEffectInstance = loadSpecifiedEffect(type, nbt.getCompound("HiddenEffect")); -@@ -311,7 +369,7 @@ public class MobEffectInstance implements Comparable { - optional = Optional.empty(); - } - -- return new MobEffectInstance(type, j, Math.max(i, 0), bl, bl2, bl3, mobEffectInstance, optional); -+ return new MobEffectInstance(type, j, Math.max(i, 0), bl, bl2, bl3, mobEffectInstance, optional, key); // Purpur - add key - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index 72dcac5708c06556549d268e54ecb4b6e71c93be..7f1c7532adace841baec500598133981a6feb78a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -465,7 +465,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - @Override - public boolean addPotionEffect(PotionEffect effect, boolean force) { - org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper -- this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon -+ this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon(), effect.getKey()), EntityPotionEffectEvent.Cause.PLUGIN); // Purpur - add key // Paper - Don't ignore icon - return true; - } - -@@ -486,7 +486,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - @Override - public PotionEffect getPotionEffect(PotionEffectType type) { - MobEffectInstance handle = this.getHandle().getEffect(MobEffect.byId(type.getId())); -- return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible()); -+ return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible(), handle.getKey()); // Purpur - add key - } - - @Override -@@ -498,7 +498,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - public Collection getActivePotionEffects() { - List effects = new ArrayList(); - for (MobEffectInstance handle : this.getHandle().activeEffects.values()) { -- effects.add(new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible())); -+ effects.add(new PotionEffect(PotionEffectType.getById(MobEffect.getId(handle.getEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible(), handle.getKey())); // Purpur - add key - } - return effects; - } -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java -index d94ab2607d1ab657a6b37924ce5ebcbbc3984011..f594596029513426f974ba820ed4702703b667a4 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java -@@ -13,6 +13,7 @@ import net.minecraft.nbt.CompoundTag; - import net.minecraft.nbt.ListTag; - import org.bukkit.Color; - import org.bukkit.Material; -+import org.bukkit.NamespacedKey; - import org.bukkit.configuration.serialization.DelegateDeserialization; - import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; - import org.bukkit.craftbukkit.potion.CraftPotionUtil; -@@ -42,6 +43,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { - static final ItemMetaKey POTION_COLOR = new ItemMetaKey("CustomPotionColor", "custom-color"); - static final ItemMetaKey ID = new ItemMetaKey("Id", "potion-id"); - static final ItemMetaKey DEFAULT_POTION = new ItemMetaKey("Potion", "potion-type"); -+ static final ItemMetaKey KEY = new ItemMetaKey("Key", "namespacedkey"); // Purpur - add key - - // Having an initial "state" in ItemMeta seems bit dirty but the UNCRAFTABLE potion type - // is treated as the empty form of the meta because it represents an empty potion with no effect -@@ -91,7 +93,13 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { - boolean ambient = effect.getBoolean(AMBIENT.NBT); - boolean particles = effect.contains(SHOW_PARTICLES.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_PARTICLES.NBT) : true; - boolean icon = effect.contains(SHOW_ICON.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_ICON.NBT) : particles; -- this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon)); -+ // Purpur start -+ NamespacedKey key = null; -+ if (tag.contains(KEY.NBT)) { -+ key = NamespacedKey.fromString(effect.getString(KEY.NBT)); -+ } -+ this.customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon, key)); -+ // Purpur end - } - } - } -@@ -138,6 +146,11 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { - effectData.putBoolean(AMBIENT.NBT, effect.isAmbient()); - effectData.putBoolean(SHOW_PARTICLES.NBT, effect.hasParticles()); - effectData.putBoolean(SHOW_ICON.NBT, effect.hasIcon()); -+ // Purpur start -+ if (effect.hasKey()) { -+ effectData.putString(KEY.NBT, effect.getKey().toString()); -+ } -+ // Purpur end - effectList.add(effectData); - } - } -@@ -199,7 +212,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { - if (index != -1) { - if (overwrite) { - PotionEffect old = this.customEffects.get(index); -- if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient()) { -+ if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient() && old.getKey() == effect.getKey()) { // Purpur - add key - return false; - } - this.customEffects.set(index, effect); -diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java -index acb69821a99aa69bce6d127e10976089c85be223..c5abd73981c5f4b41605eba0d44e6573dfd2a77a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java -+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java -@@ -101,7 +101,7 @@ public class CraftPotionUtil { - - public static MobEffectInstance fromBukkit(PotionEffect effect) { - MobEffect type = MobEffect.byId(effect.getType().getId()); -- return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()); -+ return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.getKey()); // Purpur - add key - } - - public static PotionEffect toBukkit(MobEffectInstance effect) { -@@ -110,7 +110,7 @@ public class CraftPotionUtil { - int duration = effect.getDuration(); - boolean ambient = effect.isAmbient(); - boolean particles = effect.isVisible(); -- return new PotionEffect(type, duration, amp, ambient, particles); -+ return new PotionEffect(type, duration, amp, ambient, particles, effect.getKey()); // Purpur - add key - } - - public static boolean equals(MobEffect mobEffect, PotionEffectType type) { -diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java -index 83226ec2fa977819e12a499eb3765232543c17b3..a742774dabaee0629f4e6adabee5f3ec4b3be41c 100644 ---- a/src/test/java/org/bukkit/potion/PotionTest.java -+++ b/src/test/java/org/bukkit/potion/PotionTest.java -@@ -9,6 +9,7 @@ import net.minecraft.resources.ResourceLocation; - import net.minecraft.world.effect.MobEffect; - import net.minecraft.world.effect.MobEffectInstance; - import net.minecraft.world.item.alchemy.Potion; -+import org.bukkit.NamespacedKey; - import org.bukkit.support.AbstractTestingBase; - import org.junit.Test; - -@@ -47,4 +48,27 @@ public class PotionTest extends AbstractTestingBase { - assertEquals("Same type not returned by name " + key, bukkit, byName); - } - } -+ -+ // Purpur start -+ @Test -+ public void testNamespacedKey() { -+ NamespacedKey key = new NamespacedKey("testnamespace", "testkey"); -+ PotionEffect namedSpacedEffect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true, key); -+ assertNotNull(namedSpacedEffect.getKey()); -+ assertTrue(namedSpacedEffect.hasKey()); -+ assertFalse(namedSpacedEffect.withKey(null).hasKey()); -+ -+ PotionEffect effect = new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 20, 0, true, true, true); -+ assertNull(effect.getKey()); -+ assertFalse(effect.hasKey()); -+ assertTrue(namedSpacedEffect.withKey(key).hasKey()); -+ -+ Map s1 = namedSpacedEffect.serialize(); -+ Map s2 = effect.serialize(); -+ assertTrue(s1.containsKey("namespacedKey")); -+ assertFalse(s2.containsKey("namespacedKey")); -+ assertNotNull(new PotionEffect(s1).getKey()); -+ assertNull(new PotionEffect(s2).getKey()); -+ } -+ // Purpur end - } diff --git a/patches/server/0229-Grindstone-API.patch b/patches/server/0229-Grindstone-API.patch deleted file mode 100644 index c1b78036d..000000000 --- a/patches/server/0229-Grindstone-API.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 27 Dec 2021 08:11:00 -0600 -Subject: [PATCH] Grindstone API - - -diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -index 6860e3467bf785e9d017fc98fce1e3cf71f9b6ee..23a8522b80475ad29ffb4afd2f4836acda2538e3 100644 ---- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -@@ -95,9 +95,11 @@ public class GrindstoneMenu extends AbstractContainerMenu { - - @Override - public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) { -+ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur - context.execute((world, blockposition) -> { -+ org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent grindstoneTakeResultEvent = new org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), this.getExperienceAmount(world)); grindstoneTakeResultEvent.callEvent(); // Purpur - if (world instanceof ServerLevel) { -- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), this.getExperienceAmount(world), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper -+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper // Purpur - } - - world.levelEvent(1042, blockposition, 0); -@@ -341,7 +343,9 @@ public class GrindstoneMenu extends AbstractContainerMenu { - return ItemStack.EMPTY; - } - -+ this.activeQuickItem = itemstack; // Purpur - slot1.onTake(player, itemstack1); -+ this.activeQuickItem = null; // Purpur - } - - return itemstack; diff --git a/patches/server/0230-Ability-for-hoe-to-replant-crops-and-nether-warts.patch b/patches/server/0230-Ability-for-hoe-to-replant-crops-and-nether-warts.patch deleted file mode 100644 index 6f0bf21ae..000000000 --- a/patches/server/0230-Ability-for-hoe-to-replant-crops-and-nether-warts.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Tue, 28 Dec 2021 16:22:20 -0600 -Subject: [PATCH] Ability for hoe to replant crops and nether warts - - -diff --git a/src/main/java/net/minecraft/world/level/block/BushBlock.java b/src/main/java/net/minecraft/world/level/block/BushBlock.java -index 03fde6e47c4a347c62fe9b4a3351769aedf874f6..ca906b0250e5332f7ececf1419ca6d2c1d385adc 100644 ---- a/src/main/java/net/minecraft/world/level/block/BushBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BushBlock.java -@@ -48,4 +48,24 @@ public class BushBlock extends Block { - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { - return type == PathComputationType.AIR && !this.hasCollision ? true : super.isPathfindable(state, world, pos, type); - } -+ -+ // Purpur start -+ public void playerDestroyAndReplant(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, net.minecraft.world.item.ItemStack itemInHand, net.minecraft.world.level.ItemLike itemToReplant) { -+ player.awardStat(net.minecraft.stats.Stats.BLOCK_MINED.get(this)); -+ player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); -+ java.util.List dropList = Block.getDrops(state, (net.minecraft.server.level.ServerLevel) world, pos, blockEntity, player, itemInHand); -+ -+ boolean planted = false; -+ for (net.minecraft.world.item.ItemStack itemToDrop : dropList) { -+ if (!planted && itemToDrop.getItem() == itemToReplant) { -+ world.setBlock(pos, defaultBlockState(), 3); -+ itemToDrop.setCount(itemToDrop.getCount() - 1); -+ planted = true; -+ } -+ Block.popResource(world, pos, itemToDrop); -+ } -+ -+ state.spawnAfterBreak((net.minecraft.server.level.ServerLevel) world, pos, itemInHand, true); -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java -index 373fda589014c71c07170ec6707d5ff66d46ddd5..26b3f162f25bed3e9d8001e3d9c2f8d7d4b3e08a 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -203,4 +203,15 @@ public class CropBlock extends BushBlock implements BonemealableBlock { - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(CropBlock.AGE); - } -+ -+ // Purpur start -+ @Override -+ public void playerDestroy(Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops) { -+ if (world.purpurConfig.hoeReplantsCrops && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) { -+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, getBaseSeedId()); -+ } else { -+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -index e55720c4d2fbdf6aae526910e87a67c29cf906fd..74fedd3e401c6d58c03c0579f4b919114404fd78 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -@@ -60,4 +60,15 @@ public class NetherWartBlock extends BushBlock { - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(NetherWartBlock.AGE); - } -+ -+ // Purpur start -+ @Override -+ public void playerDestroy(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops) { -+ if (world.purpurConfig.hoeReplantsNetherWarts && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) { -+ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, Items.NETHER_WART); -+ } else { -+ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e184a95a4ff3028ffa05fb4dad68c0047d22a638..98c39a3297e676c7c780430180f5e98076b7e718 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -547,6 +547,8 @@ public class PurpurWorldConfig { - public Map axeWeatherables = new HashMap<>(); - public Map hoeTillables = new HashMap<>(); - public Map shovelFlattenables = new HashMap<>(); -+ public boolean hoeReplantsCrops = false; -+ public boolean hoeReplantsNetherWarts = false; - private void toolSettings() { - axeStrippables.clear(); - axeWaxables.clear(); -@@ -740,6 +742,8 @@ public class PurpurWorldConfig { - }); - shovelFlattenables.put(block, new Flattenable(into, drops)); - }); -+ hoeReplantsCrops = getBoolean("tools.hoe.replant-crops", hoeReplantsCrops); -+ hoeReplantsNetherWarts = getBoolean("tools.hoe.replant-nether-warts", hoeReplantsNetherWarts); - } - - public boolean anvilAllowColors = false; diff --git a/patches/server/0231-Shearing-jeb-produces-random-color-wool.patch b/patches/server/0231-Shearing-jeb-produces-random-color-wool.patch deleted file mode 100644 index 8935e148a..000000000 --- a/patches/server/0231-Shearing-jeb-produces-random-color-wool.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 31 Dec 2021 06:03:12 -0600 -Subject: [PATCH] Shearing jeb produces random color wool - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index a1b323ecba25910e97f154e487acc94943117e0c..62d8ae4c689170420c7850fbbb402be85b565882 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -315,7 +315,7 @@ public class Sheep extends Animal implements Shearable { - - for (int j = 0; j < i; ++j) { - this.forceDrops = true; // CraftBukkit -- ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.getColor()), 1); -+ ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.level().purpurConfig.sheepShearJebRandomColor && hasCustomName() && getCustomName().getString().equals("jeb_") ? DyeColor.random(this.level().random) : this.getColor()), 1); // Purpur - this.forceDrops = false; // CraftBukkit - - if (entityitem != null) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 98c39a3297e676c7c780430180f5e98076b7e718..6edb5b4cac3ac9cf7d080293f0b582df31fe2c86 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2307,6 +2307,7 @@ public class PurpurWorldConfig { - public boolean sheepBypassMobGriefing = false; - public boolean sheepTakeDamageFromWater = false; - public boolean sheepAlwaysDropExp = false; -+ public boolean sheepShearJebRandomColor = false; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -2321,6 +2322,7 @@ public class PurpurWorldConfig { - sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing); - sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater); - sheepAlwaysDropExp = getBoolean("mobs.sheep.always-drop-exp", sheepAlwaysDropExp); -+ sheepShearJebRandomColor = getBoolean("mobs.sheep.jeb-shear-random-color", sheepShearJebRandomColor); - } - - public boolean shulkerRidable = false; diff --git a/patches/server/0232-Turtle-eggs-random-tick-crack-chance.patch b/patches/server/0232-Turtle-eggs-random-tick-crack-chance.patch deleted file mode 100644 index 6477054a3..000000000 --- a/patches/server/0232-Turtle-eggs-random-tick-crack-chance.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 31 Dec 2021 06:18:54 -0600 -Subject: [PATCH] Turtle eggs random tick crack chance - - -diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -index 86360fad47e4cda64b02ce4c8c0227649ed69a5d..b211d7b37095b52424ead1bb0be0c81bc4322985 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -160,7 +160,7 @@ public class TurtleEggBlock extends Block { - private boolean shouldUpdateHatchLevel(Level world) { - float f = world.getTimeOfDay(1.0F); - -- return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(500) == 0; -+ return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(world.purpurConfig.turtleEggsRandomTickCrackChance) == 0; - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6edb5b4cac3ac9cf7d080293f0b582df31fe2c86..30e63ae6bc35efc64a016966f55bfb1e7df52d89 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1015,11 +1015,13 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; - public boolean turtleEggsBypassMobGriefing = false; -+ public int turtleEggsRandomTickCrackChance = 500; - private void turtleEggSettings() { - turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs); - turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems); - turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); - turtleEggsBypassMobGriefing = getBoolean("blocks.turtle_egg.bypass-mob-griefing", turtleEggsBypassMobGriefing); -+ turtleEggsRandomTickCrackChance = getInt("blocks.turtle_egg.random-tick-crack-chance", turtleEggsRandomTickCrackChance); - } - - public int waterInfiniteRequiredSources = 2; diff --git a/patches/server/0233-Mob-head-visibility-percent.patch b/patches/server/0233-Mob-head-visibility-percent.patch deleted file mode 100644 index 104090484..000000000 --- a/patches/server/0233-Mob-head-visibility-percent.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 31 Dec 2021 06:40:19 -0600 -Subject: [PATCH] Mob head visibility percent - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 21d6851cb903f214db92109a6c16bb7367e40e1c..aff8b1b0683e52038b6d92e052a012c9198bf8ce 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1040,9 +1040,20 @@ public abstract class LivingEntity extends Entity implements Attackable { - ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); - EntityType entitytypes = entity.getType(); - -- if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL) || entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD) || entitytypes == EntityType.PIGLIN && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.PIGLIN_BRUTE && itemstack.is(Items.PIGLIN_HEAD) || entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) { -- d0 *= 0.5D; -+ // Purpur start -+ if (entitytypes == EntityType.SKELETON && itemstack.is(Items.SKELETON_SKULL)) { -+ d0 *= entity.level().purpurConfig.skeletonHeadVisibilityPercent; -+ } -+ else if (entitytypes == EntityType.ZOMBIE && itemstack.is(Items.ZOMBIE_HEAD)) { -+ d0 *= entity.level().purpurConfig.zombieHeadVisibilityPercent; -+ } -+ else if (entitytypes == EntityType.CREEPER && itemstack.is(Items.CREEPER_HEAD)) { -+ d0 *= entity.level().purpurConfig.creeperHeadVisibilityPercent; - } -+ else if ((entitytypes == EntityType.PIGLIN || entitytypes == EntityType.PIGLIN_BRUTE) && itemstack.is(Items.PIGLIN_HEAD)) { -+ d0 *= entity.level().purpurConfig.piglinHeadVisibilityPercent; -+ } -+ // Purpur end - - // Purpur start - if (entity instanceof LivingEntity entityliving) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 30e63ae6bc35efc64a016966f55bfb1e7df52d89..91af2d4187b2e61426539d018900c810d048efc9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1309,6 +1309,7 @@ public class PurpurWorldConfig { - public boolean creeperExplodeWhenKilled = false; - public boolean creeperHealthRadius = false; - public boolean creeperAlwaysDropExp = false; -+ public double creeperHeadVisibilityPercent = 0.5D; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -1326,6 +1327,7 @@ public class PurpurWorldConfig { - creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled); - creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius); - creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp); -+ creeperHeadVisibilityPercent = getDouble("mobs.creeper.head-visibility-percent", creeperHeadVisibilityPercent); - } - - public boolean dolphinRidable = false; -@@ -2112,6 +2114,7 @@ public class PurpurWorldConfig { - public boolean piglinTakeDamageFromWater = false; - public int piglinPortalSpawnModifier = 2000; - public boolean piglinAlwaysDropExp = false; -+ public double piglinHeadVisibilityPercent = 0.5D; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -2126,6 +2129,7 @@ public class PurpurWorldConfig { - piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); - piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier); - piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp); -+ piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent); - } - - public boolean piglinBruteRidable = false; -@@ -2387,6 +2391,7 @@ public class PurpurWorldConfig { - public double skeletonMaxHealth = 20.0D; - public boolean skeletonTakeDamageFromWater = false; - public boolean skeletonAlwaysDropExp = false; -+ public double skeletonHeadVisibilityPercent = 0.5D; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -2399,6 +2404,7 @@ public class PurpurWorldConfig { - skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth); - skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater); - skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp); -+ skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent); - } - - public boolean skeletonHorseRidableInWater = true; -@@ -2933,6 +2939,7 @@ public class PurpurWorldConfig { - public boolean zombieBypassMobGriefing = false; - public boolean zombieTakeDamageFromWater = false; - public boolean zombieAlwaysDropExp = false; -+ public double zombieHeadVisibilityPercent = 0.5D; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -2951,6 +2958,7 @@ public class PurpurWorldConfig { - zombieBypassMobGriefing = getBoolean("mobs.zombie.bypass-mob-griefing", zombieBypassMobGriefing); - zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater); - zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp); -+ zombieHeadVisibilityPercent = getDouble("mobs.zombie.head-visibility-percent", zombieHeadVisibilityPercent); - } - - public boolean zombieHorseRidableInWater = false; diff --git a/patches/server/0234-Configurable-valid-characters-for-usernames.patch b/patches/server/0234-Configurable-valid-characters-for-usernames.patch deleted file mode 100644 index 664d05272..000000000 --- a/patches/server/0234-Configurable-valid-characters-for-usernames.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 1 Jan 2022 18:38:58 -0600 -Subject: [PATCH] Configurable valid characters for usernames - - -diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index b4b88a3d4dc66c44ca8f3bad1025c17a9993ac1d..01d5fa265fb2818465b5a71a2e2efeec751a7a05 100644 ---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -221,6 +221,8 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - return false; - } - -+ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(in).matches(); // Purpur -+ - for (int i = 0, len = in.length(); i < len; ++i) { - char c = in.charAt(i); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index f562729f543ef2b5998c6c38c0a08b3ae4dab1bf..d56c2776d08306245618dca04cd39891bb61c1bd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -452,4 +452,11 @@ public class PurpurConfig { - private static void networkSettings() { - useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP); - } -+ -+ public static java.util.regex.Pattern usernameValidCharactersPattern; -+ private static void usernameValidationSettings() { -+ String defaultPattern = "^[a-zA-Z0-9_.]*$"; -+ String setPattern = getString("settings.username-valid-characters", defaultPattern); -+ usernameValidCharactersPattern = java.util.regex.Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern); -+ } - } diff --git a/patches/server/0235-Shears-can-have-looting-enchantment.patch b/patches/server/0235-Shears-can-have-looting-enchantment.patch deleted file mode 100644 index 5a2131cef..000000000 --- a/patches/server/0235-Shears-can-have-looting-enchantment.patch +++ /dev/null @@ -1,179 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 3 Jan 2022 00:06:51 -0600 -Subject: [PATCH] Shears can have looting enchantment - - -diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java -index 9b0049dfeaec9b688bf276f2ac2b18943b5696b2..d7563904232353cbf3b9255cedfb75920e35220c 100644 ---- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java -+++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java -@@ -107,7 +107,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior { - continue; - } - // CraftBukkit end -- ishearable.shear(SoundSource.BLOCKS); -+ ishearable.shear(SoundSource.BLOCKS, net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MOB_LOOTING, CraftItemStack.asNMSCopy(craftItem))); // Purpur - worldserver.gameEvent((Entity) null, GameEvent.SHEAR, blockposition); - return true; - } -diff --git a/src/main/java/net/minecraft/world/entity/Shearable.java b/src/main/java/net/minecraft/world/entity/Shearable.java -index 5e8cc5cfac8888628c6d513148f41be09ca65a2c..a089fc61ec09be6b7490375489178dc6ba5a644b 100644 ---- a/src/main/java/net/minecraft/world/entity/Shearable.java -+++ b/src/main/java/net/minecraft/world/entity/Shearable.java -@@ -3,7 +3,13 @@ package net.minecraft.world.entity; - import net.minecraft.sounds.SoundSource; - - public interface Shearable { -- void shear(SoundSource shearedSoundCategory); -+ // Purpur start -+ default void shear(SoundSource shearedSoundCategory) { -+ shear(shearedSoundCategory, 0); -+ } -+ -+ void shear(SoundSource shearedSoundCategory, int looting); -+ // Purpur end - - boolean readyForShearing(); - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -index 1b0a0e36baa0f664f06b2d166aa907b320066b6e..e462b81aa1276a8f1222b74459359f6c6d57f739 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -164,7 +164,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { -@@ -207,7 +207,7 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - entityhuman1.broadcastBreakEvent(hand); -@@ -308,10 +308,11 @@ public class Sheep extends Animal implements Shearable { - } - - @Override -- public void shear(SoundSource shearedSoundCategory) { -+ public void shear(SoundSource shearedSoundCategory, int looting) { // Purpur - this.level().playSound((Player) null, (Entity) this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F); - this.setSheared(true); - int i = 1 + this.random.nextInt(3); -+ if (org.purpurmc.purpur.PurpurConfig.allowShearsLooting) i += looting; // Purpur - - for (int j = 0; j < i; ++j) { - this.forceDrops = true; // CraftBukkit -diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -index fc8526af7e1df15794b4560b58e7f6a47508aa08..8b364fe9f3a3d47ae6daa331b8f16941ca17432a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -199,7 +199,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - return tryRide(player, hand); // Purpur - } - // CraftBukkit end -- this.shear(SoundSource.PLAYERS); -+ this.shear(SoundSource.PLAYERS, net.minecraft.world.item.enchantment.EnchantmentHelper.getMobLooting(player)); // Purpur - this.gameEvent(GameEvent.SHEAR, player); - if (!this.level().isClientSide) { - itemstack.hurtAndBreak(1, player, (entityhuman1) -> { -@@ -222,12 +222,13 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - } - - @Override -- public void shear(SoundSource shearedSoundCategory) { -+ public void shear(SoundSource shearedSoundCategory, int looting) { // Purpur - this.level().playSound((Player) null, (Entity) this, SoundEvents.SNOW_GOLEM_SHEAR, shearedSoundCategory, 1.0F, 1.0F); - if (!this.level().isClientSide()) { - this.setPumpkin(false); - this.forceDrops = true; // CraftBukkit - if (level().purpurConfig.snowGolemDropsPumpkin) // Purpur -+ for (int i = 0; i < 1 + (org.purpurmc.purpur.PurpurConfig.allowShearsLooting ? looting : 0); i++) // Purpur - this.spawnAtLocation(new ItemStack(Items.CARVED_PUMPKIN), 1.7F); - this.forceDrops = false; // CraftBukkit - } -diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -index 859435f747ceef860cb4e9e825a7353ea3b90798..fc2c35f57436371cb0111aedfd289ac95d506d07 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentCategory.java -@@ -128,6 +128,12 @@ public enum EnchantmentCategory { - public boolean canEnchant(Item item) { - return item instanceof BowItem || item instanceof CrossbowItem; - } -+ }, -+ WEAPON_AND_SHEARS { -+ @Override -+ public boolean canEnchant(Item item) { -+ return WEAPON.canEnchant(item) || item instanceof net.minecraft.world.item.ShearsItem; -+ } - // Purpur end - }; - -diff --git a/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java b/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java -index 4007c16550683e23b396dfdff29530a82523fe05..8fe09c13643d99639fb242da4367c42ef31b38b4 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/LootBonusEnchantment.java -@@ -7,6 +7,14 @@ public class LootBonusEnchantment extends Enchantment { - super(weight, target, slotTypes); - } - -+ // Purpur start -+ @Override -+ public boolean canEnchant(net.minecraft.world.item.ItemStack stack) { -+ // we have to cheat the system because this class is loaded before purpur's config is loaded -+ return (org.purpurmc.purpur.PurpurConfig.allowShearsLooting && this.category == EnchantmentCategory.WEAPON ? EnchantmentCategory.WEAPON_AND_SHEARS : this.category).canEnchant(stack.getItem()); -+ } -+ // Purpur end -+ - @Override - public int getMinCost(int level) { - return 15 + (level - 1) * 9; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index d56c2776d08306245618dca04cd39891bb61c1bd..730a4a2e46aeb233d8036e8d7e1749dcb397e6c0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -391,6 +391,7 @@ public class PurpurConfig { - - public static boolean allowInfinityMending = false; - public static boolean allowCrossbowInfinity = false; -+ public static boolean allowShearsLooting = false; - public static boolean allowUnsafeEnchants = false; - public static boolean allowInapplicableEnchants = true; - public static boolean allowIncompatibleEnchants = true; -@@ -413,6 +414,7 @@ public class PurpurConfig { - } - allowInfinityMending = getBoolean("settings.enchantment.allow-infinity-and-mending-together", allowInfinityMending); - allowCrossbowInfinity = getBoolean("settings.enchantment.allow-infinity-on-crossbow", allowCrossbowInfinity); -+ allowShearsLooting = getBoolean("settings.enchantment.allow-looting-on-shears", allowShearsLooting); - allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", allowUnsafeEnchants); - allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants); - allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants); diff --git a/patches/server/0236-Stop-bees-from-dying-after-stinging.patch b/patches/server/0236-Stop-bees-from-dying-after-stinging.patch deleted file mode 100644 index 254bdbdf4..000000000 --- a/patches/server/0236-Stop-bees-from-dying-after-stinging.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 3 Jan 2022 01:19:46 -0600 -Subject: [PATCH] Stop bees from dying after stinging - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index ac4ca4de2be18a08268b24dfe259cfd136b1a4da..e837500019157129007841c847d807ebae10db04 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -439,6 +439,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.hurt(this.damageSources().drown(), 1.0F); - } - -+ if (flag && !this.level().purpurConfig.beeDiesAfterSting) setHasStung(false); else // Purpur - if (flag) { - ++this.timeSinceSting; - if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 91af2d4187b2e61426539d018900c810d048efc9..a419aa0b06d5215ec66649dde8822120eb91a0f3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1110,6 +1110,7 @@ public class PurpurWorldConfig { - public boolean beeCanWorkAtNight = false; - public boolean beeCanWorkInRain = false; - public boolean beeAlwaysDropExp = false; -+ public boolean beeDiesAfterSting = true; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -1126,6 +1127,7 @@ public class PurpurWorldConfig { - beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight); - beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain); - beeAlwaysDropExp = getBoolean("mobs.bee.always-drop-exp", beeAlwaysDropExp); -+ beeDiesAfterSting = getBoolean("mobs.bee.dies-after-sting", beeDiesAfterSting); - } - - public boolean blazeRidable = false; diff --git a/patches/server/0237-Give-bee-counts-in-beehives-to-Purpur-clients.patch b/patches/server/0237-Give-bee-counts-in-beehives-to-Purpur-clients.patch deleted file mode 100644 index 450c0c063..000000000 --- a/patches/server/0237-Give-bee-counts-in-beehives-to-Purpur-clients.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Thu, 30 Dec 2021 09:56:43 -0600 -Subject: [PATCH] Give bee counts in beehives to Purpur clients - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index cf165386904e4f084ff69463e29fe10fd9af6812..1f6946e1120232d3b104e76212c470f47ed7cde1 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1034,6 +1034,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Tue, 4 Jan 2022 11:56:48 -0600 -Subject: [PATCH] Configurable farmland trample height - -This is _not_ in block height or an exact science. -During my testing I found very inconsistent values -for the fallDistance variable. Here are the results -of those tests (https://imgur.com/BojltJF): - -Value set -> Actual fall distance needed to trample - 1.0 -> 1.25 - 1.5 -> 1.75 - 2.0 -> 2.25 - 2.5 -> 2.87 - 3.0 -> 3.5 - 3.5 -> 4.25 - 4.0 -> 4.25 - 4.5 -> 5.0 - 5.0 -> 5.87 - 5.5 -> 5.87 - 6.0 -> 6.75 - -diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -index 56e66fcd840d0d3681ba671d1ffc51b232631461..d566f67f8f6f1748023430de4f191881b79e44a1 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -101,7 +101,7 @@ public class FarmBlock extends Block { - @Override - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { - super.fallOn(world, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage. -- if (!world.isClientSide && world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || world.purpurConfig.farmlandBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur -+ if (!world.isClientSide && (world.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= world.purpurConfig.farmlandTrampleHeight : world.random.nextFloat() < fallDistance - 0.5F) && entity instanceof LivingEntity && (entity instanceof Player || world.purpurConfig.farmlandBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur - // CraftBukkit start - Interact soil - org.bukkit.event.Cancellable cancellable; - if (entity instanceof Player) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a419aa0b06d5215ec66649dde8822120eb91a0f3..948269a1ce9fe56bfa77e02a1671b993d5d44699 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -893,6 +893,7 @@ public class PurpurWorldConfig { - public boolean farmlandTramplingDisabled = false; - public boolean farmlandTramplingOnlyPlayers = false; - public boolean farmlandTramplingFeatherFalling = false; -+ public double farmlandTrampleHeight = -1D; - private void farmlandSettings() { - farmlandBypassMobGriefing = getBoolean("blocks.farmland.bypass-mob-griefing", farmlandBypassMobGriefing); - farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); -@@ -900,6 +901,7 @@ public class PurpurWorldConfig { - farmlandTramplingDisabled = getBoolean("blocks.farmland.disable-trampling", farmlandTramplingDisabled); - farmlandTramplingOnlyPlayers = getBoolean("blocks.farmland.only-players-trample", farmlandTramplingOnlyPlayers); - farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling); -+ farmlandTrampleHeight = getDouble("blocks.farmland.trample-height", farmlandTrampleHeight); - } - - public double floweringAzaleaGrowthChance = 0.0D; diff --git a/patches/server/0239-Configurable-player-pickup-exp-delay.patch b/patches/server/0239-Configurable-player-pickup-exp-delay.patch deleted file mode 100644 index ba95af541..000000000 --- a/patches/server/0239-Configurable-player-pickup-exp-delay.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 10 Jan 2022 10:04:31 -0600 -Subject: [PATCH] Configurable player pickup exp delay - -Default vanilla value is to delay 2 ticks between picking up exp orbs. -Players only pick up 1 orb at a time, so even with setting this to 0 -players still only pick up one orb every tick. However, setting this -to any negative number will pick up all orbs instantly. - -diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -index 2bd576849403bc2cfae298c2210616192ddc38db..b2233635b6acc35ea3668c36c56e57f15420ac62 100644 ---- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -@@ -313,7 +313,7 @@ public class ExperienceOrb extends Entity { - public void playerTouch(Player player) { - if (!this.level().isClientSide) { - if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper -- player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, 2, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; -+ player.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(player, this.level().purpurConfig.playerExpPickupDelay, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; // Purpur - player.take(this, 1); - int i = this.repairPlayerItems(player, this.value); - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index 0a9d8b22c7127b87826d9d256a5f36c34f2bfc0f..d00035e31cf4773a418d1cc6a6018d08e6b558f0 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -630,7 +630,7 @@ public abstract class Player extends LivingEntity { - for (int i = 0; i < list.size(); ++i) { - Entity entity = (Entity) list.get(i); - -- if (entity.getType() == EntityType.EXPERIENCE_ORB) { -+ if (entity.getType() == EntityType.EXPERIENCE_ORB && entity.level().purpurConfig.playerExpPickupDelay >= 0) { // Purpur - list1.add(entity); - } else if (!entity.isRemoved()) { - this.touch(entity); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 948269a1ce9fe56bfa77e02a1671b993d5d44699..365849cce2cacd8c884d1e8e27b1b6723b1bb223 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -424,6 +424,7 @@ public class PurpurWorldConfig { - public boolean playerRidableInWater = false; - public boolean playerRemoveBindingWithWeakness = false; - public int shiftRightClickRepairsMendingPoints = 0; -+ public int playerExpPickupDelay = 2; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -453,6 +454,7 @@ public class PurpurWorldConfig { - playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); - playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness); - shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints); -+ playerExpPickupDelay = getInt("gameplay-mechanics.player.exp-pickup-delay-ticks", playerExpPickupDelay); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0240-Allow-void-trading.patch b/patches/server/0240-Allow-void-trading.patch deleted file mode 100644 index 203c3e2a8..000000000 --- a/patches/server/0240-Allow-void-trading.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 15 Jan 2022 03:27:29 -0600 -Subject: [PATCH] Allow void trading - - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 8cbb36b2dbcb9a1470c95746d7d481a142419164..122defeec159165c5fef295ec1dd2da4a6ada622 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -2837,7 +2837,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Spigot Start - if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message - // Paper start -- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { -+ if (!entity.level().purpurConfig.playerVoidTrading && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Purpur - merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); - } - // Paper end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 365849cce2cacd8c884d1e8e27b1b6723b1bb223..0f12ebf1ce10f66fd329e0b768891607238b52d1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -425,6 +425,7 @@ public class PurpurWorldConfig { - public boolean playerRemoveBindingWithWeakness = false; - public int shiftRightClickRepairsMendingPoints = 0; - public int playerExpPickupDelay = 2; -+ public boolean playerVoidTrading = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -455,6 +456,7 @@ public class PurpurWorldConfig { - playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness); - shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints); - playerExpPickupDelay = getInt("gameplay-mechanics.player.exp-pickup-delay-ticks", playerExpPickupDelay); -+ playerVoidTrading = getBoolean("gameplay-mechanics.player.allow-void-trading", playerVoidTrading); - } - - private static boolean projectileDespawnRateSettingsMigrated = false; diff --git a/patches/server/0241-Configurable-phantom-size.patch b/patches/server/0241-Configurable-phantom-size.patch deleted file mode 100644 index f5721ed8d..000000000 --- a/patches/server/0241-Configurable-phantom-size.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 17 Jan 2022 21:28:49 -0600 -Subject: [PATCH] Configurable phantom size - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index f71cbaabfff370f019f124203fb947ea7a817d95..17638b9d3340c86528a8ae597712c7590b98dba6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -271,7 +271,11 @@ public class Phantom extends FlyingMob implements Enemy { - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData entityData, @Nullable CompoundTag entityNbt) { - this.anchorPoint = this.blockPosition().above(5); -- this.setPhantomSize(0); -+ // Purpur start -+ int min = world.getLevel().purpurConfig.phantomMinSize; -+ int max = world.getLevel().purpurConfig.phantomMaxSize; -+ this.setPhantomSize(min == max ? min : world.getRandom().nextInt(max + 1 - min) + min); -+ // Purpur end - return super.finalizeSpawn(world, difficulty, spawnReason, entityData, entityNbt); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0f12ebf1ce10f66fd329e0b768891607238b52d1..fdb99c7565e0b23443d783fa01aa3f9cea5b3132 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2052,6 +2052,8 @@ public class PurpurWorldConfig { - public boolean phantomFlamesOnSwoop = false; - public boolean phantomTakeDamageFromWater = false; - public boolean phantomAlwaysDropExp = false; -+ public int phantomMinSize = 0; -+ public int phantomMaxSize = 0; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -2088,6 +2090,13 @@ public class PurpurWorldConfig { - phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop); - phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater); - phantomAlwaysDropExp = getBoolean("mobs.phantom.always-drop-exp", phantomAlwaysDropExp); -+ phantomMinSize = Mth.clamp(getInt("mobs.phantom.size.min", phantomMinSize), 0, 64); -+ phantomMaxSize = Mth.clamp(getInt("mobs.phantom.size.max", phantomMaxSize), 0, 64); -+ if (phantomMinSize > phantomMaxSize) { -+ phantomMinSize = phantomMinSize ^ phantomMaxSize; -+ phantomMaxSize = phantomMinSize ^ phantomMaxSize; -+ phantomMinSize = phantomMinSize ^ phantomMaxSize; -+ } - } - - public boolean pigRidable = false; diff --git a/patches/server/0243-Max-joins-per-second.patch b/patches/server/0243-Max-joins-per-second.patch deleted file mode 100644 index 8b6a30d7c..000000000 --- a/patches/server/0243-Max-joins-per-second.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Tue, 18 Jan 2022 06:35:54 -0600 -Subject: [PATCH] Max joins per second - -When this option is set to true the `max-joins-per-tick` setting in paper.yml will be used per second instead of per tick - -diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java -index cf20f0983fc25b26cf92b9d3a28746b1909fc56b..89c1b69ddeb420c2fbda5f588e7c9a467a76089d 100644 ---- a/src/main/java/net/minecraft/network/Connection.java -+++ b/src/main/java/net/minecraft/network/Connection.java -@@ -579,11 +579,20 @@ public class Connection extends SimpleChannelInboundHandler> { - private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - private static int joinAttemptsThisTick; // Paper - private static int currTick; // Paper -+ private static int tickSecond; // Purpur - public void tick() { - this.flushQueue(); - // Paper start - if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) { - Connection.currTick = net.minecraft.server.MinecraftServer.currentTick; -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) { -+ if (++Connection.tickSecond > 20) { -+ Connection.tickSecond = 0; -+ Connection.joinAttemptsThisTick = 0; -+ } -+ } else -+ // Purpur end - Connection.joinAttemptsThisTick = 0; - } - // Paper end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index fa81039056af14c37426a508b7b2da77e8b50737..44e8822dae170c07e48ae016826a6710b7d7e29a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -451,8 +451,10 @@ public class PurpurConfig { - } - - public static boolean useUPnP = false; -+ public static boolean maxJoinsPerSecond = false; - private static void networkSettings() { - useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP); -+ maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond); - } - - public static java.util.regex.Pattern usernameValidCharactersPattern; diff --git a/patches/server/0244-Configurable-minimum-demand-for-trades.patch b/patches/server/0244-Configurable-minimum-demand-for-trades.patch deleted file mode 100644 index 690b34a95..000000000 --- a/patches/server/0244-Configurable-minimum-demand-for-trades.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Totorewa <76272501+totorewa@users.noreply.github.com> -Date: Fri, 7 Jan 2022 21:34:57 +1300 -Subject: [PATCH] Configurable minimum demand for trades - -Addresses MC-163962 where villager demand decreases indefinitely. Paper -adds a patch to fix this by preventing demand from going below zero. -This patch adds a config option to allow the minimum demand to instead -be configurable. - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 903016cc402bc688f4fd92f339b242db6dfe3841..0d817148dedd9919e6e1dd0c38c96a57ad919ced 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -545,7 +545,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - while (iterator.hasNext()) { - MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); - -- merchantrecipe.updateDemand(); -+ merchantrecipe.updateDemand(this.level().purpurConfig.villagerMinimumDemand); // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java -index 28bdcb14cb5b458d3c990fcf343ef97f08e4f3c6..48167334162443365bb8a6d082a51b2c626ab3d8 100644 ---- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java -+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java -@@ -134,7 +134,12 @@ public class MerchantOffer { - } - - public void updateDemand() { -- this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper -+ // Purpur start -+ this.updateDemand(0); -+ } -+ public void updateDemand(int minimumDemand) { -+ this.demand = Math.max(minimumDemand, this.demand + this.uses - (this.maxUses - this.uses)); -+ // Purpur end - } - - public ItemStack assemble() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fdb99c7565e0b23443d783fa01aa3f9cea5b3132..fd141b5aaf45b6ae5aba56143c02f6a54133b9c8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2735,6 +2735,7 @@ public class PurpurWorldConfig { - public boolean villagerTakeDamageFromWater = false; - public boolean villagerAllowTrading = true; - public boolean villagerAlwaysDropExp = false; -+ public int villagerMinimumDemand = 0; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2755,6 +2756,7 @@ public class PurpurWorldConfig { - villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater); - villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); - villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp); -+ villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0245-Lobotomize-stuck-villagers.patch b/patches/server/0245-Lobotomize-stuck-villagers.patch deleted file mode 100644 index 01076a16b..000000000 --- a/patches/server/0245-Lobotomize-stuck-villagers.patch +++ /dev/null @@ -1,141 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 3 Dec 2020 17:56:18 -0600 -Subject: [PATCH] Lobotomize stuck villagers - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 0d817148dedd9919e6e1dd0c38c96a57ad919ced..3eb7ac97ea11d0c47ecbc047e2e64c17baaa70f4 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -140,6 +140,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> { - return holder.is(PoiTypes.MEETING); - }); -+ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur -+ private int notLobotomizedCount = 0; // Purpur - - public long nextGolemPanic = -1; // Pufferfish - -@@ -199,6 +201,47 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return this.level().purpurConfig.villagerAlwaysDropExp; - } - -+ private boolean checkLobotomized() { -+ int interval = this.level().purpurConfig.villagerLobotomizeCheckInterval; -+ if (this.notLobotomizedCount > 3) { -+ // check half as often if not lobotomized for the last 3+ consecutive checks -+ interval *= 2; -+ } -+ if (this.level().getGameTime() % interval == 0) { -+ // offset Y for short blocks like dirt_path/farmland -+ this.isLobotomized = !canTravelFrom(BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z)); -+ -+ if (this.isLobotomized) { -+ this.notLobotomizedCount = 0; -+ } else { -+ this.notLobotomizedCount++; -+ } -+ } -+ return this.isLobotomized; -+ } -+ -+ private boolean canTravelFrom(BlockPos pos) { -+ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south()); -+ } -+ -+ private boolean canTravelTo(BlockPos pos) { -+ net.minecraft.world.level.block.state.BlockState state = this.level().getBlockStateIfLoaded(pos); -+ if (state == null) { -+ // chunk not loaded -+ return false; -+ } -+ net.minecraft.world.level.block.Block bottom = state.getBlock(); -+ if (bottom instanceof net.minecraft.world.level.block.FenceBlock || -+ bottom instanceof net.minecraft.world.level.block.FenceGateBlock || -+ bottom instanceof net.minecraft.world.level.block.WallBlock) { -+ // bottom block is too tall to get over -+ return false; -+ } -+ net.minecraft.world.level.block.Block top = level().getBlockState(pos.above()).getBlock(); -+ // only if both blocks have no collision -+ return !bottom.hasCollision && !top.hasCollision; -+ } -+ - @Override - public Brain getBrain() { - return (Brain) super.getBrain(); // CraftBukkit - decompile error -@@ -296,14 +339,21 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - // Paper start - this.customServerAiStep(false); - } -- protected void customServerAiStep(final boolean inactive) { -+ protected void customServerAiStep(boolean inactive) { // Purpur - not final - // Paper end - this.level().getProfiler().push("villagerBrain"); -- // Pufferfish start -- if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) { // Purpur - only use brain if no rider -- this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ // Purpur start -+ if (this.level().purpurConfig.villagerLobotomizeEnabled) { -+ // treat as inactive if lobotomized -+ inactive = inactive || checkLobotomized(); -+ } else { -+ // clean up state for API -+ this.isLobotomized = false; - } -- // Pufferfish end -+ if (!inactive && (getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Purpur - only use brain if no rider -+ this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ else if (this.isLobotomized && shouldRestock()) restock(); -+ // Purpur end - this.level().getProfiler().pop(); - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -index f29e221e5b850516c169c03bfbd2b0885d1a841b..9e343d6e6393db17748fd13d76354464e128001f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -@@ -216,4 +216,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { - getHandle().getGossips().gossips.clear(); - } - // Paper end -+ -+ // Purpur start -+ @Override -+ public boolean isLobotomized() { -+ return getHandle().isLobotomized(); -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fd141b5aaf45b6ae5aba56143c02f6a54133b9c8..852d04096a1d2eb747183407f850cc9eafe44e1a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2736,6 +2736,8 @@ public class PurpurWorldConfig { - public boolean villagerAllowTrading = true; - public boolean villagerAlwaysDropExp = false; - public int villagerMinimumDemand = 0; -+ public boolean villagerLobotomizeEnabled = false; -+ public int villagerLobotomizeCheckInterval = 100; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2757,6 +2759,17 @@ public class PurpurWorldConfig { - villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); - villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp); - villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand); -+ if (PurpurConfig.version < 9) { -+ boolean oldValue = getBoolean("mobs.villager.lobotomize-1x1", villagerLobotomizeEnabled); -+ set("mobs.villager.lobotomize.enabled", oldValue); -+ set("mobs.villager.lobotomize-1x1", null); -+ } -+ if (PurpurConfig.version < 27) { -+ int oldValue = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); -+ set("mobs.villager.lobotomize.check-interval", oldValue == 60 ? 100 : oldValue); -+ } -+ villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); -+ villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0246-Option-for-villager-display-trade-item.patch b/patches/server/0246-Option-for-villager-display-trade-item.patch deleted file mode 100644 index 6514f646e..000000000 --- a/patches/server/0246-Option-for-villager-display-trade-item.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Tue, 25 Jan 2022 15:03:48 -0600 -Subject: [PATCH] Option for villager display trade item - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java -index 050be72c815010bf3f4b72427e2052b00420e8ee..8a213205f57c8dcd2226d7194d74b1b13dec5a78 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java -@@ -42,6 +42,7 @@ public class ShowTradesToPlayer extends Behavior { - - @Override - public boolean canStillUse(ServerLevel world, Villager entity, long time) { -+ if (!entity.level().purpurConfig.villagerDisplayTradeItem) return false; // Purpur - return this.checkExtraStartConditions(world, entity) && this.lookTime > 0 && entity.getBrain().getMemory(MemoryModuleType.INTERACTION_TARGET).isPresent(); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 852d04096a1d2eb747183407f850cc9eafe44e1a..265858cc747ed26c98cfea2caf27a9a0f7ea5198 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2738,6 +2738,7 @@ public class PurpurWorldConfig { - public int villagerMinimumDemand = 0; - public boolean villagerLobotomizeEnabled = false; - public int villagerLobotomizeCheckInterval = 100; -+ public boolean villagerDisplayTradeItem = true; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2770,6 +2771,7 @@ public class PurpurWorldConfig { - } - villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); - villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); -+ villagerDisplayTradeItem = getBoolean("mobs.villager.display-trade-item", villagerDisplayTradeItem); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0247-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch b/patches/server/0247-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch deleted file mode 100644 index ef6eccaa1..000000000 --- a/patches/server/0247-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 30 Jan 2022 02:03:34 -0600 -Subject: [PATCH] MC-238526 - Fix spawner not spawning water animals correctly - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -index cd2ce5bcb8c30e4657cd0e340d80544c7e805905..c8c6fed3f93903bb5c6145930538d415f6f59738 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -@@ -82,6 +82,6 @@ public abstract class WaterAnimal extends PathfinderMob { - i = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(i); - j = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(j); - // Paper end -- return pos.getY() >= j && pos.getY() <= i && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER); -+ return ((reason == MobSpawnType.SPAWNER && world.getMinecraftWorld().purpurConfig.spawnerFixMC238526) || (pos.getY() >= j && pos.getY() <= i)) && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER); // Purpur - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 265858cc747ed26c98cfea2caf27a9a0f7ea5198..15024b91f36fc2e2ab1942bc952d8f1619c94a47 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1002,8 +1002,10 @@ public class PurpurWorldConfig { - } - - public boolean spawnerDeactivateByRedstone = false; -+ public boolean spawnerFixMC238526 = false; - private void spawnerSettings() { - spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); -+ spawnerFixMC238526 = getBoolean("blocks.spawner.fix-mc-238526", spawnerFixMC238526); - } - - public int spongeAbsorptionArea = 65; diff --git a/patches/server/0248-Config-for-mob-last-hurt-by-player-time.patch b/patches/server/0248-Config-for-mob-last-hurt-by-player-time.patch deleted file mode 100644 index bbd976ea0..000000000 --- a/patches/server/0248-Config-for-mob-last-hurt-by-player-time.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Tue, 8 Feb 2022 13:35:48 -0600 -Subject: [PATCH] Config for mob last hurt by player time - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index aff8b1b0683e52038b6d92e052a012c9198bf8ce..ec3a9946b7387754c8f5a15ce6268a7fa666db02 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1502,13 +1502,13 @@ public abstract class LivingEntity extends Entity implements Attackable { - if (entity1 instanceof net.minecraft.world.entity.player.Player) { - net.minecraft.world.entity.player.Player entityhuman = (net.minecraft.world.entity.player.Player) entity1; - -- this.lastHurtByPlayerTime = 100; -+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - this.lastHurtByPlayer = entityhuman; - } else if (entity1 instanceof Wolf) { - Wolf entitywolf = (Wolf) entity1; - - if (entitywolf.isTame()) { -- this.lastHurtByPlayerTime = 100; -+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - LivingEntity entityliving2 = entitywolf.getOwner(); - - if (entityliving2 instanceof net.minecraft.world.entity.player.Player) { -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index 7f1c7532adace841baec500598133981a6feb78a..99dd93c066d4acc177de7f03ae45e491b535f4d1 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -453,7 +453,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - net.minecraft.server.level.ServerPlayer entityPlayer = killer == null ? null : ((CraftPlayer) killer).getHandle(); - getHandle().lastHurtByPlayer = entityPlayer; - getHandle().lastHurtByMob = entityPlayer; -- getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : 100; // 100 value taken from EntityLiving#damageEntity -+ getHandle().lastHurtByPlayerTime = entityPlayer == null ? 0 : getHandle().level().purpurConfig.mobLastHurtByPlayerTime; // 100 value taken from EntityLiving#damageEntity // Purpur - } - // Paper end - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 15024b91f36fc2e2ab1942bc952d8f1619c94a47..1ab0127d72fb6c8f7c3999768c7078dad1fced70 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -142,6 +142,7 @@ public class PurpurWorldConfig { - public boolean mobsIgnoreRails = false; - public boolean rainStopsAfterSleep = true; - public boolean thunderStopsAfterSleep = true; -+ public int mobLastHurtByPlayerTime = 100; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -167,6 +168,7 @@ public class PurpurWorldConfig { - mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails); - rainStopsAfterSleep = getBoolean("gameplay-mechanics.rain-stops-after-sleep", rainStopsAfterSleep); - thunderStopsAfterSleep = getBoolean("gameplay-mechanics.thunder-stops-after-sleep", thunderStopsAfterSleep); -+ mobLastHurtByPlayerTime = getInt("gameplay-mechanics.mob-last-hurt-by-player-time", mobLastHurtByPlayerTime); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0249-Anvil-repair-damage-options.patch b/patches/server/0249-Anvil-repair-damage-options.patch deleted file mode 100644 index 76278d584..000000000 --- a/patches/server/0249-Anvil-repair-damage-options.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Sat, 12 Feb 2022 01:08:18 +0100 -Subject: [PATCH] Anvil repair/damage options - - -diff --git a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java -index 5c5a3b169795bf8a527b316c666cbc2105c66622..020afeca950d2c7fb6c7b179d424548fd90f8b0d 100644 ---- a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java -@@ -55,6 +55,54 @@ public class AnvilBlock extends FallingBlock { - - @Override - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { -+ // Purpur start - repairable/damageable anvils -+ if (world.purpurConfig.anvilRepairIngotsAmount > 0) { -+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand); -+ if (itemstack.is(net.minecraft.world.item.Items.IRON_INGOT)) { -+ if (itemstack.getCount() < world.purpurConfig.anvilRepairIngotsAmount) { -+ // not enough iron ingots, play "error" sound and consume -+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); -+ return InteractionResult.CONSUME; -+ } -+ if (state.is(Blocks.DAMAGED_ANVIL)) { -+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); -+ } else if (state.is(Blocks.CHIPPED_ANVIL)) { -+ world.setBlock(pos, Blocks.ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); -+ } else if (state.is(Blocks.ANVIL)) { -+ // anvil is already fully repaired, play "error" sound and consume -+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); -+ return InteractionResult.CONSUME; -+ } -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(world.purpurConfig.anvilRepairIngotsAmount); -+ } -+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_PLACE, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); -+ return InteractionResult.CONSUME; -+ } -+ } -+ if (world.purpurConfig.anvilDamageObsidianAmount > 0) { -+ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand); -+ if (itemstack.is(net.minecraft.world.item.Items.OBSIDIAN)) { -+ if (itemstack.getCount() < world.purpurConfig.anvilDamageObsidianAmount) { -+ // not enough obsidian, play "error" sound and consume -+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); -+ return InteractionResult.CONSUME; -+ } -+ if (state.is(Blocks.DAMAGED_ANVIL)) { -+ world.destroyBlock(pos, false); -+ } else if (state.is(Blocks.CHIPPED_ANVIL)) { -+ world.setBlock(pos, Blocks.DAMAGED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); -+ } else if (state.is(Blocks.ANVIL)) { -+ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); -+ } -+ if (!player.getAbilities().instabuild) { -+ itemstack.shrink(world.purpurConfig.anvilDamageObsidianAmount); -+ } -+ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_LAND, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); -+ return InteractionResult.CONSUME; -+ } -+ } -+ // Purpur end - if (world.isClientSide) { - return InteractionResult.SUCCESS; - } else { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1ab0127d72fb6c8f7c3999768c7078dad1fced70..a279fdf4598af127b87bf21f8b74df15a1bd2664 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -754,9 +754,13 @@ public class PurpurWorldConfig { - - public boolean anvilAllowColors = false; - public boolean anvilColorsUseMiniMessage; -+ public int anvilRepairIngotsAmount = 0; -+ public int anvilDamageObsidianAmount = 0; - private void anvilSettings() { - anvilAllowColors = getBoolean("blocks.anvil.allow-colors", anvilAllowColors); - anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage); -+ anvilRepairIngotsAmount = getInt("blocks.anvil.iron-ingots-used-for-repair", anvilRepairIngotsAmount); -+ anvilDamageObsidianAmount = getInt("blocks.anvil.obsidian-used-for-damage", anvilDamageObsidianAmount); - } - - public double azaleaGrowthChance = 0.0D; diff --git a/patches/server/0250-Option-to-disable-turtle-egg-trampling-with-feather-.patch b/patches/server/0250-Option-to-disable-turtle-egg-trampling-with-feather-.patch deleted file mode 100644 index ca88b79f7..000000000 --- a/patches/server/0250-Option-to-disable-turtle-egg-trampling-with-feather-.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Mon, 9 May 2022 23:18:09 +0200 -Subject: [PATCH] Option to disable turtle egg trampling with feather falling - - -diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -index b211d7b37095b52424ead1bb0be0c81bc4322985..58facc048840d3ba0a58c92673340e6e0aa4e305 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -209,7 +209,13 @@ public class TurtleEggBlock extends Block { - if (!(entity instanceof LivingEntity)) { - return false; - } -- if (entity instanceof Player) return true; -+ if (world.purpurConfig.turtleEggsTramplingFeatherFalling) { -+ java.util.Iterator armor = entity.getArmorSlots().iterator(); -+ return !armor.hasNext() || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FALL_PROTECTION, armor.next()) < (int) entity.fallDistance; -+ } -+ if (entity instanceof Player) { -+ return true; -+ } - - return world.purpurConfig.turtleEggsBypassMobGriefing || world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); - // Purpur end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a279fdf4598af127b87bf21f8b74df15a1bd2664..64d7eb44358835c6293f02cb427785615a9ea874 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1030,12 +1030,14 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromMinecarts = true; - public boolean turtleEggsBypassMobGriefing = false; - public int turtleEggsRandomTickCrackChance = 500; -+ public boolean turtleEggsTramplingFeatherFalling = false; - private void turtleEggSettings() { - turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs); - turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems); - turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); - turtleEggsBypassMobGriefing = getBoolean("blocks.turtle_egg.bypass-mob-griefing", turtleEggsBypassMobGriefing); - turtleEggsRandomTickCrackChance = getInt("blocks.turtle_egg.random-tick-crack-chance", turtleEggsRandomTickCrackChance); -+ turtleEggsTramplingFeatherFalling = getBoolean("blocks.turtle_egg.feather-fall-distance-affects-trampling", turtleEggsTramplingFeatherFalling); - } - - public int waterInfiniteRequiredSources = 2; diff --git a/patches/server/0251-Add-toggle-for-enchant-level-clamping.patch b/patches/server/0251-Add-toggle-for-enchant-level-clamping.patch deleted file mode 100644 index 0ec4dbe62..000000000 --- a/patches/server/0251-Add-toggle-for-enchant-level-clamping.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Sat, 30 Apr 2022 10:32:40 +0200 -Subject: [PATCH] Add toggle for enchant level clamping - - -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 8c8f605c4d17e2dd5dec450bd4f9853421b0f44a..f76f912501fc647f759dbd54b2b220f5b95c1407 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -1201,7 +1201,7 @@ public final class ItemStack { - - ListTag nbttaglist = this.tag.getList("Enchantments", 10); - -- nbttaglist.add(EnchantmentHelper.storeEnchantment(EnchantmentHelper.getEnchantmentId(enchantment), (byte) level)); -+ nbttaglist.add(EnchantmentHelper.storeEnchantment(EnchantmentHelper.getEnchantmentId(enchantment), (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels) ? (byte) level : (short) level)); // Purpur - processEnchantOrder(this.tag); // Paper - } - -diff --git a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -index 2048899f8e4c8211e8dde0d11148d647678009fa..1eec84e217f6dc929091fa7451cd235ef3623822 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -@@ -46,7 +46,7 @@ public class EnchantmentHelper { - } - - public static int getEnchantmentLevel(CompoundTag nbt) { -- return Mth.clamp(nbt.getInt("lvl"), 0, 255); -+ return Mth.clamp(nbt.getInt("lvl"), 0, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels) ? 255 : 32767); // Purpur - } - - @Nullable -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 44e8822dae170c07e48ae016826a6710b7d7e29a..4d147845402a26957c905dd600bf0657bb7bd714 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -398,6 +398,7 @@ public class PurpurConfig { - public static boolean allowHigherEnchantsLevels = true; - public static boolean allowUnsafeEnchantCommand = false; - public static boolean replaceIncompatibleEnchants = false; -+ public static boolean clampEnchantLevels = true; - private static void enchantmentSettings() { - if (version < 5) { - boolean oldValue = getBoolean("settings.enchantment.allow-infinite-and-mending-together", false); -@@ -421,6 +422,7 @@ public class PurpurConfig { - allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels); - allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchants); // allowUnsafeEnchants as default for backwards compatability - replaceIncompatibleEnchants = getBoolean("settings.enchantment.anvil.replace-incompatible-enchants", replaceIncompatibleEnchants); -+ clampEnchantLevels = getBoolean("settings.enchantment.clamp-levels", clampEnchantLevels); - } - - public static boolean endermanShortHeight = false; diff --git a/patches/server/0253-Implement-configurable-search-radius-for-villagers-t.patch b/patches/server/0253-Implement-configurable-search-radius-for-villagers-t.patch deleted file mode 100644 index 112b14a81..000000000 --- a/patches/server/0253-Implement-configurable-search-radius-for-villagers-t.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 8 Jun 2022 14:13:39 -0400 -Subject: [PATCH] Implement configurable search radius for villagers to spawn - iron golems - - -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 3eb7ac97ea11d0c47ecbc047e2e64c17baaa70f4..85c8854691070d8867a5eaf410fe7a2f75fadb9f 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -1092,6 +1092,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - - public void spawnGolemIfNeeded(ServerLevel world, long time, int requiredCount) { -+ if (world.purpurConfig.villagerSpawnIronGolemRadius > 0 && world.getEntitiesOfClass(net.minecraft.world.entity.animal.IronGolem.class, getBoundingBox().inflate(world.purpurConfig.villagerSpawnIronGolemRadius)).size() > world.purpurConfig.villagerSpawnIronGolemLimit) return; // Purpur - if (this.wantsToSpawnGolem(time)) { - AABB axisalignedbb = this.getBoundingBox().inflate(10.0D, 10.0D, 10.0D); - List list = world.getEntitiesOfClass(Villager.class, axisalignedbb); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 64d7eb44358835c6293f02cb427785615a9ea874..147c25389a57da3d13c513225488d4e1160a7831 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2749,6 +2749,8 @@ public class PurpurWorldConfig { - public boolean villagerLobotomizeEnabled = false; - public int villagerLobotomizeCheckInterval = 100; - public boolean villagerDisplayTradeItem = true; -+ public int villagerSpawnIronGolemRadius = 0; -+ public int villagerSpawnIronGolemLimit = 0; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2782,6 +2784,8 @@ public class PurpurWorldConfig { - villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); - villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); - villagerDisplayTradeItem = getBoolean("mobs.villager.display-trade-item", villagerDisplayTradeItem); -+ villagerSpawnIronGolemRadius = getInt("mobs.villager.spawn-iron-golem.radius", villagerSpawnIronGolemRadius); -+ villagerSpawnIronGolemLimit = getInt("mobs.villager.spawn-iron-golem.limit", villagerSpawnIronGolemLimit); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0254-Stonecutter-damage.patch b/patches/server/0254-Stonecutter-damage.patch deleted file mode 100644 index 2e024d81d..000000000 --- a/patches/server/0254-Stonecutter-damage.patch +++ /dev/null @@ -1,122 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 8 Jun 2022 14:19:35 -0400 -Subject: [PATCH] Stonecutter damage - - -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -index c6f8ae130b75fbb0ad41f9a5917e934e33fd3043..45e4717ba832edceeafdba575323c2527c350193 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -102,6 +102,8 @@ public class CombatTracker { - // Purpur start - if (damageSource.isScissors) { - return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgRunWithScissors, this.mob); -+ } else if (damageSource.isStoneCutter) { -+ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgStonecutter, this.mob); - } - // Purpur end - return damageSource.getLocalizedDeathMessage(this.mob); -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index 33210bc66dcc5fdf03fbf438ce8734b31c4e7975..fc1fb63ee0e28b8d1f065bfad716b465cde5a69f 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -56,6 +56,8 @@ public class DamageSource { - - public boolean isScissors; // Purpur - -+ public boolean isStoneCutter; // Purpur -+ - public String toString() { - return "DamageSource (" + this.type().msgId() + ")"; - } -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -index 813916852774d6482791989252ecb67b945a8f84..4a5d2e263d2bbee96bde7012d3385fa33860bc1b 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -@@ -273,5 +273,11 @@ public class DamageSources { - source.isScissors = true; - return source; - } -+ -+ public DamageSource stonecutter() { -+ DamageSource source = new DamageSource(this.damageTypes.getHolderOrThrow(DamageTypes.MAGIC)); -+ source.isStoneCutter = true; -+ return source; -+ } - // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -index 0a95842c53a9d0286c57bcb42db97e468e30fb7d..0882e67c5cf876e0fc58a4ca4accb4be40418983 100644 ---- a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -@@ -92,4 +92,16 @@ public class StonecutterBlock extends Block { - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { - return false; - } -+ -+ // Purpur start -+ @Override -+ public void stepOn(Level level, BlockPos pos, BlockState state, net.minecraft.world.entity.Entity entity) { -+ if (level.purpurConfig.stonecutterDamage > 0.0F && entity instanceof net.minecraft.world.entity.LivingEntity) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); -+ entity.hurt(entity.damageSources().stonecutter(), level.purpurConfig.stonecutterDamage); -+ org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; -+ } -+ super.stepOn(level, pos, state, entity); -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -index d976a6df54c1e817def2d588692abe25a03ee0fa..ba57accc272958da4714896baeadb52c99383561 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -465,7 +465,7 @@ public class WalkNodeEvaluator extends NodeEvaluator { - return BlockPathTypes.BLOCKED; - } else { - // Paper end -- if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH)) { -+ if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH) || blockState.is(Blocks.STONECUTTER)) { // Purpur - return BlockPathTypes.DANGER_OTHER; - } - -@@ -498,7 +498,7 @@ public class WalkNodeEvaluator extends NodeEvaluator { - } else if (!blockState.is(BlockTags.TRAPDOORS) && !blockState.is(Blocks.LILY_PAD) && !blockState.is(Blocks.BIG_DRIPLEAF)) { - if (blockState.is(Blocks.POWDER_SNOW)) { - return BlockPathTypes.POWDER_SNOW; -- } else if (!blockState.is(Blocks.CACTUS) && !blockState.is(Blocks.SWEET_BERRY_BUSH)) { -+ } else if (!blockState.is(Blocks.CACTUS) && !blockState.is(Blocks.SWEET_BERRY_BUSH) && !blockState.is(Blocks.STONECUTTER)) { // Purpur - if (blockState.is(Blocks.HONEY_BLOCK)) { - return BlockPathTypes.STICKY_HONEY; - } else if (blockState.is(Blocks.COCOA)) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 33a67078ba6ecc11d2929a164354702664d6a480..15eb4403deefca5f16052a74bbd9059091ed4c3f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -209,8 +209,10 @@ public class PurpurConfig { - } - - public static String deathMsgRunWithScissors = " slipped and fell on their shears"; -+ public static String deathMsgStonecutter = " has sawed themself in half"; - private static void deathMessages() { - deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors); -+ deathMsgStonecutter = getString("settings.messages.death-message.stonecutter", deathMsgStonecutter); - } - - public static boolean advancementOnlyBroadcastToAffectedPlayer = false; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 147c25389a57da3d13c513225488d4e1160a7831..fe5fe3faab9fd7977139181cc8a40dd16e06e23d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1025,6 +1025,11 @@ public class PurpurWorldConfig { - spongeAbsorbsWaterFromMud = getBoolean("blocks.sponge.absorbs-water-from-mud", spongeAbsorbsWaterFromMud); - } - -+ public float stonecutterDamage = 0.0F; -+ private void stonecutterSettings() { -+ stonecutterDamage = (float) getDouble("blocks.stonecutter.damage", stonecutterDamage); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = true; - public boolean turtleEggsBreakFromItems = true; - public boolean turtleEggsBreakFromMinecarts = true; diff --git a/patches/server/0255-Configurable-damage-settings-for-magma-blocks.patch b/patches/server/0255-Configurable-damage-settings-for-magma-blocks.patch deleted file mode 100644 index 09ec3b25b..000000000 --- a/patches/server/0255-Configurable-damage-settings-for-magma-blocks.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Wed, 8 Jun 2022 14:32:55 -0400 -Subject: [PATCH] Configurable damage settings for magma blocks - - -diff --git a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java -index 1b766045687e4dcded5cbcc50b746c55b9a34e22..be365914856593bb3c4e1945cc990786072f2953 100644 ---- a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java -@@ -22,7 +22,7 @@ public class MagmaBlock extends Block { - - @Override - public void stepOn(Level world, BlockPos pos, BlockState state, Entity entity) { -- if (!entity.isSteppingCarefully() && entity instanceof LivingEntity && !EnchantmentHelper.hasFrostWalker((LivingEntity) entity)) { -+ if ((!entity.isSteppingCarefully() || world.purpurConfig.magmaBlockDamageWhenSneaking) && entity instanceof LivingEntity && (world.purpurConfig.magmaBlockDamageWithFrostWalker || !EnchantmentHelper.hasFrostWalker((LivingEntity) entity))) { // Purpur - org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); // CraftBukkit - entity.hurt(world.damageSources().hotFloor(), 1.0F); - org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; // CraftBukkit -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fe5fe3faab9fd7977139181cc8a40dd16e06e23d..6e37d135cb88443cdbb379ea2a90374bf1383ec7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -956,6 +956,13 @@ public class PurpurWorldConfig { - pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit); - } - -+ public boolean magmaBlockDamageWhenSneaking = false; -+ public boolean magmaBlockDamageWithFrostWalker = false; -+ private void magmaBlockSettings() { -+ magmaBlockDamageWhenSneaking = getBoolean("blocks.magma-block.damage-when-sneaking", magmaBlockDamageWhenSneaking); -+ magmaBlockDamageWithFrostWalker = getBoolean("blocks.magma-block.damage-with-frost-walker", magmaBlockDamageWithFrostWalker); -+ } -+ - public boolean powderSnowBypassMobGriefing = false; - private void powderSnowSettings() { - powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing); diff --git a/patches/server/0256-Add-config-for-snow-on-blue-ice.patch b/patches/server/0256-Add-config-for-snow-on-blue-ice.patch deleted file mode 100644 index fc24b8cf5..000000000 --- a/patches/server/0256-Add-config-for-snow-on-blue-ice.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 8 Jun 2022 15:19:41 -0400 -Subject: [PATCH] Add config for snow on blue ice - - -diff --git a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java -index 14e00c7feb1c051d56a3d27cd00dcef072dd771a..4952fb1aaaafb55baa0fddb389f966a120a4786c 100644 ---- a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java -@@ -81,6 +81,12 @@ public class SnowLayerBlock extends Block { - public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { - BlockState iblockdata1 = world.getBlockState(pos.below()); - -+ // Purpur start -+ if (iblockdata1.is(Blocks.BLUE_ICE) && !world.getWorldBorder().world.purpurConfig.snowOnBlueIce) { -+ return false; -+ } -+ // Purpur end -+ - return iblockdata1.is(BlockTags.SNOW_LAYER_CANNOT_SURVIVE_ON) ? false : (iblockdata1.is(BlockTags.SNOW_LAYER_CAN_SURVIVE_ON) ? true : Block.isFaceFull(iblockdata1.getCollisionShape(world, pos.below()), Direction.UP) || iblockdata1.is((Block) this) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 8); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6e37d135cb88443cdbb379ea2a90374bf1383ec7..67266a22f05ffc94a375af873339a9c718ddd66c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -937,9 +937,11 @@ public class PurpurWorldConfig { - - public boolean mobsSpawnOnPackedIce = true; - public boolean mobsSpawnOnBlueIce = true; -+ public boolean snowOnBlueIce = true; - private void iceSettings() { - mobsSpawnOnPackedIce = getBoolean("blocks.packed_ice.allow-mob-spawns", mobsSpawnOnPackedIce); - mobsSpawnOnBlueIce = getBoolean("blocks.blue_ice.allow-mob-spawns", mobsSpawnOnBlueIce); -+ snowOnBlueIce = getBoolean("blocks.blue_ice.allow-snow-formation", snowOnBlueIce); - } - - public int lavaInfiniteRequiredSources = 2; diff --git a/patches/server/0257-Skeletons-eat-wither-roses.patch b/patches/server/0257-Skeletons-eat-wither-roses.patch deleted file mode 100644 index 60ded7f6d..000000000 --- a/patches/server/0257-Skeletons-eat-wither-roses.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sat, 25 Jun 2022 00:18:33 -0400 -Subject: [PATCH] Skeletons eat wither roses - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -index 9b43150a00d7fac85aa69f5a2dbffd0dfdae4b86..f4a47ac2c86cc95178922cce7320ba1ef5121b57 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -@@ -14,6 +14,16 @@ import net.minecraft.world.item.Items; - import net.minecraft.world.level.ItemLike; - import net.minecraft.world.level.Level; - -+// Purpur start -+import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.level.block.Blocks; -+import org.bukkit.craftbukkit.event.CraftEventFactory; -+import net.minecraft.world.InteractionHand; -+import net.minecraft.world.InteractionResult; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.core.particles.ParticleTypes; -+// Purpur end -+ - public class Skeleton extends AbstractSkeleton { - - private static final int TOTAL_CONVERSION_TIME = 300; -@@ -174,4 +184,67 @@ public class Skeleton extends AbstractSkeleton { - } - - } -+ -+ // Purpur start -+ private int witherRosesFed = 0; -+ -+ @Override -+ public InteractionResult mobInteract(Player player, InteractionHand hand) { -+ ItemStack stack = player.getItemInHand(hand); -+ -+ if (level().purpurConfig.skeletonFeedWitherRoses > 0 && this.getType() != EntityType.WITHER_SKELETON && stack.getItem() == Blocks.WITHER_ROSE.asItem()) { -+ return this.feedWitherRose(player, stack); -+ } -+ -+ return super.mobInteract(player, hand); -+ } -+ -+ private InteractionResult feedWitherRose(Player player, ItemStack stack) { -+ if (++witherRosesFed < level().purpurConfig.skeletonFeedWitherRoses) { -+ if (!player.getAbilities().instabuild) { -+ stack.shrink(1); -+ } -+ return InteractionResult.CONSUME; -+ } -+ -+ WitherSkeleton skeleton = EntityType.WITHER_SKELETON.create(level()); -+ if (skeleton == null) { -+ return InteractionResult.PASS; -+ } -+ -+ skeleton.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); -+ skeleton.setHealth(this.getHealth()); -+ skeleton.setAggressive(this.isAggressive()); -+ skeleton.copyPosition(this); -+ skeleton.setYBodyRot(this.yBodyRot); -+ skeleton.setYHeadRot(this.getYHeadRot()); -+ skeleton.yRotO = this.yRotO; -+ skeleton.xRotO = this.xRotO; -+ -+ if (this.hasCustomName()) { -+ skeleton.setCustomName(this.getCustomName()); -+ } -+ -+ if (CraftEventFactory.callEntityTransformEvent(this, skeleton, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { -+ return InteractionResult.PASS; -+ } -+ -+ if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), skeleton.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) { -+ return InteractionResult.PASS; -+ } -+ -+ this.level().addFreshEntity(skeleton); -+ this.remove(RemovalReason.DISCARDED); -+ if (!player.getAbilities().instabuild) { -+ stack.shrink(1); -+ } -+ -+ for (int i = 0; i < 15; ++i) { -+ ((ServerLevel) level()).sendParticles(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER, -+ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1, -+ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0, true); -+ } -+ return InteractionResult.SUCCESS; -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 67266a22f05ffc94a375af873339a9c718ddd66c..13413402b516f892264072f4061c509d7ed1d94c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2433,6 +2433,7 @@ public class PurpurWorldConfig { - public boolean skeletonTakeDamageFromWater = false; - public boolean skeletonAlwaysDropExp = false; - public double skeletonHeadVisibilityPercent = 0.5D; -+ public int skeletonFeedWitherRoses = 0; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -2446,6 +2447,7 @@ public class PurpurWorldConfig { - skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater); - skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp); - skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent); -+ skeletonFeedWitherRoses = getInt("mobs.skeleton.feed-wither-roses", skeletonFeedWitherRoses); - } - - public boolean skeletonHorseRidableInWater = true; diff --git a/patches/server/0258-Enchantment-Table-Persists-Lapis.patch b/patches/server/0258-Enchantment-Table-Persists-Lapis.patch deleted file mode 100644 index 98ba43458..000000000 --- a/patches/server/0258-Enchantment-Table-Persists-Lapis.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sat, 25 Jun 2022 08:04:06 -0400 -Subject: [PATCH] Enchantment Table Persists Lapis - - -diff --git a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java -index 93e8316f9d64625dcc4df0644a2187bcc884ef65..1711406996ec4a9cb7b1838bd2446e5cf802e043 100644 ---- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java -@@ -38,6 +38,12 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent; - import org.bukkit.entity.Player; - // CraftBukkit end - -+// Purpur start -+import net.minecraft.world.level.block.entity.BlockEntity; -+import net.minecraft.world.level.block.entity.EnchantmentTableBlockEntity; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+// Purpur end -+ - public class EnchantmentMenu extends AbstractContainerMenu { - - private final Container enchantSlots; -@@ -71,6 +77,22 @@ public class EnchantmentMenu extends AbstractContainerMenu { - return context.getLocation(); - } - // CraftBukkit end -+ -+ // Purpur start -+ @Override -+ public void onClose(CraftHumanEntity who) { -+ super.onClose(who); -+ -+ if (who.getHandle().level().purpurConfig.enchantmentTableLapisPersists) { -+ access.execute((level, pos) -> { -+ BlockEntity blockEntity = level.getBlockEntity(pos); -+ if (blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) { -+ enchantmentTable.setLapis(this.getItem(1).getCount()); -+ } -+ }); -+ } -+ } -+ // Purpur end - }; - this.random = RandomSource.create(); - this.enchantmentSeed = DataSlot.standalone(); -@@ -96,6 +118,17 @@ public class EnchantmentMenu extends AbstractContainerMenu { - } - }); - -+ // Purpur start -+ access.execute((level, pos) -> { -+ if (level.purpurConfig.enchantmentTableLapisPersists) { -+ BlockEntity blockEntity = level.getBlockEntity(pos); -+ if (blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) { -+ this.getSlot(1).set(new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis())); -+ } -+ } -+ }); -+ // Purpur end -+ - int j; - - for (j = 0; j < 3; ++j) { -@@ -340,6 +373,7 @@ public class EnchantmentMenu extends AbstractContainerMenu { - public void removed(net.minecraft.world.entity.player.Player player) { - super.removed(player); - this.access.execute((world, blockposition) -> { -+ if (world.purpurConfig.enchantmentTableLapisPersists) this.getSlot(1).set(ItemStack.EMPTY); // Purpur - this.clearContainer(player, this.enchantSlots); - }); - } -diff --git a/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java b/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java -index 839b7bc9392906dca384003468746963631fe095..286f34eef22a85be3fe9747dc3c3f9a7d51f437c 100644 ---- a/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EnchantmentTableBlock.java -@@ -29,6 +29,8 @@ import net.minecraft.world.level.pathfinder.PathComputationType; - import net.minecraft.world.phys.BlockHitResult; - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; -+import net.minecraft.world.Containers; // Purpur -+import net.minecraft.world.item.Items; // Purpur - - public class EnchantmentTableBlock extends BaseEntityBlock { - protected static final VoxelShape SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D); -@@ -121,4 +123,18 @@ public class EnchantmentTableBlock extends BaseEntityBlock { - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { - return false; - } -+ -+ // Purpur start -+ @Override -+ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean moved) { -+ BlockEntity blockEntity = level.getBlockEntity(pos); -+ -+ if (level.purpurConfig.enchantmentTableLapisPersists && blockEntity instanceof EnchantmentTableBlockEntity enchantmentTable) { -+ Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis())); -+ level.updateNeighbourForOutputSignal(pos, this); -+ } -+ -+ super.onRemove(state, level, pos, newState, moved); -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java -index 65e1381bb2d10bd212463feb602c60f8fdb9ade1..b7370e64fd0d50e8725d7d5afc30af2e8bc8455d 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/EnchantmentTableBlockEntity.java -@@ -24,6 +24,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable - public float tRot; - private static final RandomSource RANDOM = RandomSource.create(); - private Component name; -+ private int lapis = 0; // Purpur - - public EnchantmentTableBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.ENCHANTING_TABLE, pos, state); -@@ -35,6 +36,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable - if (this.hasCustomName()) { - nbt.putString("CustomName", Component.Serializer.toJson(this.name)); - } -+ nbt.putInt("Purpur.Lapis", this.lapis); // Purpur - - } - -@@ -44,6 +46,7 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable - if (nbt.contains("CustomName", 8)) { - this.name = io.papermc.paper.util.MCUtil.getBaseComponentFromNbt("CustomName", nbt); // Paper - Catch ParseException - } -+ this.lapis = nbt.getInt("Purpur.Lapis"); // Purpur - - } - -@@ -117,4 +120,14 @@ public class EnchantmentTableBlockEntity extends BlockEntity implements Nameable - public Component getCustomName() { - return this.name; - } -+ -+ // Purpur start -+ public int getLapis() { -+ return this.lapis; -+ } -+ -+ public void setLapis(int lapis) { -+ this.lapis = lapis; -+ } -+ // Purpur - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 13413402b516f892264072f4061c509d7ed1d94c..261d451d47c00ea36748e4f610a2f1e27a4b6ab4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1468,6 +1468,11 @@ public class PurpurWorldConfig { - elderGuardianAlwaysDropExp = getBoolean("mobs.elder_guardian.always-drop-exp", elderGuardianAlwaysDropExp); - } - -+ public boolean enchantmentTableLapisPersists = false; -+ private void enchantmentTableSettings() { -+ enchantmentTableLapisPersists = getBoolean("blocks.enchantment-table.lapis-persists", enchantmentTableLapisPersists); -+ } -+ - public boolean enderDragonRidable = false; - public boolean enderDragonRidableInWater = true; - public boolean enderDragonControllable = true; diff --git a/patches/server/0259-Spark-Profiler.patch b/patches/server/0259-Spark-Profiler.patch deleted file mode 100644 index c1d92008a..000000000 --- a/patches/server/0259-Spark-Profiler.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ben Kerllenevich -Date: Sat, 25 Jun 2022 19:40:36 -0400 -Subject: [PATCH] Spark Profiler - -Co-authored-by: granny - -diff --git a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java -index 89bf48fd581ee6580b91e2eb31dd532cb622df5e..e35da199be67e04c34df6bc09afd8d8122cb0487 100644 ---- a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java -+++ b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java -@@ -102,6 +102,7 @@ public class PluginInitializerManager { - java.util.List files = (java.util.List) optionSet.valuesOf("add-plugin"); - // Register plugins from the flag - io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.PluginFlagProviderSource.INSTANCE, files); -+ io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.SparkProviderSource.INSTANCE, new File("cache", "spark.jar").toPath()); // Purpur - } - - // This will be the end of me... -diff --git a/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a7d1ae53eac94bc2dcf8bc78ef1da0d3b8554736 ---- /dev/null -+++ b/src/main/java/io/papermc/paper/plugin/provider/source/SparkProviderSource.java -@@ -0,0 +1,102 @@ -+package io.papermc.paper.plugin.provider.source; -+ -+import com.mojang.logging.LogUtils; -+import io.papermc.paper.plugin.entrypoint.Entrypoint; -+import io.papermc.paper.plugin.entrypoint.EntrypointHandler; -+import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler; -+import io.papermc.paper.plugin.provider.PluginProvider; -+import java.io.BufferedReader; -+import java.io.File; -+import java.io.InputStreamReader; -+import java.math.BigInteger; -+import java.net.URL; -+import java.net.URLConnection; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.nio.file.StandardCopyOption; -+import java.security.MessageDigest; -+import java.util.stream.Collectors; -+import org.bukkit.plugin.java.JavaPlugin; -+import org.slf4j.Logger; -+ -+public class SparkProviderSource extends FileProviderSource { -+ public static final SparkProviderSource INSTANCE = new SparkProviderSource(); -+ -+ private static final Logger LOGGER = LogUtils.getClassLogger(); -+ -+ public SparkProviderSource() { -+ super("File '%s' specified by Purpur"::formatted); -+ } -+ -+ @Override -+ public void registerProviders(EntrypointHandler entrypointHandler, Path context) throws Exception { -+ // first, check if user doesn't want spark at all -+ if (Boolean.getBoolean("Purpur.IReallyDontWantSpark")) { -+ return; // boo! -+ } -+ -+ // second, check if user has their own spark -+ if (hasSpark()) { -+ LOGGER.info("Purpur: Using user-provided spark plugin instead of our own."); -+ return; // let's hope it's at least the modern version :3 -+ } -+ -+ // you can't have errors in your code if you wrap the entire codebase in a try/catch block -+ try { -+ -+ // make sure the directory exists where we want to keep spark -+ File file = context.toFile(); -+ file.getParentFile().mkdirs(); -+ -+ boolean shouldDownload; -+ -+ // check if our spark exists -+ if (!file.exists()) { -+ // it does not, so let's download it -+ shouldDownload = true; -+ } else { -+ // we have a spark file, let's see if it's up-to-date by comparing shas -+ String fileSha1 = String.format("%040x", new BigInteger(1, MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(file.toPath())))); -+ String sparkSha1; -+ -+ // luck has a nifty endpoint containing the sha of the newest version -+ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit/sha1").openConnection(); -+ -+ // set a reasonable timeout to prevent servers without internet from hanging for 60+ seconds on startup -+ urlConnection.setReadTimeout(5000); -+ urlConnection.setConnectTimeout(5000); -+ -+ // read it -+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) { -+ sparkSha1 = reader.lines().collect(Collectors.joining("")); -+ } -+ -+ // compare; we only download a new spark if the shas don't match -+ shouldDownload = !fileSha1.equals(sparkSha1); -+ } -+ -+ // ok, finally we can download spark if we need it -+ if (shouldDownload) { -+ URLConnection urlConnection = new URL("https://sparkapi.lucko.me/download/bukkit").openConnection(); -+ urlConnection.setReadTimeout(5000); -+ urlConnection.setConnectTimeout(5000); -+ Files.copy(urlConnection.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); -+ } -+ -+ // register the spark, newly downloaded or existing -+ super.registerProviders(entrypointHandler, context); -+ -+ } catch (Throwable e) { -+ LOGGER.error("Purpur: Failed to download and install spark plugin", e); -+ } -+ } -+ -+ private static boolean hasSpark() { -+ for (PluginProvider provider : LaunchEntryPointHandler.INSTANCE.get(Entrypoint.PLUGIN).getRegisteredProviders()) { -+ if (provider.getMeta().getName().equalsIgnoreCase("spark")) { -+ return true; -+ } -+ } -+ return false; -+ } -+} diff --git a/patches/server/0260-Option-to-disable-kick-for-out-of-order-chat.patch b/patches/server/0260-Option-to-disable-kick-for-out-of-order-chat.patch deleted file mode 100644 index a8be2efe8..000000000 --- a/patches/server/0260-Option-to-disable-kick-for-out-of-order-chat.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 3 Jul 2022 04:13:57 -0500 -Subject: [PATCH] Option to disable kick for out of order chat - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index cb1851fca9290edb502662c100bb2ac6fa4d70d1..b1050654cca5ce8a834c9e913b8e804609499aa9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2510,7 +2510,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - do { - instant1 = (Instant) this.lastChatTimeStamp.get(); - if (timestamp.isBefore(instant1)) { -- return false; -+ return !org.purpurmc.purpur.PurpurConfig.kickForOutOfOrderChat; // Purpur - } - } while (!this.lastChatTimeStamp.compareAndSet(instant1, timestamp)); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index e78e809cc7644d5007b149d5940f8cc164a76975..ed9b2f0b55229848894d9d6b401d050cb031b893 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -456,9 +456,11 @@ public class PurpurConfig { - - public static boolean useUPnP = false; - public static boolean maxJoinsPerSecond = false; -+ public static boolean kickForOutOfOrderChat = true; - private static void networkSettings() { - useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP); - maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond); -+ kickForOutOfOrderChat = getBoolean("settings.network.kick-for-out-of-order-chat", kickForOutOfOrderChat); - } - - public static java.util.regex.Pattern usernameValidCharactersPattern; diff --git a/patches/server/0261-Config-for-sculk-shrieker-can_summon-state.patch b/patches/server/0261-Config-for-sculk-shrieker-can_summon-state.patch deleted file mode 100644 index 6b5ac1959..000000000 --- a/patches/server/0261-Config-for-sculk-shrieker-can_summon-state.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Mon, 4 Jul 2022 13:32:51 -0400 -Subject: [PATCH] Config for sculk shrieker can_summon state - - -diff --git a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java -index 02d01eabb9606ae8c3b76ad9fa4bb9a525e247b1..ce51fec4a874f9466f9966684c535315dbf40b9e 100644 ---- a/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SculkShriekerBlock.java -@@ -130,7 +130,7 @@ public class SculkShriekerBlock extends BaseEntityBlock implements SimpleWaterlo - @Nullable - @Override - public BlockState getStateForPlacement(BlockPlaceContext ctx) { -- return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER); -+ return (BlockState) this.defaultBlockState().setValue(SculkShriekerBlock.WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).getType() == Fluids.WATER).setValue(SculkShriekerBlock.CAN_SUMMON, ctx.getLevel().purpurConfig.sculkShriekerCanSummonDefault); // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 261d451d47c00ea36748e4f610a2f1e27a4b6ab4..9f7ca8b8f79fdea1dc664639dc1f09c76610a043 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1001,6 +1001,11 @@ public class PurpurWorldConfig { - fixSandDuping = getBoolean("blocks.sand.fix-duping", fixSandDuping); - } - -+ public boolean sculkShriekerCanSummonDefault = false; -+ private void sculkShriekerSettings() { -+ sculkShriekerCanSummonDefault = getBoolean("blocks.sculk_shrieker.can-summon-default", sculkShriekerCanSummonDefault); -+ } -+ - public boolean shulkerBoxAllowOversizedStacks = false; - private void shulkerBoxSettings() { - shulkerBoxAllowOversizedStacks = getBoolean("blocks.shulker_box.allow-oversized-stacks", shulkerBoxAllowOversizedStacks); diff --git a/patches/server/0262-Config-to-not-let-coral-die.patch b/patches/server/0262-Config-to-not-let-coral-die.patch deleted file mode 100644 index 90bebff8d..000000000 --- a/patches/server/0262-Config-to-not-let-coral-die.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Mon, 4 Jul 2022 13:57:06 -0400 -Subject: [PATCH] Config to not let coral die - - -diff --git a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java -index 3d2b34c5a7c9b00c1164b4f89c2cbff81fc460eb..b5505e926e5cdb447de68e8eb8e46c97eb988e27 100644 ---- a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java -@@ -35,6 +35,7 @@ public class BaseCoralPlantTypeBlock extends Block implements SimpleWaterloggedB - } - - protected static boolean scanForWater(BlockState state, BlockGetter world, BlockPos pos) { -+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - if (state.getValue(WATERLOGGED)) { - return true; - } else { -diff --git a/src/main/java/net/minecraft/world/level/block/CoralBlock.java b/src/main/java/net/minecraft/world/level/block/CoralBlock.java -index 88faea00be60a519f56f975a5311df5e1eb3e6b8..cbb726ac367be81e27d3a86643baf7c4f0746edf 100644 ---- a/src/main/java/net/minecraft/world/level/block/CoralBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CoralBlock.java -@@ -45,6 +45,7 @@ public class CoralBlock extends Block { - } - - protected boolean scanForWater(BlockGetter world, BlockPos pos) { -+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Direction[] aenumdirection = Direction.values(); - int i = aenumdirection.length; - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 9f7ca8b8f79fdea1dc664639dc1f09c76610a043..ce7c96dd55ad88ad35fef9aa992952b8ac333d06 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -838,6 +838,11 @@ public class PurpurWorldConfig { - composterBulkProcess = getBoolean("blocks.composter.sneak-to-bulk-process", composterBulkProcess); - } - -+ public boolean coralDieOutsideWater = true; -+ private void coralSettings() { -+ coralDieOutsideWater = getBoolean("blocks.coral.die-outside-water", coralDieOutsideWater); -+ } -+ - public boolean dispenserApplyCursedArmor = true; - public boolean dispenserPlaceAnvils = false; - private void dispenserSettings() { diff --git a/patches/server/0263-Add-local-difficulty-api.patch b/patches/server/0263-Add-local-difficulty-api.patch deleted file mode 100644 index 58f0258a5..000000000 --- a/patches/server/0263-Add-local-difficulty-api.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 9 Jul 2022 00:57:32 -0500 -Subject: [PATCH] Add local difficulty api - - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index f857f490ffba2f25f7c06c5fb1a1905f0b51fbe2..a2e852adf47261b1b2eb9734cc90f4676ed58126 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2290,6 +2290,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { - return (this.getHandle().getDragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().getDragonFight()); - } - -+ // Purpur start -+ public float getLocalDifficultyAt(Location location) { -+ return getHandle().getCurrentDifficultyAt(io.papermc.paper.util.MCUtil.toBlockPosition(location)).getEffectiveDifficulty(); -+ } -+ // Purpur end -+ - @Override - public PersistentDataContainer getPersistentDataContainer() { - return this.persistentDataContainer; diff --git a/patches/server/0264-Add-toggle-for-RNG-manipulation.patch b/patches/server/0264-Add-toggle-for-RNG-manipulation.patch deleted file mode 100644 index 475b95f4a..000000000 --- a/patches/server/0264-Add-toggle-for-RNG-manipulation.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Encode42 -Date: Tue, 12 Jul 2022 14:16:10 -0400 -Subject: [PATCH] Add toggle for RNG manipulation - -Paper patches RNG maniplulation by using a shared (and locked) random source. -This comes with a performance gain, but technical players may prefer the ability to manipulate RNG. - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index c99d273b8d686872d5699e4490769786951256e1..156c2683d72c5e83b1cefb9ebf356c6e4d31e7f4 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -616,7 +616,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.bb = Entity.INITIAL_AABB; - this.stuckSpeedMultiplier = Vec3.ZERO; - this.nextStep = 1.0F; -- this.random = SHARED_RANDOM; // Paper -+ this.random = world == null || world.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create(); // Paper // Purpur - this.remainingFireTicks = -this.getFireImmuneTicks(); - this.fluidHeight = new Object2DoubleArrayMap(2); - this.fluidOnEyes = new HashSet(); -diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java -index 88c238e492b1081d1a64a3b6f05d7baa17e5d8c9..dd7f2beabf0edad4143ac2365ac04a22edf1f75e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -44,7 +44,7 @@ public class Squid extends WaterAnimal { - - public Squid(EntityType type, Level world) { - super(type, world); -- //this.random.setSeed((long)this.getId()); // Paper - we set the random to shared, do not clobber the seed -+ if (!world.purpurConfig.entitySharedRandom) this.random.setSeed((long) this.getId()); // Paper - we set the random to shared, do not clobber the seed // Purpur - this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ce7c96dd55ad88ad35fef9aa992952b8ac333d06..370b5ff992fb3ff7dd88ab479b3c251d36ced42f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -204,9 +204,11 @@ public class PurpurWorldConfig { - - public int entityLifeSpan = 0; - public float entityLeftHandedChance = 0.05f; -+ public boolean entitySharedRandom = true; - private void entitySettings() { - entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); - entityLeftHandedChance = (float) getDouble("gameplay-mechanics.entity-left-handed-chance", entityLeftHandedChance); -+ entitySharedRandom = getBoolean("settings.entity.shared-random", entitySharedRandom); - } - - public boolean infinityWorksWithoutArrows = false; diff --git a/patches/server/0266-PaperPR-7822-Fix-exact-choice-recipe-book-clicks.patch b/patches/server/0266-PaperPR-7822-Fix-exact-choice-recipe-book-clicks.patch deleted file mode 100644 index 98df8a3c0..000000000 --- a/patches/server/0266-PaperPR-7822-Fix-exact-choice-recipe-book-clicks.patch +++ /dev/null @@ -1,129 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jake Potrebic -Date: Sat, 14 May 2022 15:42:34 -0700 -Subject: [PATCH] PaperPR #7822 Fix exact choice recipe book clicks - - -diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java -index 68d272e98f9e54c9b150c75c27a9ae545be842f6..63fc6074082b579c3f88316008cbeb2a2d7f5841 100644 ---- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java -+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java -@@ -40,8 +40,62 @@ public class StackedContents { - int j = Math.min(maxCount, stack.getCount()); - if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it - this.put(i, j); -+ // PaperPR start -+ if (stack.hasTag()) { -+ this.put(getExactStackingIndex(stack), j); -+ } -+ } -+ -+ } -+ private static final net.minecraft.core.IdMap EXACT_MATCHES_ID_MAP = new net.minecraft.core.IdMap<>() { -+ private final java.util.concurrent.atomic.AtomicInteger idCounter = new java.util.concurrent.atomic.AtomicInteger(BuiltInRegistries.ITEM.size()); -+ private final it.unimi.dsi.fastutil.objects.Object2IntMap itemstackToId = new it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap<>(new it.unimi.dsi.fastutil.Hash.Strategy<>() { -+ @Override -+ public int hashCode(ItemStack o) { -+ return java.util.Objects.hash(o.getItem(), o.getTag()); -+ } -+ -+ @Override -+ public boolean equals(@Nullable ItemStack a, @Nullable ItemStack b) { -+ if (a == null || b == null) { -+ return false; -+ } -+ return ItemStack.matches(a, b); -+ } -+ }); -+ private final it.unimi.dsi.fastutil.ints.Int2ObjectMap idToItemstack = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(); -+ -+ @Override -+ public int getId(ItemStack value) { -+ if (!this.itemstackToId.containsKey(value)) { -+ final int id = this.idCounter.incrementAndGet(); -+ final ItemStack copy = value.copy(); -+ this.itemstackToId.put(copy, id); -+ this.idToItemstack.put(id, copy); -+ return id; -+ } -+ return this.itemstackToId.getInt(value); -+ } -+ -+ @Override -+ public @Nullable ItemStack byId(int index) { -+ return this.idToItemstack.get(index); -+ } -+ -+ @Override -+ public int size() { -+ return this.itemstackToId.size(); -+ } -+ -+ @Override -+ public java.util.Iterator iterator() { -+ return this.idToItemstack.values().iterator(); - } -+ }; - -+ public static int getExactStackingIndex(ItemStack stack) { -+ return EXACT_MATCHES_ID_MAP.getId(stack); -+ // PaperPR end - } - - public static int getStackingIndex(ItemStack stack) { -@@ -83,6 +137,12 @@ public class StackedContents { - } - - public static ItemStack fromStackingIndex(int itemId) { -+ // PaperPR start -+ if (itemId > BuiltInRegistries.ITEM.size()) { -+ final ItemStack stack = EXACT_MATCHES_ID_MAP.byId(itemId); -+ return stack == null ? ItemStack.EMPTY : stack.copy(); -+ } -+ // PaperPR end - return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId)); - } - -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index f76f912501fc647f759dbd54b2b220f5b95c1407..7e68596e28db88213e9352f798c5a4ce37cc5656 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -113,6 +113,7 @@ import org.bukkit.event.world.StructureGrowEvent; - - public final class ItemStack { - -+ public boolean isExactRecipeIngredient = false; // PaperPR - public static final Codec CODEC = RecordCodecBuilder.create((instance) -> { - return instance.group(BuiltInRegistries.ITEM.byNameCodec().fieldOf("id").forGetter(ItemStack::getItem), Codec.INT.fieldOf("Count").forGetter(ItemStack::getCount), CompoundTag.CODEC.optionalFieldOf("tag").forGetter((itemstack) -> { - return Optional.ofNullable(itemstack.getTag()); -diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -index 3ca086418ad037c48775db73d2b9c410acf1e326..f47eab4c31925f51de4a6bc8be730281cb3388fc 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -@@ -51,7 +51,11 @@ public final class Ingredient implements Predicate { - if (this.itemStacks == null) { - this.itemStacks = (ItemStack[]) Arrays.stream(this.values).flatMap((recipeitemstack_provider) -> { - return recipeitemstack_provider.getItems().stream(); -- }).distinct().toArray((i) -> { -+ // PaperPR start -+ }).distinct().peek(stack -> { -+ stack.isExactRecipeIngredient = this.exact; -+ }).toArray((i) -> { -+ // PaperPR end - return new ItemStack[i]; - }); - } -@@ -106,7 +110,13 @@ public final class Ingredient implements Predicate { - for (int j = 0; j < i; ++j) { - ItemStack itemstack = aitemstack1[j]; - -+ // PaperPR start -+ if (itemstack.isExactRecipeIngredient) { -+ this.stackingIds.add(StackedContents.getExactStackingIndex(itemstack)); -+ } else { -+ // PaperPR end - this.stackingIds.add(StackedContents.getStackingIndex(itemstack)); -+ } // PaperPR - } - - this.stackingIds.sort(IntComparators.NATURAL_COMPARATOR); diff --git a/patches/server/0267-Allow-custom-ChatDecorators.patch b/patches/server/0267-Allow-custom-ChatDecorators.patch deleted file mode 100644 index 6a1e5cb38..000000000 --- a/patches/server/0267-Allow-custom-ChatDecorators.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 11 Jul 2022 20:44:19 -0500 -Subject: [PATCH] Allow custom ChatDecorators - -Requires NMS to utilize. I'll write an API for this once our upstreams calm down with the changes. - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 1f6946e1120232d3b104e76212c470f47ed7cde1..fb52feb1028747caf942dd534b6f994bfb94ffa5 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -2672,6 +2672,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - final io.papermc.paper.adventure.ChatDecorationProcessor processor = new io.papermc.paper.adventure.ChatDecorationProcessor(this, sender, commandSourceStack, message); diff --git a/patches/server/0268-Remove-Timings.patch b/patches/server/0268-Remove-Timings.patch deleted file mode 100644 index 0c03815e3..000000000 --- a/patches/server/0268-Remove-Timings.patch +++ /dev/null @@ -1,939 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 1 Jul 2022 04:03:37 -0500 -Subject: [PATCH] Remove Timings - - -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -index abd0217cf0bff183c8e262edc173a53403797c1a..2519ad2884b6c09b312432b933c31476b369e599 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -@@ -1315,9 +1315,9 @@ public final class ChunkHolderManager { - } - - public boolean processTicketUpdates() { -- co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager -+ //co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager // Purpur - return this.processTicketUpdates(true, true, null); -- } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager -+ //} finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager // Purpur - } - - private static final ThreadLocal> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>(); -diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java -index 51304c5cf4b0ac7646693ef97ef4a3847d3342b5..535ab99585cd4463d051334681bc80b5d20df7c0 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java -+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java -@@ -1779,7 +1779,7 @@ public final class NewChunkHolder { - boolean canSavePOI = !(chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave) && (poi != null && poi.isDirty()); - boolean canSaveEntities = entities != null; - -- try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { // Paper -+ //try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { // Paper // Purpur - if (canSaveChunk) { - canSaveChunk = this.saveChunk(chunk, unloading); - } -@@ -1793,7 +1793,7 @@ public final class NewChunkHolder { - this.lastEntityUnload = null; - } - } -- } -+ //} // Purpur - - return executedUnloadTask | canSaveChunk | canSaveEntities | canSavePOI ? new SaveStat(executedUnloadTask || canSaveChunk, canSaveEntities, canSavePOI): null; - } -diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -index d2f0a0755317f5fa9a1ccf7db346aa77fd287d80..03852e7d21d9470a4469676367463fefb38acdc6 100644 ---- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java -+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -@@ -47,7 +47,7 @@ public class PacketUtils { - if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) return; // CraftBukkit, MC-142590 - if (listener.isAcceptingMessages()) { - co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings -- try (co.aikar.timings.Timing ignored = timing.startTiming()) { // Paper - timings -+ try { // Paper - timings // Purpur - packet.handle(listener); - } catch (Exception exception) { - if (listener.shouldPropagateHandlingExceptions()) { -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index fb52feb1028747caf942dd534b6f994bfb94ffa5..9711ba8466e939a047c6656231a5a0250a5435b7 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -314,7 +314,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { -@@ -1405,15 +1405,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - return !this.canOversleep(); - }); -- isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); -+ //isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); // Purpur - // Paper end - new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - -@@ -1448,9 +1448,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -@@ -1522,21 +1522,21 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop list = Lists.newArrayList(); - List list1 = this.level.players(); - ObjectIterator objectiterator = this.entityMap.values().iterator(); -- level.timings.tracker1.startTiming(); // Paper -+ //level.timings.tracker1.startTiming(); // Paper // Purpur - - ChunkMap.TrackedEntity playerchunkmap_entitytracker; - -@@ -1326,17 +1326,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - playerchunkmap_entitytracker.serverEntity.sendChanges(); - } - } -- level.timings.tracker1.stopTiming(); // Paper -+ //level.timings.tracker1.stopTiming(); // Paper // Purpur - - if (!list.isEmpty()) { - objectiterator = this.entityMap.values().iterator(); - -- level.timings.tracker2.startTiming(); // Paper -+ //level.timings.tracker2.startTiming(); // Paper // Purpur - while (objectiterator.hasNext()) { - playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); - playerchunkmap_entitytracker.updatePlayers(list); - } -- level.timings.tracker2.stopTiming(); // Paper -+ //level.timings.tracker2.stopTiming(); // Paper // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index ef6c98c7949d2feb3ec2a0b2be161978989b6771..2b13f177d154ff322837e055e15fcb55941a2306 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -299,10 +299,10 @@ public class ServerChunkCache extends ChunkSource { - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system - // Paper end - com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info -- this.level.timings.syncChunkLoad.startTiming(); // Paper -+ //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur - chunkproviderserver_b.managedBlock(completablefuture::isDone); - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system -- this.level.timings.syncChunkLoad.stopTiming(); // Paper -+ //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur - } // Paper - ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { - return ichunkaccess1; -@@ -451,17 +451,17 @@ public class ServerChunkCache extends ChunkSource { - - public void save(boolean flush) { - this.runDistanceManagerUpdates(); -- try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings -+ //try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings // Purpur - this.chunkMap.saveAllChunks(flush); -- } // Paper - Timings -+ //} // Paper - Timings // Purpur - } - - // Paper start - duplicate save, but call incremental - public void saveIncrementally() { - this.runDistanceManagerUpdates(); -- try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings -+ //try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings // Purpur - this.chunkMap.saveIncrementally(); -- } // Paper - Timings -+ //} // Paper - Timings // Purpur - } - // Paper end - -@@ -491,22 +491,22 @@ public class ServerChunkCache extends ChunkSource { - @Override - public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) { - this.level.getProfiler().push("purge"); -- this.level.timings.doChunkMap.startTiming(); // Spigot -+ //this.level.timings.doChunkMap.startTiming(); // Spigot // Purpur - this.distanceManager.purgeStaleTickets(); - this.runDistanceManagerUpdates(); -- this.level.timings.doChunkMap.stopTiming(); // Spigot -+ //this.level.timings.doChunkMap.stopTiming(); // Spigot // Purpur - this.level.getProfiler().popPush("chunks"); - if (tickChunks) { -- this.level.timings.chunks.startTiming(); // Paper - timings -+ //this.level.timings.chunks.startTiming(); // Paper - timings // Purpur - this.chunkMap.level.playerChunkLoader.tick(); // Paper - replace player chunk loader - this is mostly required to account for view distance changes - this.tickChunks(); -- this.level.timings.chunks.stopTiming(); // Paper - timings -+ //this.level.timings.chunks.stopTiming(); // Paper - timings // Purpur - } - -- this.level.timings.doChunkUnload.startTiming(); // Spigot -+ //this.level.timings.doChunkUnload.startTiming(); // Spigot // Purpur - this.level.getProfiler().popPush("unload"); - this.chunkMap.tick(shouldKeepTicking); -- this.level.timings.doChunkUnload.stopTiming(); // Spigot -+ //this.level.timings.doChunkUnload.stopTiming(); // Spigot // Purpur - this.level.getProfiler().pop(); - this.clearCache(); - } -@@ -561,7 +561,7 @@ public class ServerChunkCache extends ChunkSource { - boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit - - gameprofilerfiller.push("naturalSpawnCount"); -- this.level.timings.countNaturalMobs.startTiming(); // Paper - timings -+ //this.level.timings.countNaturalMobs.startTiming(); // Paper - timings // Purpur - int l = this.distanceManager.getNaturalSpawnChunkCount(); - // Paper start - per player mob spawning - NaturalSpawner.SpawnState spawnercreature_d; // moved down -@@ -592,12 +592,12 @@ public class ServerChunkCache extends ChunkSource { - // Pufferfish end - } - // Paper end -- this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings -+ //this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings // Purpur - - //this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously - gameprofilerfiller.popPush("filteringLoadedChunks"); - // Paper - moved down -- this.level.timings.chunkTicks.startTiming(); // Paper -+ //this.level.timings.chunkTicks.startTiming(); // Paper // Purpur - - // Paper - moved down - -@@ -652,17 +652,17 @@ public class ServerChunkCache extends ChunkSource { - } - } - // Paper end - optimise chunk tick iteration -- this.level.timings.chunkTicks.stopTiming(); // Paper -+ //this.level.timings.chunkTicks.stopTiming(); // Paper // Purpur - gameprofilerfiller.popPush("customSpawners"); - if (flag2) { -- try (co.aikar.timings.Timing ignored = this.level.timings.miscMobSpawning.startTiming()) { // Paper - timings -+ //try (co.aikar.timings.Timing ignored = this.level.timings.miscMobSpawning.startTiming()) { // Paper - timings // Purpur - this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); -- } // Paper - timings -+ //} // Paper - timings // Purpur - } - gameprofilerfiller.pop(); - // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded - gameprofilerfiller.popPush("broadcast"); -- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing -+ //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur - if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { - ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); - this.chunkMap.needsChangeBroadcasting.clear(); -@@ -674,7 +674,7 @@ public class ServerChunkCache extends ChunkSource { - } - } - } -- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing -+ //this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing // Purpur - gameprofilerfiller.pop(); - // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded - // Paper start - controlled flush for entity tracker packets -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 122defeec159165c5fef295ec1dd2da4a6ada622..5b7507db055d7be59b369f66e659e128c9c7e00b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -828,7 +828,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.updateSkyBrightness(); - this.tickTime(); - gameprofilerfiller.popPush("tickPending"); -- timings.scheduledBlocks.startTiming(); // Paper -+ //timings.scheduledBlocks.startTiming(); // Paper // Purpur - if (!this.isDebug()) { - j = this.getGameTime(); - gameprofilerfiller.push("blockTicks"); -@@ -837,20 +837,20 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.fluidTicks.tick(j, 65536, this::tickFluid); - gameprofilerfiller.pop(); - } -- timings.scheduledBlocks.stopTiming(); // Paper -+ //timings.scheduledBlocks.stopTiming(); // Paper // Purpur - - gameprofilerfiller.popPush("raid"); -- this.timings.raids.startTiming(); // Paper - timings -+ //this.timings.raids.startTiming(); // Paper - timings // Purpur - this.raids.tick(); -- this.timings.raids.stopTiming(); // Paper - timings -+ //this.timings.raids.stopTiming(); // Paper - timings // Purpur - gameprofilerfiller.popPush("chunkSource"); -- this.timings.chunkProviderTick.startTiming(); // Paper - timings -+ //this.timings.chunkProviderTick.startTiming(); // Paper - timings // Purpur - this.getChunkSource().tick(shouldKeepTicking, true); -- this.timings.chunkProviderTick.stopTiming(); // Paper - timings -+ //this.timings.chunkProviderTick.stopTiming(); // Paper - timings // Purpur - gameprofilerfiller.popPush("blockEvents"); -- timings.doSounds.startTiming(); // Spigot -+ //timings.doSounds.startTiming(); // Spigot // Purpur - this.runBlockEvents(); -- timings.doSounds.stopTiming(); // Spigot -+ //timings.doSounds.stopTiming(); // Spigot // Purpur - this.handlingTick = false; - gameprofilerfiller.pop(); - boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players -@@ -861,7 +861,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - - if (flag || this.emptyTime++ < 300) { - gameprofilerfiller.push("entities"); -- timings.tickEntities.startTiming(); // Spigot -+ //timings.tickEntities.startTiming(); // Spigot // Purpur - if (this.dragonFight != null) { - gameprofilerfiller.push("dragonFight"); - this.dragonFight.tick(); -@@ -869,7 +869,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - org.spigotmc.ActivationRange.activateEntities(this); // Spigot -- timings.entityTick.startTiming(); // Spigot -+ //timings.entityTick.startTiming(); // Spigot // Purpur - this.entityTickList.forEach((entity) -> { - entity.activatedPriorityReset = false; // Pufferfish - DAB - if (!entity.isRemoved()) { -@@ -910,8 +910,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - } - }); -- timings.entityTick.stopTiming(); // Spigot -- timings.tickEntities.stopTiming(); // Spigot -+ //timings.entityTick.stopTiming(); // Spigot // Purpur -+ //timings.tickEntities.stopTiming(); // Spigot // Purpur - gameprofilerfiller.pop(); - this.tickBlockEntities(); - } -@@ -1093,7 +1093,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - - // Paper start - optimise random block ticking - gameprofilerfiller.popPush("randomTick"); -- timings.chunkTicksBlocks.startTiming(); // Paper -+ //timings.chunkTicksBlocks.startTiming(); // Paper // Purpur - if (randomTickSpeed > 0) { - LevelChunkSection[] sections = chunk.getSections(); - int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this); -@@ -1127,7 +1127,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - } - // Paper end - optimise random block ticking -- timings.chunkTicksBlocks.stopTiming(); // Paper -+ //timings.chunkTicksBlocks.stopTiming(); // Paper // Purpur - gameprofilerfiller.pop(); - } - -@@ -1420,8 +1420,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Spigot end - // Paper start- timings - final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); -- timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper -- try { -+ //timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper // Purpur -+ //try { // Purpur - // Paper end - timings - entity.setOldPosAndRot(); - ProfilerFiller gameprofilerfiller = this.getProfiler(); -@@ -1437,7 +1437,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - entity.postTick(); // CraftBukkit - } else { entity.inactiveTick(); } // Paper - EAR 2 - this.getProfiler().pop(); -- } finally { timer.stopTiming(); } // Paper - timings -+ //} finally { timer.stopTiming(); } // Paper - timings // Purpur - Iterator iterator = entity.getPassengers().iterator(); - - while (iterator.hasNext()) { -@@ -1460,8 +1460,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - if (passenger instanceof Player || this.entityTickList.contains(passenger)) { - // Paper - EAR 2 - final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger); -- co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper -- try { -+ //co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper // Purpur -+ //try { // Purpur - // Paper end - passenger.setOldPosAndRot(); - ++passenger.tickCount; -@@ -1491,7 +1491,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.tickPassenger(passenger, entity2); - } - -- } finally { timer.stopTiming(); }// Paper - EAR2 timings -+ //} finally { timer.stopTiming(); }// Paper - EAR2 timings // Purpur - } - } else { - passenger.stopRiding(); -@@ -1511,14 +1511,14 @@ public class ServerLevel extends Level implements WorldGenLevel { - org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); - } - -- try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { -+ //try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { // Purpur - if (doFull) { - this.saveLevelData(); - } - -- this.timings.worldSaveChunks.startTiming(); // Paper -+ //this.timings.worldSaveChunks.startTiming(); // Paper // Purpur - if (!this.noSave()) chunkproviderserver.saveIncrementally(); -- this.timings.worldSaveChunks.stopTiming(); // Paper -+ //this.timings.worldSaveChunks.stopTiming(); // Paper // Purpur - - // Copied from save() - // CraftBukkit start - moved from MinecraftServer.saveChunks -@@ -1530,7 +1530,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); - } - // CraftBukkit end -- } -+ //} // Purpur - } - // Paper end - -@@ -1544,7 +1544,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - - if (!savingDisabled) { - org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit -- try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper -+ //try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper // Purpur // Purpur - if (progressListener != null) { - progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel")); - } -@@ -1554,11 +1554,11 @@ public class ServerLevel extends Level implements WorldGenLevel { - progressListener.progressStage(Component.translatable("menu.savingChunks")); - } - -- timings.worldSaveChunks.startTiming(); // Paper -+ //timings.worldSaveChunks.startTiming(); // Paper // Purpur - if (!close) chunkproviderserver.save(flush); // Paper - rewrite chunk system - if (close) chunkproviderserver.close(true); // Paper - rewrite chunk system -- timings.worldSaveChunks.stopTiming(); // Paper -- }// Paper -+ //timings.worldSaveChunks.stopTiming(); // Paper // Purpur -+ //}// Paper // Purpur - // Paper - rewrite chunk system - entity saving moved into ChunkHolder - - } else if (close) { chunkproviderserver.close(false); } // Paper - rewrite chunk system -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index b1050654cca5ce8a834c9e913b8e804609499aa9..5feaf5bb7e366f593d5700fb78243a824ce97f9c 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2641,7 +2641,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - } - // Paper End -- co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper -+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper // Purpur - if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot - this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s); - -@@ -2651,7 +2651,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.cserver.getPluginManager().callEvent(event); - - if (event.isCancelled()) { -- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper -+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper // Purpur - return; - } - -@@ -2664,7 +2664,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - java.util.logging.Logger.getLogger(ServerGamePacketListenerImpl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); - return; - } finally { -- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper -+ //co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper // Purpur - } - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 43fdebcd107a7ca08b93de1d649b4d5e6acf5e81..824f31b68b38f2f8642fb9d59a123cfdaffbb7b2 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1257,7 +1257,7 @@ public abstract class PlayerList { - - public void saveAll(int interval) { - io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main -- MinecraftTimings.savePlayers.startTiming(); // Paper -+ //MinecraftTimings.savePlayers.startTiming(); // Paper // Purpur - int numSaved = 0; - long now = MinecraftServer.currentTick; - for (int i = 0; i < this.players.size(); ++i) { -@@ -1268,7 +1268,7 @@ public abstract class PlayerList { - } - // Paper end - } -- MinecraftTimings.savePlayers.stopTiming(); // Paper -+ //MinecraftTimings.savePlayers.stopTiming(); // Paper // Purpur - return null; }); // Paper - ensure main - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java -index 57ef7fbba3028c28231abf7b7ae78aa019323536..651c156dc8a5aad04d461add02e22147af657d07 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java -@@ -58,9 +58,9 @@ public abstract class Behavior implements BehaviorContro - this.status = Behavior.Status.RUNNING; - int i = this.minDuration + world.getRandom().nextInt(this.maxDuration + 1 - this.minDuration); - this.endTimestamp = time + (long)i; -- this.timing.startTiming(); // Paper - behavior timings -+ //this.timing.startTiming(); // Paper - behavior timings // Purpur - this.start(world, entity, time); -- this.timing.stopTiming(); // Paper - behavior timings -+ //this.timing.stopTiming(); // Paper - behavior timings // Purpur - return true; - } else { - return false; -@@ -72,13 +72,13 @@ public abstract class Behavior implements BehaviorContro - - @Override - public final void tickOrStop(ServerLevel world, E entity, long time) { -- this.timing.startTiming(); // Paper - behavior timings -+ //this.timing.startTiming(); // Paper - behavior timings // Purpur - if (!this.timedOut(time) && this.canStillUse(world, entity, time)) { - this.tick(world, entity, time); - } else { - this.doStop(world, entity, time); - } -- this.timing.stopTiming(); // Paper - behavior timings -+ //this.timing.stopTiming(); // Paper - behavior timings // Purpur - - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java -index fcdb9bde8e1605e30dde3e580491522d4b62cdc0..7094701d213c73ba47ace806962244c10fdf4dda 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java -@@ -46,10 +46,10 @@ public abstract class Sensor { - if (--this.timeToTick <= 0L) { - // Paper start - configurable sensor tick rate and timings - this.timeToTick = java.util.Objects.requireNonNullElse(world.paperConfig().tickRates.sensor.get(entity.getType(), this.configKey), this.scanRate); -- this.timing.startTiming(); -+ //this.timing.startTiming(); // Purpur - // Paper end - this.doTick(world, entity); -- this.timing.stopTiming(); // Paper - sensor timings -+ //this.timing.stopTiming(); // Paper - sensor timings // Purpur - } - - } -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 84ac5db63a51d1490fcf625d10ed0952d671017e..ed7ed29147a2bb681e5d4c44d34b06d936b8fa74 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1016,15 +1016,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - ProfilerFiller gameprofilerfiller = this.getProfiler(); - - gameprofilerfiller.push("blockEntities"); -- timings.tileEntityPending.startTiming(); // Spigot -+ //timings.tileEntityPending.startTiming(); // Spigot // Purpur - this.tickingBlockEntities = true; - if (!this.pendingBlockEntityTickers.isEmpty()) { - this.blockEntityTickers.addAll(this.pendingBlockEntityTickers); - this.pendingBlockEntityTickers.clear(); - } -- timings.tileEntityPending.stopTiming(); // Spigot -+ //timings.tileEntityPending.stopTiming(); // Spigot // Purpur - -- timings.tileEntityTick.startTiming(); // Spigot -+ //timings.tileEntityTick.startTiming(); // Spigot // Purpur - // Spigot start - // Iterator iterator = this.blockEntityTickers.iterator(); - int tilesThisCycle = 0; -@@ -1057,7 +1057,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - this.blockEntityTickers.removeAll(toRemove); - -- timings.tileEntityTick.stopTiming(); // Spigot -+ //timings.tileEntityTick.stopTiming(); // Spigot // Purpur - this.tickingBlockEntities = false; - co.aikar.timings.TimingHistory.tileEntityTicks += this.blockEntityTickers.size(); // Paper - gameprofilerfiller.pop(); -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 49de1fca183b2c6a0a5399025abfc0e47f314315..482dfb2bbf8b85ba98bf164c5cf3edcdf927eb98 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -133,7 +133,7 @@ public final class NaturalSpawner { - - public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) { - world.getProfiler().push("spawner"); -- world.timings.mobSpawn.startTiming(); // Spigot -+ //world.timings.mobSpawn.startTiming(); // Spigot // Purpur - MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES; - int i = aenumcreaturetype.length; - -@@ -188,7 +188,7 @@ public final class NaturalSpawner { - } - } - -- world.timings.mobSpawn.stopTiming(); // Spigot -+ //world.timings.mobSpawn.stopTiming(); // Spigot // Purpur - world.getProfiler().pop(); - } - -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index dbb0593a6feb60216379bde6720ca16f3ca827ae..4dab159024cf91cd297dcb31833f9d57e2132326 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -895,7 +895,7 @@ public class LevelChunk extends ChunkAccess { - this.chunkHolder.getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system - - if (this.needsDecoration) { -- try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper -+ //try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper // Purpur - this.needsDecoration = false; - java.util.Random random = new java.util.Random(); - random.setSeed(this.level.getSeed()); -@@ -915,7 +915,7 @@ public class LevelChunk extends ChunkAccess { - } - } - server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); -- } // Paper -+ //} // Paper // Purpur - } - } - } -@@ -1273,7 +1273,7 @@ public class LevelChunk extends ChunkAccess { - ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler(); - - gameprofilerfiller.push(this::getType); -- this.blockEntity.tickTimer.startTiming(); // Spigot -+ //this.blockEntity.tickTimer.startTiming(); // Spigot // Purpur - BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); - - if (this.blockEntity.getType().isValid(iblockdata)) { -@@ -1295,7 +1295,7 @@ public class LevelChunk extends ChunkAccess { - // Paper end - // Spigot start - } finally { -- this.blockEntity.tickTimer.stopTiming(); -+ //this.blockEntity.tickTimer.stopTiming(); // Purpur - // Spigot end - } - } -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 5a47a8785bc2e251d041f80a79295c43459de3bc..a4567188e2fe3f922bb6aeb71a2845d1a1be536f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -513,10 +513,10 @@ public class CraftScheduler implements BukkitScheduler { - this.runners.remove(task.getTaskId()); - } - } -- MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper -+ //MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper // Purpur - this.pending.addAll(temp); - temp.clear(); -- MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper -+ //MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper // Purpur - //this.debugHead = this.debugHead.getNextHead(currentTick); // Paper - } - -@@ -559,7 +559,7 @@ public class CraftScheduler implements BukkitScheduler { - } - - void parsePending() { // Paper -- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper -+ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper // Purpur - CraftTask head = this.head; - CraftTask task = head.getNext(); - CraftTask lastTask = head; -@@ -578,7 +578,7 @@ public class CraftScheduler implements BukkitScheduler { - task.setNext(null); - } - this.head = lastTask; -- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper -+ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper // Purpur - } - - private boolean isReady(final int currentTick) { -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java -index 3f45bab0e9f7b3697e6d9d1092a1e6e579f7066f..4f1cf281c4bf68c37982d390da8779dea78dab18 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java -@@ -96,13 +96,13 @@ public class CraftTask implements BukkitTask, Runnable { // Spigot - - @Override - public void run() { -- try (Timing ignored = timings.startTiming()) { // Paper -+ //try (Timing ignored = timings.startTiming()) { // Paper // Purpur - if (this.rTask != null) { - this.rTask.run(); - } else { - this.cTask.accept(this); - } -- } // Paper -+ //} // Paper // Purpur - } - - long getCreatedAt() { -diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java -index 500f2eb0df5a07637cd278c263e95592b0037eb6..895431584c476067790f1f131699d49eac3f4b8f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java -+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java -@@ -115,7 +115,7 @@ public final class CraftScoreboardManager implements ScoreboardManager { - public void getScoreboardScores(ObjectiveCriteria criteria, String name, Consumer consumer) { - // Paper start - add timings for scoreboard search - // plugins leaking scoreboards will make this very expensive, let server owners debug it easily -- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); -+ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); // Purpur - try { - // Paper end - add timings for scoreboard search - for (CraftScoreboard scoreboard : this.scoreboards) { -@@ -123,7 +123,7 @@ public final class CraftScoreboardManager implements ScoreboardManager { - board.forAllObjectives(criteria, name, (score) -> consumer.accept(score)); - } - } finally { // Paper start - add timings for scoreboard search -- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); -+ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); // Purpur - } - // Paper end - add timings for scoreboard search - } -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 79d027e517ce08443d86b877a55df24cc20d102b..43d9e7287cf0e498ccbff9b865bb813e7fb567c0 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -170,7 +170,7 @@ public class ActivationRange - */ - public static void activateEntities(Level world) - { -- MinecraftTimings.entityActivationCheckTimer.startTiming(); -+ //MinecraftTimings.entityActivationCheckTimer.startTiming(); // Purpur - final int miscActivationRange = world.spigotConfig.miscActivationRange; - final int raiderActivationRange = world.spigotConfig.raiderActivationRange; - final int animalActivationRange = world.spigotConfig.animalActivationRange; -@@ -245,7 +245,7 @@ public class ActivationRange - } - // Paper end - } -- MinecraftTimings.entityActivationCheckTimer.stopTiming(); -+ //MinecraftTimings.entityActivationCheckTimer.stopTiming(); // Purpur - } - - /** diff --git a/patches/server/0269-Remove-Mojang-Profiler.patch b/patches/server/0269-Remove-Mojang-Profiler.patch deleted file mode 100644 index 013e2c596..000000000 --- a/patches/server/0269-Remove-Mojang-Profiler.patch +++ /dev/null @@ -1,1909 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 16 Jul 2022 21:37:10 -0500 -Subject: [PATCH] Remove Mojang Profiler - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 5f6cc8b16af6dce3b74f0c2c662b0ecf84ae8d36..52b06c34d9d3ffb8844556e7b0eaed5a7f03da0c 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -150,7 +150,7 @@ public class Commands { - DamageCommand.register(this.dispatcher, commandRegistryAccess); - DataCommands.register(this.dispatcher); - DataPackCommand.register(this.dispatcher); -- DebugCommand.register(this.dispatcher); -+ //DebugCommand.register(this.dispatcher); // Purpur - DefaultGameModeCommands.register(this.dispatcher); - DifficultyCommand.register(this.dispatcher); - EffectCommands.register(this.dispatcher, commandRegistryAccess); -@@ -317,9 +317,9 @@ public class Commands { - public int performCommand(ParseResults parseresults, String s, String label) { // CraftBukkit - CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); - -- commandlistenerwrapper.getServer().getProfiler().push(() -> { -+ /*commandlistenerwrapper.getServer().getProfiler().push(() -> { // Purpur - return "/" + s; -- }); -+ });*/ // Purpur - - byte b0; - -@@ -402,7 +402,7 @@ public class Commands { - b0 = 0; - } - } finally { -- commandlistenerwrapper.getServer().getProfiler().pop(); -+ //commandlistenerwrapper.getServer().getProfiler().pop(); // Purpur - } - - return b0; -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 9711ba8466e939a047c6656231a5a0250a5435b7..798586a3b01814548d702d16faddd406cdd5acec 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -341,13 +341,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -+ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur -+ //this.profiler = this.metricsRecorder.getProfiler(); // Purpur -+ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur - this.stopRecordingMetrics(); -- }; -- this.onMetricsRecordingFinished = (path) -> { -- }; -+ };*/ // Purpur -+ //this.onMetricsRecordingFinished = (path) -> { // Purpur -+ //}; // Purpur - this.random = RandomSource.create(); - this.port = -1; - this.levels = Maps.newLinkedHashMap(); -@@ -936,9 +936,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % autosavePeriod == 0; - try { - this.isSaving = true; -@@ -1444,7 +1444,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - -- this.profiler.push(() -> { -+ /*this.profiler.push(() -> { // Purpur - return worldserver + " " + worldserver.dimension().location(); -- }); -+ });*/ // Purpur - /* Drop global time updates - if (this.tickCount % 20 == 0) { -- this.profiler.push("timeSync"); -+ //this.profiler.push("timeSync"); // Purpur - this.synchronizeTime(worldserver); -- this.profiler.pop(); -+ //this.profiler.pop(); // Purpur - } - // CraftBukkit end */ - -- this.profiler.push("tick"); -+ //this.profiler.push("tick"); // Purpur - - try { - //worldserver.timings.doTick.startTiming(); // Spigot // Purpur -@@ -1604,17 +1604,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { - this.executeBlocking(() -> { - this.saveDebugReport(path.resolve("server")); -@@ -2550,40 +2550,40 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resultConsumer, Consumer dumpConsumer) { -- this.onMetricsRecordingStopped = (methodprofilerresults) -> { -+ /*this.onMetricsRecordingStopped = (methodprofilerresults) -> { // Purpur - this.stopRecordingMetrics(); - resultConsumer.accept(methodprofilerresults); - }; - this.onMetricsRecordingFinished = dumpConsumer; -- this.willStartRecordingMetrics = true; -+ this.willStartRecordingMetrics = true;*/ // Purpur - } - - public void stopRecordingMetrics() { -- this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; -+ //this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; // Purpur - } - - public void finishRecordingMetrics() { -- this.metricsRecorder.end(); -+ //this.metricsRecorder.end(); // Purpur - } - - public void cancelRecordingMetrics() { -- this.metricsRecorder.cancel(); -- this.profiler = this.metricsRecorder.getProfiler(); -+ //this.metricsRecorder.cancel(); // Purpur -+ //this.profiler = this.metricsRecorder.getProfiler(); // Purpur - } - - public Path getWorldPath(LevelResource worldSavePath) { -@@ -2632,15 +2632,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop functions, ResourceLocation label) { -- ProfilerFiller gameprofilerfiller = this.server.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.server.getProfiler(); // Purpur - - Objects.requireNonNull(label); -- gameprofilerfiller.push(label::toString); -+ //gameprofilerfiller.push(label::toString); // Purpur - Iterator iterator = functions.iterator(); - - while (iterator.hasNext()) { -@@ -70,7 +70,7 @@ public class ServerFunctionManager { - this.execute(customfunction, this.getGameLoopSender()); - } - -- this.server.getProfiler().pop(); -+ //this.server.getProfiler().pop(); // Purpur - } - - public int execute(CommandFunction function, CommandSourceStack source) { -@@ -187,10 +187,10 @@ public class ServerFunctionManager { - - try { - ServerFunctionManager.QueuedCommand customfunctiondata_queuedcommand = (ServerFunctionManager.QueuedCommand) this.commandQueue.removeFirst(); -- ProfilerFiller gameprofilerfiller = ServerFunctionManager.this.server.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = ServerFunctionManager.this.server.getProfiler(); // Purpur - - Objects.requireNonNull(customfunctiondata_queuedcommand); -- gameprofilerfiller.push(customfunctiondata_queuedcommand::toString); -+ //gameprofilerfiller.push(customfunctiondata_queuedcommand::toString); // Purpur - this.depth = customfunctiondata_queuedcommand.depth; - customfunctiondata_queuedcommand.execute(ServerFunctionManager.this, this.commandQueue, i, this.tracer); - if (this.abortCurrentDepth) { -@@ -209,7 +209,7 @@ public class ServerFunctionManager { - - this.nestedCalls.clear(); - } finally { -- ServerFunctionManager.this.server.getProfiler().pop(); -+ //ServerFunctionManager.this.server.getProfiler().pop(); // Purpur - } - - ++j; -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index f4e1fc878ecf72dafade1c72869c9656a0c5b8aa..319c469c20f3cb4f6d61425bfa6d98b55b55c4d6 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -637,20 +637,20 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - protected void tick(BooleanSupplier shouldKeepTicking) { -- ProfilerFiller gameprofilerfiller = this.level.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur - - //try (Timing ignored = this.level.timings.poiUnload.startTiming()) { // Paper // Purpur -- gameprofilerfiller.push("poi"); -+ //gameprofilerfiller.push("poi"); // Purpur - this.poiManager.tick(shouldKeepTicking); - //} // Paper // Purpur -- gameprofilerfiller.popPush("chunk_unload"); -+ //gameprofilerfiller.popPush("chunk_unload"); // Purpur - if (!this.level.noSave()) { - //try (Timing ignored = this.level.timings.chunkUnload.startTiming()) { // Paper // Purpur - this.processUnloads(shouldKeepTicking); - //} // Paper // Purpur - } - -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - - public boolean hasWork() { -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 14613af193b178133fd688f26a766b36fa969590..2d5b0f293bb30243e774bfb62b8100c01e7eb383 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -280,16 +280,16 @@ public class ServerChunkCache extends ChunkSource { - return ifLoaded; - } - // Paper end -- ProfilerFiller gameprofilerfiller = this.level.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur - -- gameprofilerfiller.incrementCounter("getChunk"); -+ //gameprofilerfiller.incrementCounter("getChunk"); // Purpur - long k = ChunkPos.asLong(x, z); - - ChunkAccess ichunkaccess; - - // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system - -- gameprofilerfiller.incrementCounter("getChunkCacheMiss"); -+ //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur - CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper - ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; - -@@ -478,24 +478,24 @@ public class ServerChunkCache extends ChunkSource { - // CraftBukkit start - modelled on below - public void purgeUnload() { - if (true) return; // Paper - tickets will be removed later, this behavior isn't really well accounted for by the chunk system -- this.level.getProfiler().push("purge"); -+ //this.level.getProfiler().push("purge"); // Purpur - this.distanceManager.purgeStaleTickets(); - this.runDistanceManagerUpdates(); -- this.level.getProfiler().popPush("unload"); -+ //this.level.getProfiler().popPush("unload"); // Purpur - this.chunkMap.tick(() -> true); -- this.level.getProfiler().pop(); -+ //this.level.getProfiler().pop(); // Purpur - this.clearCache(); - } - // CraftBukkit end - - @Override - public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) { -- this.level.getProfiler().push("purge"); -+ //this.level.getProfiler().push("purge"); // Purpur - //this.level.timings.doChunkMap.startTiming(); // Spigot // Purpur - this.distanceManager.purgeStaleTickets(); - this.runDistanceManagerUpdates(); - //this.level.timings.doChunkMap.stopTiming(); // Spigot // Purpur -- this.level.getProfiler().popPush("chunks"); -+ //this.level.getProfiler().popPush("chunks"); // Purpur - if (tickChunks) { - //this.level.timings.chunks.startTiming(); // Paper - timings // Purpur - this.chunkMap.level.playerChunkLoader.tick(); // Paper - replace player chunk loader - this is mostly required to account for view distance changes -@@ -504,10 +504,10 @@ public class ServerChunkCache extends ChunkSource { - } - - //this.level.timings.doChunkUnload.startTiming(); // Spigot // Purpur -- this.level.getProfiler().popPush("unload"); -+ //this.level.getProfiler().popPush("unload"); // Purpur - this.chunkMap.tick(shouldKeepTicking); - //this.level.timings.doChunkUnload.stopTiming(); // Spigot // Purpur -- this.level.getProfiler().pop(); -+ //this.level.getProfiler().pop(); // Purpur - this.clearCache(); - } - -@@ -553,14 +553,14 @@ public class ServerChunkCache extends ChunkSource { - } - // Paper end - optimize isOutisdeRange - LevelData worlddata = this.level.getLevelData(); -- ProfilerFiller gameprofilerfiller = this.level.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur - -- gameprofilerfiller.push("pollingChunks"); -+ //gameprofilerfiller.push("pollingChunks"); // Purpur - this.level.resetIceAndSnowTick(); // Pufferfish - reset ice & snow tick random - int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); - boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit - -- gameprofilerfiller.push("naturalSpawnCount"); -+ //gameprofilerfiller.push("naturalSpawnCount"); // Purpur - //this.level.timings.countNaturalMobs.startTiming(); // Paper - timings // Purpur - int l = this.distanceManager.getNaturalSpawnChunkCount(); - // Paper start - per player mob spawning -@@ -595,13 +595,13 @@ public class ServerChunkCache extends ChunkSource { - //this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings // Purpur - - //this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously -- gameprofilerfiller.popPush("filteringLoadedChunks"); -+ //gameprofilerfiller.popPush("filteringLoadedChunks"); // Purpur - // Paper - moved down - //this.level.timings.chunkTicks.startTiming(); // Paper // Purpur - - // Paper - moved down - -- gameprofilerfiller.popPush("spawnAndTick"); -+ //gameprofilerfiller.popPush("spawnAndTick"); // Purpur - boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit - - // Paper - only shuffle if per-player mob spawning is disabled -@@ -653,15 +653,15 @@ public class ServerChunkCache extends ChunkSource { - } - // Paper end - optimise chunk tick iteration - //this.level.timings.chunkTicks.stopTiming(); // Paper // Purpur -- gameprofilerfiller.popPush("customSpawners"); -+ //gameprofilerfiller.popPush("customSpawners"); // Purpur - if (flag2) { - //try (co.aikar.timings.Timing ignored = this.level.timings.miscMobSpawning.startTiming()) { // Paper - timings // Purpur - this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); - //} // Paper - timings // Purpur - } -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded -- gameprofilerfiller.popPush("broadcast"); -+ //gameprofilerfiller.popPush("broadcast"); // Purpur - //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur - if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { - ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); -@@ -675,7 +675,7 @@ public class ServerChunkCache extends ChunkSource { - } - } - //this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing // Purpur -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - // Paper end - use set of chunks requiring updates, rather than iterating every single one loaded - // Paper start - controlled flush for entity tracker packets - List disabledFlushes = new java.util.ArrayList<>(this.level.players.size()); -@@ -900,7 +900,7 @@ public class ServerChunkCache extends ChunkSource { - - @Override - protected void doRunTask(Runnable task) { -- ServerChunkCache.this.level.getProfiler().incrementCounter("runTask"); -+ //ServerChunkCache.this.level.getProfiler().incrementCounter("runTask"); // Purpur - super.doRunTask(task); - } - -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 5b7507db055d7be59b369f66e659e128c9c7e00b..221d1d0e1b4b46de6ebca5faac09bbda875fae17 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -795,12 +795,12 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - } - // Paper end - optimise checkDespawn -- ProfilerFiller gameprofilerfiller = this.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur - - this.handlingTick = true; -- gameprofilerfiller.push("world border"); -+ //gameprofilerfiller.push("world border"); // Purpur - this.getWorldBorder().tick(); -- gameprofilerfiller.popPush("weather"); -+ //gameprofilerfiller.popPush("weather"); // Purpur - this.advanceWeatherCycle(); - int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); - long j; -@@ -827,32 +827,32 @@ public class ServerLevel extends Level implements WorldGenLevel { - - this.updateSkyBrightness(); - this.tickTime(); -- gameprofilerfiller.popPush("tickPending"); -+ //gameprofilerfiller.popPush("tickPending"); // Purpur - //timings.scheduledBlocks.startTiming(); // Paper // Purpur - if (!this.isDebug()) { - j = this.getGameTime(); -- gameprofilerfiller.push("blockTicks"); -+ //gameprofilerfiller.push("blockTicks"); // Purpur - this.blockTicks.tick(j, 65536, this::tickBlock); -- gameprofilerfiller.popPush("fluidTicks"); -+ //gameprofilerfiller.popPush("fluidTicks"); // Purpur - this.fluidTicks.tick(j, 65536, this::tickFluid); -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - //timings.scheduledBlocks.stopTiming(); // Paper // Purpur - -- gameprofilerfiller.popPush("raid"); -+ //gameprofilerfiller.popPush("raid"); // Purpur - //this.timings.raids.startTiming(); // Paper - timings // Purpur - this.raids.tick(); - //this.timings.raids.stopTiming(); // Paper - timings // Purpur -- gameprofilerfiller.popPush("chunkSource"); -+ //gameprofilerfiller.popPush("chunkSource"); // Purpur - //this.timings.chunkProviderTick.startTiming(); // Paper - timings // Purpur - this.getChunkSource().tick(shouldKeepTicking, true); - //this.timings.chunkProviderTick.stopTiming(); // Paper - timings // Purpur -- gameprofilerfiller.popPush("blockEvents"); -+ //gameprofilerfiller.popPush("blockEvents"); // Purpur - //timings.doSounds.startTiming(); // Spigot // Purpur - this.runBlockEvents(); - //timings.doSounds.stopTiming(); // Spigot // Purpur - this.handlingTick = false; -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players - - if (flag) { -@@ -860,12 +860,12 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - if (flag || this.emptyTime++ < 300) { -- gameprofilerfiller.push("entities"); -+ //gameprofilerfiller.push("entities"); // Purpur - //timings.tickEntities.startTiming(); // Spigot // Purpur - if (this.dragonFight != null) { -- gameprofilerfiller.push("dragonFight"); -+ //gameprofilerfiller.push("dragonFight"); // Purpur - this.dragonFight.tick(); -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - - org.spigotmc.ActivationRange.activateEntities(this); // Spigot -@@ -876,9 +876,9 @@ public class ServerLevel extends Level implements WorldGenLevel { - if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed - entity.discard(); - } else { -- gameprofilerfiller.push("checkDespawn"); -+ //gameprofilerfiller.push("checkDespawn"); // Purpur - entity.checkDespawn(); -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list - Entity entity1 = entity.getVehicle(); - -@@ -890,7 +890,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - entity.stopRiding(); - } - -- gameprofilerfiller.push("tick"); -+ //gameprofilerfiller.push("tick"); // Purpur - // Pufferfish start - copied from this.guardEntityTick - try { - this.tickNonPassenger(entity); // Pufferfish - changed -@@ -905,20 +905,19 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Paper end - } - // Pufferfish end -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - } - } - }); - //timings.entityTick.stopTiming(); // Spigot // Purpur - //timings.tickEntities.stopTiming(); // Spigot // Purpur -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - this.tickBlockEntities(); - } - -- gameprofilerfiller.push("entityManagement"); -+ //gameprofilerfiller.push("entityManagement"); // Purpur - //this.entityManager.tick(); // Paper - rewrite chunk system -- gameprofilerfiller.pop(); - } - - @Override -@@ -1000,9 +999,9 @@ public class ServerLevel extends Level implements WorldGenLevel { - boolean flag = this.isRaining(); - int j = chunkcoordintpair.getMinBlockX(); - int k = chunkcoordintpair.getMinBlockZ(); -- ProfilerFiller gameprofilerfiller = this.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur - -- gameprofilerfiller.push("thunder"); -+ //gameprofilerfiller.push("thunder"); // Purpur - final BlockPos.MutableBlockPos blockposition = this.chunkTickMutablePosition; // Paper - use mutable to reduce allocation rate, final to force compile fail on change - - if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - disable thunder // Pufferfish - replace random with shouldDoLightning -@@ -1040,7 +1039,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - } - -- gameprofilerfiller.popPush("iceandsnow"); -+ //gameprofilerfiller.popPush("iceandsnow"); // Purpur - int l; - int i1; - -@@ -1092,7 +1091,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - - // Paper start - optimise random block ticking -- gameprofilerfiller.popPush("randomTick"); -+ //gameprofilerfiller.popPush("randomTick"); // Purpur - //timings.chunkTicksBlocks.startTiming(); // Paper // Purpur - if (randomTickSpeed > 0) { - LevelChunkSection[] sections = chunk.getSections(); -@@ -1128,7 +1127,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - } - // Paper end - optimise random block ticking - //timings.chunkTicksBlocks.stopTiming(); // Paper // Purpur -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - - public Optional findLightningRod(BlockPos pos) { -@@ -1424,19 +1423,19 @@ public class ServerLevel extends Level implements WorldGenLevel { - //try { // Purpur - // Paper end - timings - entity.setOldPosAndRot(); -- ProfilerFiller gameprofilerfiller = this.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur - - ++entity.tickCount; -- this.getProfiler().push(() -> { -+ /*this.getProfiler().push(() -> { // Purpur - return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString(); -- }); -- gameprofilerfiller.incrementCounter("tickNonPassenger"); -+ });*/ // Purpur -+ //gameprofilerfiller.incrementCounter("tickNonPassenger"); // Purpur - if (isActive) { // Paper - EAR 2 - TimingHistory.activatedEntityTicks++; - entity.tick(); - entity.postTick(); // CraftBukkit - } else { entity.inactiveTick(); } // Paper - EAR 2 -- this.getProfiler().pop(); -+ //this.getProfiler().pop(); // Purpur - //} finally { timer.stopTiming(); } // Paper - timings // Purpur - Iterator iterator = entity.getPassengers().iterator(); - -@@ -1465,12 +1464,12 @@ public class ServerLevel extends Level implements WorldGenLevel { - // Paper end - passenger.setOldPosAndRot(); - ++passenger.tickCount; -- ProfilerFiller gameprofilerfiller = this.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur - -- gameprofilerfiller.push(() -> { -+ /*gameprofilerfiller.push(() -> { // Purpur - return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString(); -- }); -- gameprofilerfiller.incrementCounter("tickPassenger"); -+ });*/ // Purpur -+ //gameprofilerfiller.incrementCounter("tickPassenger"); // Purpur - // Paper start - EAR 2 - if (isActive) { - passenger.rideTick(); -@@ -1482,7 +1481,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - vehicle.positionRider(passenger); - } - // Paper end - EAR 2 -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - Iterator iterator = passenger.getPassengers().iterator(); - - while (iterator.hasNext()) { -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 01f001c39581c4af5765548e221e254465fe0195..2f013c3365d065325d645d43f7c5137f942b06df 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1241,7 +1241,7 @@ public class ServerPlayer extends Player { - PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver); - - if (shapedetectorshape != null) { -- worldserver1.getProfiler().push("moving"); -+ //worldserver1.getProfiler().push("moving"); // Purpur - worldserver = shapedetectorshape.world; // CraftBukkit - if (worldserver == null) { } else // CraftBukkit - empty to fall through to null to event - if (resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit -@@ -1264,8 +1264,8 @@ public class ServerPlayer extends Player { - worldserver = ((CraftWorld) exit.getWorld()).getHandle(); - // CraftBukkit end - -- worldserver1.getProfiler().pop(); -- worldserver1.getProfiler().push("placing"); -+ //worldserver1.getProfiler().pop(); // Purpur -+ //worldserver1.getProfiler().push("placing"); // Purpur - if (true) { // CraftBukkit - this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds - -@@ -1283,7 +1283,7 @@ public class ServerPlayer extends Player { - this.connection.teleport(exit); // CraftBukkit - use internal teleport without event - this.connection.resetPosition(); - worldserver.addDuringPortalTeleport(this); -- worldserver1.getProfiler().pop(); -+ //worldserver1.getProfiler().pop(); // Purpur - this.triggerDimensionChangeTriggers(worldserver1); - this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities())); - playerlist.sendLevelInfo(this, worldserver); -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 5feaf5bb7e366f593d5700fb78243a824ce97f9c..e22f403c54922f05d6d5704ec648dce02097bbb0 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -412,7 +412,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - this.aboveGroundVehicleTickCount = 0; - } - -- this.server.getProfiler().push("keepAlive"); -+ //this.server.getProfiler().push("keepAlive"); // Purpur - // Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings - // This should effectively place the keepalive handling back to "as it was" before 1.12.2 - long currentTime = Util.getMillis(); -@@ -448,7 +448,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - } - // Paper end - -- this.server.getProfiler().pop(); -+ //this.server.getProfiler().pop(); // Purpur - // CraftBukkit start - for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !this.chatSpamTickCount.compareAndSet(spam, spam - 1); ) ; - if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable -diff --git a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java -index 9ddbfcf80d9a381dace78a62880f85a4d767e0eb..7383c7d3820dce06108eaafd236a7c6c06a10a42 100644 ---- a/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java -+++ b/src/main/java/net/minecraft/server/packs/resources/ResourceManagerReloadListener.java -@@ -9,11 +9,11 @@ public interface ResourceManagerReloadListener extends PreparableReloadListener - @Override - default CompletableFuture reload(PreparableReloadListener.PreparationBarrier synchronizer, ResourceManager manager, ProfilerFiller prepareProfiler, ProfilerFiller applyProfiler, Executor prepareExecutor, Executor applyExecutor) { - return synchronizer.wait(Unit.INSTANCE).thenRunAsync(() -> { -- applyProfiler.startTick(); -- applyProfiler.push("listener"); -+ //applyProfiler.startTick(); // Purpur -+ //applyProfiler.push("listener"); // Purpur - this.onResourceManagerReload(manager); -- applyProfiler.pop(); -- applyProfiler.endTick(); -+ //applyProfiler.pop(); // Purpur -+ //applyProfiler.endTick(); // Purpur - }, applyExecutor); - } - -diff --git a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java -index 196c7331138fee2822c76aacd136f9da040e0049..c6c30d99399c5cde2b0ec2f320d81d952b422d78 100644 ---- a/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java -+++ b/src/main/java/net/minecraft/util/profiling/ActiveProfiler.java -@@ -55,7 +55,7 @@ public class ActiveProfiler implements ProfileCollector { - this.started = true; - this.path = ""; - this.paths.clear(); -- this.push("root"); -+ //this.push("root"); // Purpur - } - } - -@@ -64,7 +64,7 @@ public class ActiveProfiler implements ProfileCollector { - if (!this.started) { - LOGGER.error("Profiler tick already ended - missing startTick()?"); - } else { -- this.pop(); -+ //this.pop(); // Purpur - this.started = false; - if (!this.path.isEmpty()) { - LOGGER.error("Profiler tick ended before path was fully popped (remainder: '{}'). Mismatched push/pop?", LogUtils.defer(() -> { -@@ -93,7 +93,7 @@ public class ActiveProfiler implements ProfileCollector { - - @Override - public void push(Supplier locationGetter) { -- this.push(locationGetter.get()); -+ //this.push(locationGetter.get()); // Purpur - } - - @Override -@@ -132,14 +132,14 @@ public class ActiveProfiler implements ProfileCollector { - - @Override - public void popPush(String location) { -- this.pop(); -- this.push(location); -+ //this.pop(); // Purpur -+ //this.push(location); // Purpur - } - - @Override - public void popPush(Supplier locationGetter) { -- this.pop(); -- this.push(locationGetter); -+ //this.pop(); // Purpur -+ //this.push(locationGetter); // Purpur - } - - private ActiveProfiler.PathEntry getCurrentEntry() { -diff --git a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java -index 2e6e8eac987c4ef6b2dcd3de592d8a51d2b29792..863343a87fe34d72f04af89d75268b477b2adc7a 100644 ---- a/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java -+++ b/src/main/java/net/minecraft/util/profiling/ProfilerFiller.java -@@ -6,32 +6,44 @@ import net.minecraft.util.profiling.metrics.MetricCategory; - public interface ProfilerFiller { - String ROOT = "root"; - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void startTick(); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void endTick(); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void push(String location); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void push(Supplier locationGetter); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void pop(); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void popPush(String location); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void popPush(Supplier locationGetter); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void markForCharting(MetricCategory type); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - default void incrementCounter(String marker) { -- this.incrementCounter(marker, 1); -+ //this.incrementCounter(marker, 1); // Purpur - } - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void incrementCounter(String marker, int num); - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - default void incrementCounter(Supplier markerGetter) { -- this.incrementCounter(markerGetter, 1); -+ //this.incrementCounter(markerGetter, 1); // Purpur - } - -+ @io.papermc.paper.annotation.DoNotUse // Purpur - void incrementCounter(Supplier markerGetter, int num); - - static ProfilerFiller tee(final ProfilerFiller a, final ProfilerFiller b) { -@@ -41,62 +53,62 @@ public interface ProfilerFiller { - return b == InactiveProfiler.INSTANCE ? a : new ProfilerFiller() { - @Override - public void startTick() { -- a.startTick(); -- b.startTick(); -+ //a.startTick(); // Purpur -+ //b.startTick(); // Purpur - } - - @Override - public void endTick() { -- a.endTick(); -- b.endTick(); -+ //a.endTick(); // Purpur -+ //b.endTick(); // Purpur - } - - @Override - public void push(String location) { -- a.push(location); -- b.push(location); -+ //a.push(location); // Purpur -+ //b.push(location); // Purpur - } - - @Override - public void push(Supplier locationGetter) { -- a.push(locationGetter); -- b.push(locationGetter); -+ //a.push(locationGetter); // Purpur -+ //b.push(locationGetter); // Purpur - } - - @Override - public void markForCharting(MetricCategory type) { -- a.markForCharting(type); -- b.markForCharting(type); -+ //a.markForCharting(type); // Purpur -+ //b.markForCharting(type); // Purpur - } - - @Override - public void pop() { -- a.pop(); -- b.pop(); -+ //a.pop(); // Purpur -+ //b.pop(); // Purpur - } - - @Override - public void popPush(String location) { -- a.popPush(location); -- b.popPush(location); -+ //a.popPush(location); // Purpur -+ //b.popPush(location); // Purpur - } - - @Override - public void popPush(Supplier locationGetter) { -- a.popPush(locationGetter); -- b.popPush(locationGetter); -+ //a.popPush(locationGetter); // Purpur -+ //b.popPush(locationGetter); // Purpur - } - - @Override - public void incrementCounter(String marker, int num) { -- a.incrementCounter(marker, num); -- b.incrementCounter(marker, num); -+ //a.incrementCounter(marker, num); // Purpur -+ //b.incrementCounter(marker, num); // Purpur - } - - @Override - public void incrementCounter(Supplier markerGetter, int num) { -- a.incrementCounter(markerGetter, num); -- b.incrementCounter(markerGetter, num); -+ //a.incrementCounter(markerGetter, num); // Purpur -+ //b.incrementCounter(markerGetter, num); // Purpur - } - }; - } -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 156c2683d72c5e83b1cefb9ebf356c6e4d31e7f4..e634f9b7998979e00e4de4899214863e32f1a776 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -439,7 +439,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // golf score - public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // used where needed - // Pufferfish end -- -+ - public float getBukkitYaw() { - return this.yRot; - } -@@ -864,7 +864,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // CraftBukkit end - - public void baseTick() { -- this.level().getProfiler().push("entityBaseTick"); -+ //this.level().getProfiler().push("entityBaseTick"); // Purpur - if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Update last hurt when ticking - this.feetBlockState = null; - if (this.isPassenger() && this.getVehicle().isRemoved()) { -@@ -925,7 +925,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - this.firstTick = false; -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - - public void setSharedFlagOnFire(boolean onFire) { -@@ -1144,7 +1144,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - } - -- this.level().getProfiler().push("move"); -+ //this.level().getProfiler().push("move"); // Purpur - if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) { - movement = movement.multiply(this.stuckSpeedMultiplier); - this.stuckSpeedMultiplier = Vec3.ZERO; -@@ -1153,7 +1153,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - // Paper start - ignore movement changes while inactive. - if (isTemporarilyActive && !(this instanceof ItemEntity || this instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) && movement == getDeltaMovement() && movementType == MoverType.SELF) { - setDeltaMovement(Vec3.ZERO); -- this.level.getProfiler().pop(); -+ //this.level.getProfiler().pop(); // Purpur - return; - } - // Paper end -@@ -1174,8 +1174,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z); - } - -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("rest"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("rest"); // Purpur - boolean flag = !Mth.equal(movement.x, vec3d1.x); - boolean flag1 = !Mth.equal(movement.z, vec3d1.z); - -@@ -1194,7 +1194,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - - this.checkFallDamage(vec3d1.y, this.onGround(), iblockdata, blockposition); - if (this.isRemoved()) { -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } else { - if (this.horizontalCollision) { - Vec3 vec3d2 = this.getDeltaMovement(); -@@ -1332,7 +1332,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.setRemainingFireTicks(-this.getFireImmuneTicks()); - } - -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - } - // Paper start - detailed watchdog information -@@ -3101,7 +3101,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - ServerLevel worldserver1 = minecraftserver.getLevel(resourcekey); - - if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit -- this.level().getProfiler().push("portal"); -+ //this.level().getProfiler().push("portal"); // Purpur - this.portalTime = i; - // Paper start - io.papermc.paper.event.entity.EntityPortalReadyEvent event = new io.papermc.paper.event.entity.EntityPortalReadyEvent(this.getBukkitEntity(), worldserver1 == null ? null : worldserver1.getWorld(), org.bukkit.PortalType.NETHER); -@@ -3119,7 +3119,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - } // Paper - // CraftBukkit end -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - - this.isInsidePortal = false; -@@ -3590,14 +3590,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - // Paper end - if (this.level() instanceof ServerLevel && !this.isRemoved()) { -- this.level().getProfiler().push("changeDimension"); -+ //this.level().getProfiler().push("changeDimension"); // Purpur - // CraftBukkit start - // this.unRide(); - if (worldserver == null) { - return null; - } - // CraftBukkit end -- this.level().getProfiler().push("reposition"); -+ //this.level().getProfiler().push("reposition"); // Purpur - PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit - - if (shapedetectorshape == null) { -@@ -3636,7 +3636,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - this.unRide(); - // CraftBukkit end - -- this.level().getProfiler().popPush("reloading"); -+ //this.level().getProfiler().popPush("reloading"); // Purpur - // Paper start - Change lead drop timing to prevent dupe - if (this instanceof Mob) { - ((Mob) this).dropLeash(true, true); // Paper drop lead -@@ -3659,10 +3659,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - this.removeAfterChangingDimensions(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - ((ServerLevel) this.level()).resetEmptyTime(); - worldserver.resetEmptyTime(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - return entity; - } - } else { -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index ec3a9946b7387754c8f5a15ce6268a7fa666db02..9012b5b918bca409166db9eb29c201a793886dbb 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -410,7 +410,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - super.baseTick(); -- this.level().getProfiler().push("livingEntityBaseTick"); -+ //this.level().getProfiler().push("livingEntityBaseTick"); // Purpur - if (this.fireImmune() || this.level().isClientSide) { - this.clearFire(); - } -@@ -513,7 +513,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.yHeadRotO = this.yHeadRot; - this.yRotO = this.getYRot(); - this.xRotO = this.getXRot(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - - public boolean canSpawnSoulSpeedParticle() { -@@ -3123,10 +3123,10 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - this.run += (f3 - this.run) * 0.3F; -- this.level().getProfiler().push("headTurn"); -+ //this.level().getProfiler().push("headTurn"); // Purpur - f2 = this.tickHeadTurn(f1, f2); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("rangeChecks"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("rangeChecks"); // Purpur - - // Paper start - stop large pitch and yaw changes from crashing the server - this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F; -@@ -3138,7 +3138,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; - // Paper end - -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - this.animStep += f2; - if (this.isFallFlying()) { - ++this.fallFlyTicks; -@@ -3427,19 +3427,19 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - this.setDeltaMovement(d4, d5, d6); -- this.level().getProfiler().push("ai"); -+ //this.level().getProfiler().push("ai"); // Purpur - if (this.isImmobile()) { - this.jumping = false; - this.xxa = 0.0F; - this.zza = 0.0F; - } else if (this.isEffectiveAi()) { -- this.level().getProfiler().push("newAi"); -+ //this.level().getProfiler().push("newAi"); // Purpur - this.serverAiStep(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("jump"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("jump"); // Purpur - if (this.jumping && this.isAffectedByFluids()) { - double d7; - -@@ -3466,8 +3466,8 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.noJumpDelay = 0; - } - -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("travel"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("travel"); // Purpur - this.xxa *= 0.98F; - this.zza *= 0.98F; - this.updateFallFlying(); -@@ -3494,8 +3494,8 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.travel(vec3d1); - } - -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("freezing"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("freezing"); // Purpur - if (!this.level().isClientSide && !this.isDeadOrDying() && !freezeLocked) { // Paper - Freeze Tick Lock API - int i = this.getTicksFrozen(); - -@@ -3512,15 +3512,15 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.hurt(this.damageSources().freeze(), 1.0F); - } - -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("push"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("push"); // Purpur - if (this.autoSpinAttackTicks > 0) { - --this.autoSpinAttackTicks; - this.checkAutoSpinAttack(axisalignedbb, this.getBoundingBox()); - } - - this.pushEntities(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - // Paper start - // Purpur start - if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 7dae759b777cf9077bf46d2e1189c20c1c2db60e..19d9d3a9f634631e2ec5a6fa7f2cd9dd18f62f89 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -365,13 +365,13 @@ public abstract class Mob extends LivingEntity implements Targeting { - @Override - public void baseTick() { - super.baseTick(); -- this.level().getProfiler().push("mobBaseTick"); -+ //this.level().getProfiler().push("mobBaseTick"); // Purpur - if (this.isAlive() && this.random.nextInt(1000) < this.ambientSoundTime++) { - this.resetAmbientSoundTime(); - this.playAmbientSound(); - } - -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - incrementTicksSinceLastInteraction(); // Purpur - } - -@@ -702,7 +702,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - @Override - public void aiStep() { - super.aiStep(); -- this.level().getProfiler().push("looting"); -+ //this.level().getProfiler().push("looting"); // Purpur - if (!this.level().isClientSide && this.canPickUpLoot() && this.isAlive() && !this.dead && (this.level().purpurConfig.entitiesPickUpLootBypassMobGriefing || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { - Vec3i baseblockposition = this.getPickupReach(); - List list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ())); -@@ -722,7 +722,7 @@ public abstract class Mob extends LivingEntity implements Targeting { - } - } - -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - - protected Vec3i getPickupReach() { -@@ -934,46 +934,46 @@ public abstract class Mob extends LivingEntity implements Targeting { - return; - } - // Paper end -- this.level().getProfiler().push("sensing"); -+ //this.level().getProfiler().push("sensing"); // Purpur - this.sensing.tick(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - int i = this.level().getServer().getTickCount() + this.getId(); - - if (i % 2 != 0 && this.tickCount > 1) { -- this.level().getProfiler().push("targetSelector"); -+ //this.level().getProfiler().push("targetSelector"); // Purpur - if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tickRunningGoals(false); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("goalSelector"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("goalSelector"); // Purpur - if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tickRunningGoals(false); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } else { -- this.level().getProfiler().push("targetSelector"); -+ //this.level().getProfiler().push("targetSelector"); - if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tick(); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("goalSelector"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("goalSelector"); // Purpur - if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tick(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - } - -- this.level().getProfiler().push("navigation"); -+ //this.level().getProfiler().push("navigation"); // Purpur - this.navigation.tick(); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("mob tick"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("mob tick"); // Purpur - this.customServerAiStep(); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("controls"); -- this.level().getProfiler().push("move"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("controls"); // Purpur -+ //this.level().getProfiler().push("move"); // Purpur - this.moveControl.tick(); -- this.level().getProfiler().popPush("look"); -+ //this.level().getProfiler().popPush("look"); // Purpur - this.lookControl.tick(); -- this.level().getProfiler().popPush("jump"); -+ //this.level().getProfiler().popPush("jump"); // Purpur - this.jumpControl.tick(); -- this.level().getProfiler().pop(); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().pop(); // Purpur - this.sendDebugPackets(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -index 1635818fc4b1788c0d397085239df6dd75b210ab..02978315bc2b828cc603ce7478408f3f82c249c2 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -105,8 +105,8 @@ public class GoalSelector { - } - - public void tick() { -- ProfilerFiller profilerFiller = this.profiler.get(); -- profilerFiller.push("goalCleanup"); -+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur -+ //profilerFiller.push("goalCleanup"); // Purpur - - for(WrappedGoal wrappedGoal : this.availableGoals) { - if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { -@@ -123,8 +123,8 @@ public class GoalSelector { - } - } - -- profilerFiller.pop(); -- profilerFiller.push("goalUpdate"); -+ //profilerFiller.pop(); // Purpur -+ //profilerFiller.push("goalUpdate"); // Purpur - - for(WrappedGoal wrappedGoal2 : this.availableGoals) { - // Paper start -@@ -144,13 +144,13 @@ public class GoalSelector { - } - } - -- profilerFiller.pop(); -+ //profilerFiller.pop(); // Purpur - this.tickRunningGoals(true); - } - - public void tickRunningGoals(boolean tickAll) { -- ProfilerFiller profilerFiller = this.profiler.get(); -- profilerFiller.push("goalTick"); -+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur -+ //profilerFiller.push("goalTick"); // Purpur - - for(WrappedGoal wrappedGoal : this.availableGoals) { - if (wrappedGoal.isRunning() && (tickAll || wrappedGoal.requiresUpdateEveryTick())) { -@@ -158,7 +158,7 @@ public class GoalSelector { - } - } - -- profilerFiller.pop(); -+ //profilerFiller.pop(); // Purpur - } - - public Set getAvailableGoals() { -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -index b376670d11088e524ce246f667e580e90cd119a3..fdb08ecc19bc4a1bc93cf6b18adcea2bc5573965 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -@@ -172,12 +172,12 @@ public abstract class PathNavigation { - } - } - // Paper end -- this.level.getProfiler().push("pathfind"); -+ //this.level.getProfiler().push("pathfind"); // Purpur - BlockPos blockPos = useHeadPos ? this.mob.blockPosition().above() : this.mob.blockPosition(); - int i = (int)(followRange + (float)range); - PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i)); - Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, positions, followRange, distance, this.maxVisitedNodesMultiplier); -- this.level.getProfiler().pop(); -+ //this.level.getProfiler().pop(); // Purpur - if (path != null && path.getTarget() != null) { - this.targetPos = path.getTarget(); - this.reachRange = distance; -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -index 51772f03a3469b11e7166ec6f3a1b9c64a606221..02f2f46ccc48bb4d9bd08555818b0489f60d9f13 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java -@@ -26,9 +26,9 @@ public class Sensing { - } else if (this.unseen.contains(i)) { - return false; - } else { -- this.mob.level().getProfiler().push("hasLineOfSight"); -+ //this.mob.level().getProfiler().push("hasLineOfSight"); // Purpur - boolean bl = this.mob.hasLineOfSight(entity); -- this.mob.level().getProfiler().pop(); -+ //this.mob.level().getProfiler().pop(); // Purpur - if (bl) { - this.seen.add(i); - } else { -diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 6c2129b93dbeaeb1e8e8db58fce8670ef8ce5716..d50bf1b980231a1045c1c9df622a9a50fc2ed893 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -260,13 +260,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("allayBrain"); -+ //this.level().getProfiler().push("allayBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("allayActivityUpdate"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("allayActivityUpdate"); // Purpur - AllayAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index 51b475191113cf3cae5e776b0dfbcd0236a92808..07abf72b7aed98652ac6639b26ade358fcd9135a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -325,13 +325,13 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder optional = this.getBrain().getMemory(MemoryModuleType.PLAY_DEAD_TICKS); - -diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -index 8073a3a0df7d75a29419303c85d7dadd9f94be99..9f19ebfa6392a080672c472e08f755379e9776b4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -@@ -161,13 +161,13 @@ public class Camel extends AbstractHorse implements PlayerRideableJumping, Rider - - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("camelBrain"); -+ //this.level().getProfiler().push("camelBrain"); // Purpur - Brain brain = (Brain) this.getBrain(); // Paper - decompile fix - brain.tick((ServerLevel)this.level(), this); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("camelActivityUpdate"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("camelActivityUpdate"); // Purpur - CamelAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index 840b61241c44e92b3053157492f4389f46179e81..dc1e8bcd8049d79c0e383ccd6a5697f79a3a2ebd 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -@@ -218,13 +218,13 @@ public class Frog extends Animal implements VariantHolder { - private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("frogBrain"); -+ //this.level().getProfiler().push("frogBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("frogActivityUpdate"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("frogActivityUpdate"); // Purpur - FrogAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index af8438ae8c805d3276ef2d82eb39b08880fcc8a1..6b012bea26e8ef0c04571f43da67f6e108188830 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -@@ -117,13 +117,13 @@ public class Tadpole extends AbstractFish { - private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("tadpoleBrain"); -+ //this.level().getProfiler().push("tadpoleBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("tadpoleActivityUpdate"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("tadpoleActivityUpdate"); // Purpur - TadpoleAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 0f8af63a88e425e5e66f68133b6c604caefc6977..31cfc2c8c5188a266c9e9993e5cf322e000df397 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -224,13 +224,14 @@ public class Goat extends Animal { - private int behaviorTick = 0; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("goatBrain"); -+ //this.level().getProfiler().push("goatBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Uncomment when pufferfish patch -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider // Purpur - TODO: Uncomment when pufferfish patch - this.getBrain().tick((ServerLevel) this.level(), this); -- this.level().getProfiler().pop(); -- this.level().getProfiler().push("goatActivityUpdate"); -+ //this.level().getProfiler().pop(); // Purpur -+ //this.level().getProfiler().push("goatActivityUpdate"); // Purpur - GoatAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -index fbc08af025e1f53696093c459656a753f8f89ebe..552777d7ef6a1190a3b84bdf2f130f735c61d275 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -+++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -@@ -520,11 +520,11 @@ public class Sniffer extends Animal { - - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("snifferBrain"); -+ //this.level().getProfiler().push("snifferBrain"); // Purpur - this.getBrain().tick((ServerLevel) this.level(), this); -- this.level().getProfiler().popPush("snifferActivityUpdate"); -+ //this.level().getProfiler().popPush("snifferActivityUpdate"); // Purpur - SnifferAi.updateActivity(this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index fc5272e8671ff4bda3bd17abe5c31cc0ff4114f8..399e9baedaf37597c539aa5432a513365df83b5e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -230,10 +230,10 @@ public class Zoglin extends Monster implements Enemy, HoglinBase { - - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("zoglinBrain"); -+ //this.level().getProfiler().push("zoglinBrain"); // Purpur - if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - this.updateActivity(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 0ddcff8d4cd22cc01462c412c3db39238237ca49..ecc445bb3cda2d79d64c580b72544567dd304f53 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -166,10 +166,10 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("hoglinBrain"); -+ //this.level().getProfiler().push("hoglinBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - HoglinAi.updateActivity(this); - if (this.isConverting()) { - ++this.timeInOverworld; -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index 9fb7f6d0532a7419674cc20e4fc6cb4b08c73514..79f969ea76fc2073e10c050c2aa64b5b5e473181 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -340,10 +340,10 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - private int behaviorTick; // Pufferfish - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("piglinBrain"); -+ //this.level().getProfiler().push("piglinBrain"); // Purpur - if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel) this.level(), this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - PiglinAi.updateActivity(this); - super.customServerAiStep(); - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -index 61e8f2d030fc50840c3f80dfb6fc810797ec440f..856e6e02c9424a6c06e310262cb4f5bdd34da516 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java -@@ -117,10 +117,10 @@ public class PiglinBrute extends AbstractPiglin { - - @Override - protected void customServerAiStep() { -- this.level().getProfiler().push("piglinBruteBrain"); -+ //this.level().getProfiler().push("piglinBruteBrain"); // Purpur - if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick((ServerLevel)this.level(), this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - PiglinBruteAi.updateActivity(this); - PiglinBruteAi.maybePlayActivitySound(this); - super.customServerAiStep(); -diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 0aa6874022d4ee8e38f2d85c45a2a4201c2a83fe..3fa46affc4d77d01909cfeaeaba6e06ba9fd5592 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -300,10 +300,10 @@ public class Warden extends Monster implements VibrationSystem { - protected void customServerAiStep() { - ServerLevel worldserver = (ServerLevel) this.level(); - -- worldserver.getProfiler().push("wardenBrain"); -+ //worldserver.getProfiler().push("wardenBrain"); // Purpur - if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish - this.getBrain().tick(worldserver, this); -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - super.customServerAiStep(); - if ((this.tickCount + this.getId()) % 120 == 0) { - Warden.applyDarknessAround(worldserver, this.position(), this, 20); -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 85c8854691070d8867a5eaf410fe7a2f75fadb9f..2ea19f72dd8e86d5821f734c13ac2dc6f53e9c30 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -341,7 +341,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - protected void customServerAiStep(boolean inactive) { // Purpur - not final - // Paper end -- this.level().getProfiler().push("villagerBrain"); -+ //this.level().getProfiler().push("villagerBrain"); // Purpur - // Purpur start - if (this.level().purpurConfig.villagerLobotomizeEnabled) { - // treat as inactive if lobotomized -@@ -354,7 +354,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - this.getBrain().tick((ServerLevel) this.level(), this); // Paper - else if (this.isLobotomized && shouldRestock()) restock(); - // Purpur end -- this.level().getProfiler().pop(); -+ //this.level().getProfiler().pop(); // Purpur - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; - } -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 8f97c9df726ac20cfce7bdddd5dd4f8c5aa76c35..e35ebb5b923c2f63e37e5dae006bc4a030e49bb7 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -382,7 +382,7 @@ public class Explosion { - if (!iblockdata.isAir() && iblockdata.isDestroyable()) { // Paper - BlockPos blockposition1 = blockposition.immutable(); - -- this.level.getProfiler().push("explosion_blocks"); -+ //this.level.getProfiler().push("explosion_blocks"); // Purpur - if (block.dropFromExplosion(this)) { - Level world = this.level; - -@@ -404,7 +404,7 @@ public class Explosion { - - this.level.setBlock(blockposition, Blocks.AIR.defaultBlockState(), 3); - block.wasExploded(this.level, blockposition, this); -- this.level.getProfiler().pop(); -+ //this.level.getProfiler().pop(); // Purpur - } - } - -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index ed7ed29147a2bb681e5d4c44d34b06d936b8fa74..bdb32964524cb2a4398b8d3bedfb03b0cb805b6d 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -1013,9 +1013,9 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - - protected void tickBlockEntities() { -- ProfilerFiller gameprofilerfiller = this.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = this.getProfiler(); // Purpur - -- gameprofilerfiller.push("blockEntities"); -+ //gameprofilerfiller.push("blockEntities"); // Purpur - //timings.tileEntityPending.startTiming(); // Spigot // Purpur - this.tickingBlockEntities = true; - if (!this.pendingBlockEntityTickers.isEmpty()) { -@@ -1060,7 +1060,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - //timings.tileEntityTick.stopTiming(); // Spigot // Purpur - this.tickingBlockEntities = false; - co.aikar.timings.TimingHistory.tileEntityTicks += this.blockEntityTickers.size(); // Paper -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - spigotConfig.currentPrimedTnt = 0; // Spigot - } - -@@ -1253,7 +1253,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - @Override - public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { -- this.getProfiler().incrementCounter("getEntities"); -+ //this.getProfiler().incrementCounter("getEntities"); // Purpur - List list = Lists.newArrayList(); - ((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call - return list; -@@ -1272,7 +1272,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - - public void getEntities(EntityTypeTest filter, AABB box, Predicate predicate, List result, int limit) { -- this.getProfiler().incrementCounter("getEntities"); -+ //this.getProfiler().incrementCounter("getEntities"); // Purpur - // Paper start - optimise this call - //TODO use limit - if (filter instanceof net.minecraft.world.entity.EntityType entityTypeTest) { -@@ -1529,7 +1529,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - } - - public ProfilerFiller getProfiler() { -- if (gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish -+ if (true || gg.pufferfish.pufferfish.PufferfishConfig.disableMethodProfiler) return net.minecraft.util.profiling.InactiveProfiler.INSTANCE; // Pufferfish // Purpur - return (ProfilerFiller) this.profiler.get(); - } - -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 482dfb2bbf8b85ba98bf164c5cf3edcdf927eb98..ad89aac8d6b1ac1095806930afd4fe196084525b 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -132,7 +132,7 @@ public final class NaturalSpawner { - } - - public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) { -- world.getProfiler().push("spawner"); -+ //world.getProfiler().push("spawner"); // Purpur - //world.timings.mobSpawn.startTiming(); // Spigot // Purpur - MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES; - int i = aenumcreaturetype.length; -@@ -189,7 +189,7 @@ public final class NaturalSpawner { - } - - //world.timings.mobSpawn.stopTiming(); // Spigot // Purpur -- world.getProfiler().pop(); -+ //world.getProfiler().pop(); // Purpur - } - - // Paper start -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 4dab159024cf91cd297dcb31833f9d57e2132326..c17b04880b4032984b43dd4fbe1c08f96112abac 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -552,11 +552,11 @@ public class LevelChunk extends ChunkAccess { - if (LightEngine.hasDifferentLightProperties(this, blockposition, iblockdata1, iblockdata)) { - ProfilerFiller gameprofilerfiller = this.level.getProfiler(); - -- gameprofilerfiller.push("updateSkyLightSources"); -+ //gameprofilerfiller.push("updateSkyLightSources"); // Purpur - this.skyLightSources.update(this, j, i, l); -- gameprofilerfiller.popPush("queueCheckLight"); -+ //gameprofilerfiller.popPush("queueCheckLight"); // Purpur - this.level.getChunkSource().getLightEngine().checkBlock(blockposition); -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } - - boolean flag3 = iblockdata1.hasBlockEntity(); -@@ -1270,9 +1270,9 @@ public class LevelChunk extends ChunkAccess { - - if (LevelChunk.this.isTicking(blockposition)) { - try { -- ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler(); -+ //ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler(); // Purpur - -- gameprofilerfiller.push(this::getType); -+ //gameprofilerfiller.push(this::getType); // Purpur - //this.blockEntity.tickTimer.startTiming(); // Spigot // Purpur - BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); - -@@ -1284,7 +1284,7 @@ public class LevelChunk extends ChunkAccess { - LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata}); - } - -- gameprofilerfiller.pop(); -+ //gameprofilerfiller.pop(); // Purpur - } catch (Throwable throwable) { - if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent tile entity and entity crashes -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -index d23481453717f715124156b5d83f6448f720d049..a8af51a25b0f99c3a64d9150fdfcd6b818aa7581 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -@@ -53,8 +53,8 @@ public class PathFinder { - @Nullable - // Paper start - optimize collection - private Path findPath(ProfilerFiller profiler, Node startNode, List> positions, float followRange, int distance, float rangeMultiplier) { -- profiler.push("find_path"); -- profiler.markForCharting(MetricCategory.PATH_FINDING); -+ //profiler.push("find_path"); // Purpur -+ //profiler.markForCharting(MetricCategory.PATH_FINDING); // Purpur - // Set set = positions.keySet(); - startNode.g = 0.0F; - startNode.h = this.getBestH(startNode, positions); // Paper - optimize collection -diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java -index 1d7c663fa0e550bd0cfb9a4b83ccd7e2968666f0..0043c0087896a6df6910b0500da37d84b287c901 100644 ---- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java -+++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java -@@ -86,20 +86,20 @@ public class LevelTicks implements LevelTickAccess { - } - - public void tick(long time, int maxTicks, BiConsumer ticker) { -- ProfilerFiller profilerFiller = this.profiler.get(); -- profilerFiller.push("collect"); -- this.collectTicks(time, maxTicks, profilerFiller); -- profilerFiller.popPush("run"); -- profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size()); -+ //ProfilerFiller profilerFiller = this.profiler.get(); // Purpur -+ //profilerFiller.push("collect"); // Purpur -+ this.collectTicks(time, maxTicks, null); // Purpur -+ //profilerFiller.popPush("run"); // Purpur -+ //profilerFiller.incrementCounter("ticksToRun", this.toRunThisTick.size()); // Purpur - this.runCollectedTicks(ticker); -- profilerFiller.popPush("cleanup"); -+ //profilerFiller.popPush("cleanup"); // Purpur - this.cleanupAfterTick(); -- profilerFiller.pop(); -+ //profilerFiller.pop(); // Purpur - } - - private void collectTicks(long time, int maxTicks, ProfilerFiller profiler) { - this.sortContainersToTick(time); -- profiler.incrementCounter("containersToTick", this.containersToTick.size()); -+ //profiler.incrementCounter("containersToTick", this.containersToTick.size()); // Purpur - this.drainContainers(time, maxTicks); - this.rescheduleLeftoverContainers(); - } diff --git a/patches/server/0270-Add-more-logger-output-for-invalid-movement-kicks.patch b/patches/server/0270-Add-more-logger-output-for-invalid-movement-kicks.patch deleted file mode 100644 index 242ab5cb7..000000000 --- a/patches/server/0270-Add-more-logger-output-for-invalid-movement-kicks.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Wed, 27 Jul 2022 00:42:39 -0500 -Subject: [PATCH] Add more logger output for invalid movement kicks - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index e22f403c54922f05d6d5704ec648dce02097bbb0..9b488a98fdf06515ff267040dd6d654004665207 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -867,6 +867,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - if (packet.getId() == this.awaitingTeleport) { - if (this.awaitingPositionFromClient == null) { - this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause -+ ServerGamePacketListenerImpl.LOGGER.warn("Disconnected on accept teleport packet. Was not expecting position data from client at this time"); // Purpur - return; - } - -@@ -1455,8 +1456,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic - @Override - public void handleMovePlayer(ServerboundMovePlayerPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); -- if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) { -+ // Purpur start -+ boolean invalidX = Double.isNaN(packet.getX(0.0D)); -+ boolean invalidY = Double.isNaN(packet.getY(0.0D)); -+ boolean invalidZ = Double.isNaN(packet.getZ(0.0D)); -+ boolean invalidYaw = !Floats.isFinite(packet.getYRot(0.0F)); -+ boolean invalidPitch = !Floats.isFinite(packet.getXRot(0.0F)); -+ if (invalidX || invalidY || invalidZ || invalidYaw || invalidPitch) { - this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause -+ ServerGamePacketListenerImpl.LOGGER.warn(String.format("Disconnected on move player packet. Invalid data: x=%b, y=%b, z=%b, yaw=%b, pitch=%b", invalidX, invalidY, invalidZ, invalidYaw, invalidPitch)); -+ // Purpur end - } else { - ServerLevel worldserver = this.player.serverLevel(); - diff --git a/patches/server/0271-Add-Bee-API.patch b/patches/server/0271-Add-Bee-API.patch deleted file mode 100644 index 9103ece8b..000000000 --- a/patches/server/0271-Add-Bee-API.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Mon, 25 Jul 2022 19:33:49 +0200 -Subject: [PATCH] Add Bee API - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index e837500019157129007841c847d807ebae10db04..6c04c8e7776b2830ac368229da834532e8ce163e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -807,6 +807,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - if (optional.isPresent()) { - Bee.this.savedFlowerPos = (BlockPos) optional.get(); - Bee.this.navigation.moveTo((double) Bee.this.savedFlowerPos.getX() + 0.5D, (double) Bee.this.savedFlowerPos.getY() + 0.5D, (double) Bee.this.savedFlowerPos.getZ() + 0.5D, 1.2000000476837158D); -+ new org.purpurmc.purpur.event.entity.BeeFoundFlowerEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos)).callEvent(); // Purpur - return true; - } else { - Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60); -@@ -863,6 +864,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.pollinating = false; - Bee.this.navigation.stop(); - Bee.this.remainingCooldownBeforeLocatingNewFlower = 200; -+ new org.purpurmc.purpur.event.entity.BeeStopPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), Bee.this.savedFlowerPos == null ? null : io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos), Bee.this.hasNectar()).callEvent(); // Purpur - } - - @Override -@@ -909,6 +911,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.setWantedPos(); - } - -+ if (this.successfulPollinatingTicks == 0) new org.purpurmc.purpur.event.entity.BeeStartedPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos)).callEvent(); // Purpur - ++this.successfulPollinatingTicks; - if (Bee.this.random.nextFloat() < 0.05F && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) { - this.lastSoundPlayedTick = this.successfulPollinatingTicks; diff --git a/patches/server/0272-Debug-Marker-API.patch b/patches/server/0272-Debug-Marker-API.patch deleted file mode 100644 index 5adeb3b9e..000000000 --- a/patches/server/0272-Debug-Marker-API.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Sat, 23 Jul 2022 14:40:38 +0200 -Subject: [PATCH] Debug Marker API - - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 790bf8b0a87c00a45fac2737b6148b2144f838e7..5ac085f56464dfd9ff6b3786968ad43f4f506371 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1566,6 +1566,42 @@ public final class CraftServer implements Server { - public void removeFuel(org.bukkit.Material material) { - net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity.removeFuel(net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material))); - } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration) { -+ sendBlockHighlight(location, duration, "", 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, int argb) { -+ sendBlockHighlight(location, duration, "", argb); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text) { -+ sendBlockHighlight(location, duration, text, 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, int argb) { -+ this.worlds.forEach((name, world) -> world.sendBlockHighlight(location, duration, text, argb)); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { -+ sendBlockHighlight(location, duration, "", color, transparency); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { -+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); -+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); -+ } -+ -+ @Override -+ public void clearBlockHighlights() { -+ this.worlds.forEach((name, world) -> clearBlockHighlights()); -+ } - // Purpur End - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index a2e852adf47261b1b2eb9734cc90f4676ed58126..1392e483c363e25d1f16465d876cb7d7c70afa68 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2294,6 +2294,42 @@ public class CraftWorld extends CraftRegionAccessor implements World { - public float getLocalDifficultyAt(Location location) { - return getHandle().getCurrentDifficultyAt(io.papermc.paper.util.MCUtil.toBlockPosition(location)).getEffectiveDifficulty(); - } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration) { -+ sendBlockHighlight(location, duration, "", 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, int argb) { -+ sendBlockHighlight(location, duration, "", argb); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text) { -+ sendBlockHighlight(location, duration, text, 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, int argb) { -+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestAddMarker(getHandle(), io.papermc.paper.util.MCUtil.toBlockPosition(location), text, argb, duration); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { -+ sendBlockHighlight(location, duration, "", color, transparency); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { -+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); -+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); -+ } -+ -+ @Override -+ public void clearBlockHighlights() { -+ net.minecraft.network.protocol.game.DebugPackets.sendGameTestClearPacket(getHandle()); -+ } - // Purpur end - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 5baf36d8d65574f3e57de0937a949cff03435d30..beae58414b27d2a33bb6507ae8ab2104fde2cc0e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3334,5 +3334,48 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - public void setSpawnInvulnerableTicks(int spawnInvulnerableTime) { - getHandle().spawnInvulnerableTime = spawnInvulnerableTime; - } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration) { -+ sendBlockHighlight(location, duration, "", 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, int argb) { -+ sendBlockHighlight(location, duration, "", argb); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text) { -+ sendBlockHighlight(location, duration, text, 0x6400FF00); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, int argb) { -+ if (this.getHandle().connection == null) return; -+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); -+ buf.writeBlockPos(io.papermc.paper.util.MCUtil.toBlockPosition(location)); -+ buf.writeInt(argb); -+ buf.writeUtf(text); -+ buf.writeInt(duration); -+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.DEBUG_GAME_TEST_ADD_MARKER, buf)); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { -+ sendBlockHighlight(location, duration, "", color, transparency); -+ } -+ -+ @Override -+ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { -+ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); -+ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); -+ } -+ -+ @Override -+ public void clearBlockHighlights() { -+ if (this.getHandle().connection == null) return; -+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.DEBUG_GAME_TEST_CLEAR, new FriendlyByteBuf(io.netty.buffer.Unpooled.buffer()))); -+ } - // Purpur end - } diff --git a/patches/server/0273-mob-spawning-option-to-ignore-creative-players.patch b/patches/server/0273-mob-spawning-option-to-ignore-creative-players.patch deleted file mode 100644 index 9b75d48ec..000000000 --- a/patches/server/0273-mob-spawning-option-to-ignore-creative-players.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Fri, 26 Aug 2022 22:44:41 -0700 -Subject: [PATCH] mob spawning option to ignore creative players - - -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index ad89aac8d6b1ac1095806930afd4fe196084525b..1f9994f4b0b736f64a8676d9431469527c6484df 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -260,7 +260,7 @@ public final class NaturalSpawner { - blockposition_mutableblockposition.set(l, i, i1); - double d0 = (double) l + 0.5D; - double d1 = (double) i1 + 0.5D; -- Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); // Paper - use chunk's player cache to optimize search in range -+ Player entityhuman = (chunk instanceof LevelChunk) ? ((LevelChunk)chunk).findNearestPlayer(d0, i, d1, 576.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers ? net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR : net.minecraft.world.entity.EntitySelector.NO_SPECTATORS) : world.getNearestPlayer(d0, (double) i, d1, -1.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers); // Paper - use chunk's player cache to optimize search in range // Purpur - - if (entityhuman != null) { - double d2 = entityhuman.distanceToSqr(d0, (double) i, d1); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 370b5ff992fb3ff7dd88ab479b3c251d36ced42f..8519259a086dfcceb793e5e1177e06248c97bc2e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -377,6 +377,7 @@ public class PurpurWorldConfig { - public boolean phantomSpawning; - public boolean villagerTraderSpawning; - public boolean villageSiegeSpawning; -+ public boolean mobSpawningIgnoreCreativePlayers = false; - private void mobSpawnerSettings() { - // values of "default" or null will default to true only if the world environment is normal (aka overworld) - Predicate predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL); -@@ -385,6 +386,7 @@ public class PurpurWorldConfig { - phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate); - villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate); - villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate); -+ mobSpawningIgnoreCreativePlayers = getBoolean("gameplay-mechanics.mob-spawning.ignore-creative-players", mobSpawningIgnoreCreativePlayers); - } - - public boolean disableObserverClocks = false; diff --git a/patches/server/0274-Add-skeleton-bow-accuracy-option.patch b/patches/server/0274-Add-skeleton-bow-accuracy-option.patch deleted file mode 100644 index 85cdb8a96..000000000 --- a/patches/server/0274-Add-skeleton-bow-accuracy-option.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 2 Sep 2022 13:04:53 -0500 -Subject: [PATCH] Add skeleton bow accuracy option - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -index 41376b705748e14c1c4174e07732ce09ad8e581f..dd84433a988712da9d799cbda2487a902315fb92 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -182,7 +182,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - double d2 = target.getZ() - this.getZ(); - double d3 = Math.sqrt(d0 * d0 + d2 * d2); - -- entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.level().getDifficulty().getId() * 4)); -+ entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, this.level().purpurConfig.skeletonBowAccuracyMap.getOrDefault(this.level().getDifficulty().getId(), (float) (14 - this.level().getDifficulty().getId() * 4))); // Purpur - // CraftBukkit start - org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), entityarrow.getPickupItem(), entityarrow, net.minecraft.world.InteractionHand.MAIN_HAND, 0.8F, true); // Paper - if (event.isCancelled()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8519259a086dfcceb793e5e1177e06248c97bc2e..5f4b6d211e1a310ca2efcc94686e2757cff973ec 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2453,6 +2453,8 @@ public class PurpurWorldConfig { - public boolean skeletonAlwaysDropExp = false; - public double skeletonHeadVisibilityPercent = 0.5D; - public int skeletonFeedWitherRoses = 0; -+ public String skeletonBowAccuracy = "14 - difficulty * 4"; -+ public Map skeletonBowAccuracyMap = new HashMap<>(); - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -2467,6 +2469,18 @@ public class PurpurWorldConfig { - skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp); - skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent); - skeletonFeedWitherRoses = getInt("mobs.skeleton.feed-wither-roses", skeletonFeedWitherRoses); -+ final String defaultSkeletonBowAccuracy = skeletonBowAccuracy; -+ skeletonBowAccuracy = getString("mobs.skeleton.bow-accuracy", skeletonBowAccuracy); -+ for (int i = 1; i < 4; i++) { -+ final float divergence; -+ try { -+ divergence = ((Number) Entity.scriptEngine.eval("let difficulty = " + i + "; " + skeletonBowAccuracy)).floatValue(); -+ } catch (javax.script.ScriptException e) { -+ e.printStackTrace(); -+ break; -+ } -+ skeletonBowAccuracyMap.put(i, divergence); -+ } - } - - public boolean skeletonHorseRidableInWater = true; diff --git a/patches/server/0276-Add-death-screen-API.patch b/patches/server/0276-Add-death-screen-API.patch deleted file mode 100644 index 3e78bfa92..000000000 --- a/patches/server/0276-Add-death-screen-API.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Fri, 23 Sep 2022 18:41:05 -0700 -Subject: [PATCH] Add death screen API - - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index beae58414b27d2a33bb6507ae8ab2104fde2cc0e..668d825a2469706e4de11629a0b41877de700ca6 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3377,5 +3377,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - if (this.getHandle().connection == null) return; - this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.DEBUG_GAME_TEST_CLEAR, new FriendlyByteBuf(io.netty.buffer.Unpooled.buffer()))); - } -+ -+ @Override -+ public void sendDeathScreen(net.kyori.adventure.text.Component message) { -+ if (this.getHandle().connection == null) return; -+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message))); -+ } - // Purpur end - } diff --git a/patches/server/0277-Make-pufferfish-config-relocatable.patch b/patches/server/0277-Make-pufferfish-config-relocatable.patch deleted file mode 100644 index 3970a76c1..000000000 --- a/patches/server/0277-Make-pufferfish-config-relocatable.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 12 Jun 2022 09:18:57 -0500 -Subject: [PATCH] Make pufferfish config relocatable - - -diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -index 219fc5bbe7dfade0b3576e6e1e7e60e361fdee0f..3e36958365bc136516bafbaad0c168f7956406f1 100644 ---- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java -@@ -28,6 +28,7 @@ public class PufferfishConfig { - - private static final YamlFile config = new YamlFile(); - private static int updates = 0; -+ public static File pufferfishFile; // Purpur - - private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { - ConfigurationSection newSection = new MemoryConfiguration(); -@@ -50,7 +51,7 @@ public class PufferfishConfig { - } - - public static void load() throws IOException { -- File configFile = new File("pufferfish.yml"); -+ File configFile = pufferfishFile; // Purpur - - if (configFile.exists()) { - try { -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index fa258f1a15a93db0c3401397433b184938412a9b..41db72915baa4c02b11a701dfdde09b66ba72476 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -230,6 +230,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now - io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider - // Paper end -+ gg.pufferfish.pufferfish.PufferfishConfig.pufferfishFile = (java.io.File) options.valueOf("pufferfish-settings"); // Purpur - gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish - gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish - -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 75e10acdab366deea1ff47274424aeae11173c8c..9a3374a1236164194ef0df43ed639296cbe1c731 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -179,6 +179,12 @@ public class Main { - .ofType(File.class) - .defaultsTo(new File("purpur.yml")) - .describedAs("Yml file"); -+ -+ acceptsAll(asList("pufferfish", "pufferfish-settings"), "File for pufferfish settings") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("pufferfish.yml")) -+ .describedAs("Yml file"); - // Purpur end - - // Paper start diff --git a/patches/server/0278-Implement-ram-and-rambar-commands.patch b/patches/server/0278-Implement-ram-and-rambar-commands.patch deleted file mode 100644 index a2e8c3bcf..000000000 --- a/patches/server/0278-Implement-ram-and-rambar-commands.patch +++ /dev/null @@ -1,361 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Mon, 26 Sep 2022 07:43:30 -0500 -Subject: [PATCH] Implement ram and rambar commands - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 52b06c34d9d3ffb8844556e7b0eaed5a7f03da0c..5c0085589b08f199c75ceeab8d0cf25e970a0533 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -230,6 +230,8 @@ public class Commands { - org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur - org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur -+ org.purpurmc.purpur.command.RamBarCommand.register(this.dispatcher); // Purpur -+ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur - } - - if (environment.includeIntegrated) { -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 2f013c3365d065325d645d43f7c5137f942b06df..0249eeb3937cf48cea13846a7e39b248947e21a4 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -280,6 +280,7 @@ public class ServerPlayer extends Player { - public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event - public boolean purpurClient = false; // Purpur - public boolean acceptingResourcePack = false; // Purpur -+ private boolean ramBar = false; // Purpur - private boolean tpsBar = false; // Purpur - private boolean compassBar = false; // Purpur - -@@ -562,6 +563,7 @@ public class ServerPlayer extends Player { - } - } - -+ if (nbt.contains("Purpur.RamBar")) { this.ramBar = nbt.getBoolean("Purpur.RamBar"); } // Purpur - if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur - if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur - } -@@ -630,6 +632,7 @@ public class ServerPlayer extends Player { - } - this.getBukkitEntity().setExtraData(nbt); // CraftBukkit - -+ nbt.putBoolean("Purpur.RamBar", this.ramBar); // Purpur - nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur - nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - } -@@ -2816,6 +2819,14 @@ public class ServerPlayer extends Player { - } - } - -+ public boolean ramBar() { -+ return this.ramBar; -+ } -+ -+ public void ramBar(boolean ramBar) { -+ this.ramBar = ramBar; -+ } -+ - public boolean tpsBar() { - return this.tpsBar; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index ed9b2f0b55229848894d9d6b401d050cb031b893..568bb53e91dda4804cd328a81ba12ce735c52603 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -182,6 +182,8 @@ public class PurpurConfig { - public static String creditsCommandOutput = "%s has been shown the end credits"; - public static String demoCommandOutput = "%s has been shown the demo screen"; - public static String pingCommandOutput = "%s's ping is %sms"; -+ public static String ramCommandOutput = "Ram Usage: / ()"; -+ public static String rambarCommandOutput = "Rambar toggled for "; - public static String tpsbarCommandOutput = "Tpsbar toggled for "; - public static String dontRunWithScissors = "Don't run with scissors!"; - public static String uptimeCommandOutput = "Server uptime is "; -@@ -199,6 +201,8 @@ public class PurpurConfig { - creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput); - demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); - pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); -+ ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput); -+ rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput); - tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); - dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); - uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput); -@@ -247,6 +251,15 @@ public class PurpurConfig { - disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops); - } - -+ public static String commandRamBarTitle = "Ram: / ()"; -+ public static BossBar.Overlay commandRamBarProgressOverlay = BossBar.Overlay.NOTCHED_20; -+ public static BossBar.Color commandRamBarProgressColorGood = BossBar.Color.GREEN; -+ public static BossBar.Color commandRamBarProgressColorMedium = BossBar.Color.YELLOW; -+ public static BossBar.Color commandRamBarProgressColorLow = BossBar.Color.RED; -+ public static String commandRamBarTextColorGood = ""; -+ public static String commandRamBarTextColorMedium = ""; -+ public static String commandRamBarTextColorLow = ""; -+ public static int commandRamBarTickInterval = 20; - public static String commandTPSBarTitle = "TPS: MSPT: Ping: ms"; - public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20; - public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT; -@@ -274,6 +287,16 @@ public class PurpurConfig { - public static String uptimeSecond = "%02d second"; - public static String uptimeSeconds = "%02d seconds"; - private static void commandSettings() { -+ commandRamBarTitle = getString("settings.command.rambar.title", commandRamBarTitle); -+ commandRamBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.rambar.overlay", commandRamBarProgressOverlay.name())); -+ commandRamBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.good", commandRamBarProgressColorGood.name())); -+ commandRamBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.medium", commandRamBarProgressColorMedium.name())); -+ commandRamBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.low", commandRamBarProgressColorLow.name())); -+ commandRamBarTextColorGood = getString("settings.command.rambar.text-color.good", commandRamBarTextColorGood); -+ commandRamBarTextColorMedium = getString("settings.command.rambar.text-color.medium", commandRamBarTextColorMedium); -+ commandRamBarTextColorLow = getString("settings.command.rambar.text-color.low", commandRamBarTextColorLow); -+ commandRamBarTickInterval = getInt("settings.command.rambar.tick-interval", commandRamBarTickInterval); -+ - commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); - commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); - commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name())); -diff --git a/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..2852c07adb080c34905f5d1b19efed8ea47eecc6 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java -@@ -0,0 +1,44 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.format.NamedTextColor; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import net.minecraft.commands.arguments.EntityArgument; -+import net.minecraft.server.level.ServerPlayer; -+import org.purpurmc.purpur.PurpurConfig; -+import org.purpurmc.purpur.task.RamBarTask; -+ -+import java.util.Collection; -+import java.util.Collections; -+ -+public class RamBarCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("rambar") -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar")) -+ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) -+ .then(Commands.argument("targets", EntityArgument.players()) -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.rambar.other")) -+ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) -+ ) -+ ); -+ } -+ -+ private static int execute(CommandSourceStack sender, Collection targets) { -+ for (ServerPlayer player : targets) { -+ boolean result = RamBarTask.instance().togglePlayer(player.getBukkitEntity()); -+ player.ramBar(result); -+ -+ Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.rambarCommandOutput, -+ Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off") -+ .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)), -+ Placeholder.parsed("target", player.getGameProfile().getName())); -+ -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/command/RamCommand.java b/src/main/java/org/purpurmc/purpur/command/RamCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..992f8dfc628c7485e335191e1308cdfd4eedfbe8 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/command/RamCommand.java -@@ -0,0 +1,30 @@ -+package org.purpurmc.purpur.command; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import io.papermc.paper.adventure.PaperAdventure; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import net.minecraft.commands.CommandSourceStack; -+import net.minecraft.commands.Commands; -+import org.purpurmc.purpur.PurpurConfig; -+import org.purpurmc.purpur.task.RamBarTask; -+ -+public class RamCommand { -+ public static void register(CommandDispatcher dispatcher) { -+ dispatcher.register(Commands.literal("ram") -+ .requires(listener -> listener.hasPermission(2, "bukkit.command.ram")) -+ .executes(context -> { -+ CommandSourceStack sender = context.getSource(); -+ RamBarTask ramBar = RamBarTask.instance(); -+ sender.sendSuccess(() -> PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(PurpurConfig.ramCommandOutput, -+ Placeholder.component("allocated", ramBar.format(ramBar.getAllocated())), -+ Placeholder.component("used", ramBar.format(ramBar.getUsed())), -+ Placeholder.component("xmx", ramBar.format(ramBar.getXmx())), -+ Placeholder.component("xms", ramBar.format(ramBar.getXms())), -+ Placeholder.unparsed("percent", ((int) (ramBar.getPercent() * 100)) + "%") -+ )), false); -+ return 1; -+ }) -+ ); -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -index d333334f323049ca97e756324cff0b23eddacd2a..114f273dd7f8b8a3c02f0651f6944859b33a65d4 100644 ---- a/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -+++ b/src/main/java/org/purpurmc/purpur/task/BossBarTask.java -@@ -89,17 +89,22 @@ public abstract class BossBarTask extends BukkitRunnable { - } - - public static void startAll() { -+ RamBarTask.instance().start(); - TPSBarTask.instance().start(); - CompassTask.instance().start(); - } - - public static void stopAll() { -+ RamBarTask.instance().stop(); - TPSBarTask.instance().stop(); - CompassTask.instance().stop(); - } - - public static void addToAll(ServerPlayer player) { - Player bukkit = player.getBukkitEntity(); -+ if (player.ramBar()) { -+ RamBarTask.instance().addPlayer(bukkit); -+ } - if (player.tpsBar()) { - TPSBarTask.instance().addPlayer(bukkit); - } -@@ -109,6 +114,7 @@ public abstract class BossBarTask extends BukkitRunnable { - } - - public static void removeFromAll(Player player) { -+ RamBarTask.instance().removePlayer(player); - TPSBarTask.instance().removePlayer(player); - CompassTask.instance().removePlayer(player); - } -diff --git a/src/main/java/org/purpurmc/purpur/task/RamBarTask.java b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8e98c0ae73e2c40002a72b5d0d246ffa0c3ab38f ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/task/RamBarTask.java -@@ -0,0 +1,117 @@ -+package org.purpurmc.purpur.task; -+ -+import net.kyori.adventure.bossbar.BossBar; -+import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.minimessage.MiniMessage; -+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -+import org.bukkit.entity.Player; -+import org.purpurmc.purpur.PurpurConfig; -+ -+import java.lang.management.ManagementFactory; -+import java.lang.management.MemoryUsage; -+ -+public class RamBarTask extends BossBarTask { -+ private static RamBarTask instance; -+ private long allocated = 0L; -+ private long used = 0L; -+ private long xmx = 0L; -+ private long xms = 0L; -+ private float percent = 0F; -+ private int tick = 0; -+ -+ public static RamBarTask instance() { -+ if (instance == null) { -+ instance = new RamBarTask(); -+ } -+ return instance; -+ } -+ -+ @Override -+ BossBar createBossBar() { -+ return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandRamBarProgressOverlay); -+ } -+ -+ @Override -+ void updateBossBar(BossBar bossbar, Player player) { -+ bossbar.progress(getBossBarProgress()); -+ bossbar.color(getBossBarColor()); -+ bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandRamBarTitle, -+ Placeholder.component("allocated", format(this.allocated)), -+ Placeholder.component("used", format(this.used)), -+ Placeholder.component("xmx", format(this.xmx)), -+ Placeholder.component("xms", format(this.xms)), -+ Placeholder.unparsed("percent", ((int) (this.percent * 100)) + "%") -+ )); -+ } -+ -+ @Override -+ public void run() { -+ if (++this.tick < PurpurConfig.commandRamBarTickInterval) { -+ return; -+ } -+ this.tick = 0; -+ -+ MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); -+ -+ this.allocated = heap.getCommitted(); -+ this.used = heap.getUsed(); -+ this.xmx = heap.getMax(); -+ this.xms = heap.getInit(); -+ this.percent = Math.max(Math.min((float) this.used / this.xmx, 1.0F), 0.0F); -+ -+ super.run(); -+ } -+ -+ private float getBossBarProgress() { -+ return this.percent; -+ } -+ -+ private BossBar.Color getBossBarColor() { -+ if (this.percent < 0.5F) { -+ return PurpurConfig.commandRamBarProgressColorGood; -+ } else if (this.percent < 0.75F) { -+ return PurpurConfig.commandRamBarProgressColorMedium; -+ } else { -+ return PurpurConfig.commandRamBarProgressColorLow; -+ } -+ } -+ -+ public Component format(long v) { -+ String color; -+ if (this.percent < 0.60F) { -+ color = PurpurConfig.commandRamBarTextColorGood; -+ } else if (this.percent < 0.85F) { -+ color = PurpurConfig.commandRamBarTextColorMedium; -+ } else { -+ color = PurpurConfig.commandRamBarTextColorLow; -+ } -+ String value; -+ if (v < 1024) { -+ value = v + "B"; -+ } else { -+ int z = (63 - Long.numberOfLeadingZeros(v)) / 10; -+ value = String.format("%.1f%s", (double) v / (1L << (z * 10)), "BKMGTPE".charAt(z)); -+ } -+ return MiniMessage.miniMessage().deserialize(color, Placeholder.unparsed("text", value)); -+ } -+ -+ public long getAllocated() { -+ return this.allocated; -+ } -+ -+ public long getUsed() { -+ return this.used; -+ } -+ -+ public long getXmx() { -+ return this.xmx; -+ } -+ -+ public long getXms() { -+ return this.xms; -+ } -+ -+ public float getPercent() { -+ return this.percent; -+ } -+} diff --git a/patches/server/0280-Add-an-option-to-fix-MC-3304-projectile-looting.patch b/patches/server/0280-Add-an-option-to-fix-MC-3304-projectile-looting.patch deleted file mode 100644 index b24108886..000000000 --- a/patches/server/0280-Add-an-option-to-fix-MC-3304-projectile-looting.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Sat, 1 Oct 2022 11:33:15 -0700 -Subject: [PATCH] Add an option to fix MC-3304 (projectile looting) - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -index e8773aa2e796ca4e3b9b7cf85c40906d16eb241d..6b070ad6aa1b62a66fb85d54042af9d64cdcea7f 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -72,6 +72,7 @@ public abstract class AbstractArrow extends Projectile { - private IntOpenHashSet piercingIgnoreEntityIds; - @Nullable - private List piercedAndKilledEntities; -+ public int lootingLevel; // Purpur - - // Spigot Start - @Override -@@ -612,6 +613,12 @@ public abstract class AbstractArrow extends Projectile { - this.knockback = punch; - } - -+ // Purpur start -+ public void setLootingLevel(int looting) { -+ this.lootingLevel = looting; -+ } -+ // Purpur end -+ - public int getKnockback() { - return this.knockback; - } -diff --git a/src/main/java/net/minecraft/world/item/BowItem.java b/src/main/java/net/minecraft/world/item/BowItem.java -index 220513d3fd5645322886522ea4f6b8c55d043b3c..d45a2f49c82d00801578c34e5f5277fc5e82be87 100644 ---- a/src/main/java/net/minecraft/world/item/BowItem.java -+++ b/src/main/java/net/minecraft/world/item/BowItem.java -@@ -64,6 +64,13 @@ public class BowItem extends ProjectileWeaponItem implements Vanishable { - if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.FLAMING_ARROWS, stack) > 0) { - entityarrow.setSecondsOnFire(100); - } -+ // Purpur start -+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MOB_LOOTING, stack); -+ -+ if (lootingLevel > 0) { -+ entityarrow.setLootingLevel(lootingLevel); -+ } -+ // Purpur end - // CraftBukkit start - org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(entityhuman, stack, itemstack1, entityarrow, entityhuman.getUsedItemHand(), f, !flag1); - if (event.isCancelled()) { -diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java -index 0a0f19f73a6a4e5aece7c17089dc4d31ed2a5299..2c51a73ebfd05af21b0f5d731fc9f1df77fed1a1 100644 ---- a/src/main/java/net/minecraft/world/item/CrossbowItem.java -+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java -@@ -291,6 +291,14 @@ public class CrossbowItem extends ProjectileWeaponItem implements Vanishable { - entityarrow.setPierceLevel((byte) i); - } - -+ // Purpur start -+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.MOB_LOOTING, crossbow); -+ -+ if (lootingLevel > 0) { -+ entityarrow.setLootingLevel(lootingLevel); -+ } -+ // Purpur end -+ - return entityarrow; - } - -diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java -index 6d1573161f0d8c7999f84925ba7bbf536ee9583a..c32cbe6065ecb6810f352b8a3598c21e42e60e1d 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -82,6 +82,14 @@ public class TridentItem extends Item implements Vanishable { - entitythrowntrident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; - } - -+ // Purpur start -+ int lootingLevel = EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MOB_LOOTING, stack); -+ -+ if (lootingLevel > 0) { -+ entitythrowntrident.setLootingLevel(lootingLevel); -+ } -+ // Purpur end -+ - // CraftBukkit start - // Paper start - com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) entitythrowntrident.getBukkitEntity()); -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java b/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java -index 31918fa2eb38e42a5ea5366e559f25ea9d7d59ae..15d8e9261a89da30529ac347462c520920ca4e7d 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/functions/LootingEnchantFunction.java -@@ -49,6 +49,13 @@ public class LootingEnchantFunction extends LootItemConditionalFunction { - - if (entity instanceof LivingEntity) { - int i = EnchantmentHelper.getMobLooting((LivingEntity) entity); -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.fixProjectileLootingTransfer && -+ context.getParamOrNull(LootContextParams.DIRECT_KILLER_ENTITY) -+ instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) { -+ i = arrow.lootingLevel; -+ } -+ // Purpur end - // CraftBukkit start - use lootingModifier if set by plugin - if (context.hasParam(LootContextParams.LOOTING_MOD)) { - i = context.getParamOrNull(LootContextParams.LOOTING_MOD); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 62aabfc021fa3349b0ab46744a9ec78d57f058b0..32aaa66acc35950caec1a0b0dac9413e950b37a6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -568,4 +568,9 @@ public class PurpurConfig { - private static void fixNetworkSerializedCreativeItems() { - fixNetworkSerializedItemsInCreative = getBoolean("settings.fix-network-serialized-items-in-creative", fixNetworkSerializedItemsInCreative); - } -+ -+ public static boolean fixProjectileLootingTransfer = false; -+ private static void fixProjectileLootingTransfer() { -+ fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer); -+ } - } diff --git a/patches/server/0281-Configurable-block-blast-resistance.patch b/patches/server/0281-Configurable-block-blast-resistance.patch deleted file mode 100644 index 9fc5ce17f..000000000 --- a/patches/server/0281-Configurable-block-blast-resistance.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Sat, 1 Oct 2022 13:29:17 -0700 -Subject: [PATCH] Configurable block blast resistance - - -diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index 5ac102afde62c08f36886b466010ccfedabfa05e..942ce713afe27ec75d849877a88721ef6334fafa 100644 ---- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -81,7 +81,7 @@ public abstract class BlockBehaviour implements FeatureElement { - - protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP}; - public final boolean hasCollision; -- protected final float explosionResistance; -+ public float explosionResistance; // Purpur - protected final -> public - protected final boolean isRandomlyTicking; - protected final SoundType soundType; - protected final float friction; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 32aaa66acc35950caec1a0b0dac9413e950b37a6..8426be7906cd0f4df8c274aa1d113388965d8d8b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -573,4 +573,19 @@ public class PurpurConfig { - private static void fixProjectileLootingTransfer() { - fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer); - } -+ -+ private static void blastResistanceSettings() { -+ getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { -+ log(Level.SEVERE, "Invalid block for `settings.blast-resistance-overrides`: " + blockId); -+ return; -+ } -+ if (!(value instanceof Number blastResistance)) { -+ log(Level.SEVERE, "Invalid blast resistance for `settings.blast-resistance-overrides." + blockId + "`: " + value); -+ return; -+ } -+ block.explosionResistance = blastResistance.floatValue(); -+ }); -+ } - } diff --git a/patches/server/0282-Configurable-block-fall-damage-modifiers.patch b/patches/server/0282-Configurable-block-fall-damage-modifiers.patch deleted file mode 100644 index ae3343513..000000000 --- a/patches/server/0282-Configurable-block-fall-damage-modifiers.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Sat, 1 Oct 2022 18:06:52 -0700 -Subject: [PATCH] Configurable block fall damage modifiers - - -diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java -index e8405a57fb88e63b63baaf00645c417633bdc0f2..2b66ddafaaca17f64d1e7502dfa4d7576e3e032f 100644 ---- a/src/main/java/net/minecraft/world/level/block/BedBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java -@@ -173,7 +173,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock - - @Override - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { -- super.fallOn(world, state, pos, entity, fallDistance * 0.5F); -+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java -index 69c78ee2d012344505063c9b297c9c550bde80c4..dc718efe054aaf36961514287c9cc48e2d28bc4c 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -94,6 +94,10 @@ public class Block extends BlockBehaviour implements ItemLike { - public static final int UPDATE_LIMIT = 512; - protected final StateDefinition stateDefinition; - private BlockState defaultBlockState; -+ // Purpur start -+ public float fallDamageMultiplier = 1.0F; -+ public float fallDistanceMultiplier = 1.0F; -+ // Purpur end - // Paper start - public final boolean isDestroyable() { - return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || -@@ -506,7 +510,7 @@ public class Block extends BlockBehaviour implements ItemLike { - } - - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { -- entity.causeFallDamage(fallDistance, 1.0F, entity.damageSources().fall()); -+ entity.causeFallDamage(fallDistance * fallDistanceMultiplier, fallDamageMultiplier, entity.damageSources().fall()); // Purpur - } - - public void updateEntityAfterFallOn(BlockGetter world, Entity entity) { -diff --git a/src/main/java/net/minecraft/world/level/block/HayBlock.java b/src/main/java/net/minecraft/world/level/block/HayBlock.java -index cfbe1dae76db76cf54a4f5d72aca72d5e893859e..74cb10230d459ac9f300a9d59af504d233ac663e 100644 ---- a/src/main/java/net/minecraft/world/level/block/HayBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/HayBlock.java -@@ -15,6 +15,6 @@ public class HayBlock extends RotatedPillarBlock { - - @Override - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { -- entity.causeFallDamage(fallDistance, 0.2F, world.damageSources().fall()); -+ super.fallOn(world, state, pos, entity, fallDistance); // Purpur - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 8426be7906cd0f4df8c274aa1d113388965d8d8b..29dd74f28c66cce2c25dc9aa916f230cdfbe2da3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -588,4 +588,50 @@ public class PurpurConfig { - block.explosionResistance = blastResistance.floatValue(); - }); - } -+ private static void blockFallMultiplierSettings() { -+ getMap("settings.block-fall-multipliers", Map.ofEntries( -+ Map.entry("minecraft:hay_block", Map.of("damage", 0.2F)), -+ Map.entry("minecraft:white_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:light_gray_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:gray_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:black_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:brown_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:pink_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:red_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:orange_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:yellow_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:green_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:lime_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:cyan_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:light_blue_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:blue_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:purple_bed", Map.of("distance", 0.5F)), -+ Map.entry("minecraft:magenta_bed", Map.of("distance", 0.5F)) -+ )).forEach((blockId, value) -> { -+ Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); -+ if (block == Blocks.AIR) { -+ log(Level.SEVERE, "Invalid block for `settings.block-fall-multipliers`: " + blockId); -+ return; -+ } -+ if (!(value instanceof Map map)) { -+ log(Level.SEVERE, "Invalid fall multiplier for `settings.block-fall-multipliers." + blockId + "`: " + value -+ + ", expected a map with keys `damage` and `distance` to floats."); -+ return; -+ } -+ Object rawFallDamageMultiplier = map.get("damage"); -+ if (rawFallDamageMultiplier == null) rawFallDamageMultiplier = 1F; -+ if (!(rawFallDamageMultiplier instanceof Number fallDamageMultiplier)) { -+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".damage`: " + map.get("damage")); -+ return; -+ } -+ Object rawFallDistanceMultiplier = map.get("distance"); -+ if (rawFallDistanceMultiplier == null) rawFallDistanceMultiplier = 1F; -+ if (!(rawFallDistanceMultiplier instanceof Number fallDistanceMultiplier)) { -+ log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".distance`: " + map.get("distance")); -+ return; -+ } -+ block.fallDamageMultiplier = fallDamageMultiplier.floatValue(); -+ block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue(); -+ }); -+ } - } diff --git a/patches/server/0283-Language-API.patch b/patches/server/0283-Language-API.patch deleted file mode 100644 index edaf2a367..000000000 --- a/patches/server/0283-Language-API.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Sat, 1 Oct 2022 17:08:43 -0700 -Subject: [PATCH] Language API - - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 5ac085f56464dfd9ff6b3786968ad43f4f506371..b08d4a2a74392b1d59b1eeeab3108103d83ad96c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -397,6 +397,20 @@ public final class CraftServer implements Server { - this.dataPackManager = new CraftDataPackManager(this.getServer().getPackRepository()); - - Bukkit.setServer(this); -+ // Purpur start -+ org.purpurmc.purpur.language.Language.setLanguage(new org.purpurmc.purpur.language.Language() { -+ private net.minecraft.locale.Language language = net.minecraft.locale.Language.getInstance(); -+ @Override -+ public boolean has(@org.jetbrains.annotations.NotNull String key) { -+ return language.has(key); -+ } -+ -+ @Override -+ public @org.jetbrains.annotations.NotNull String getOrDefault(@org.jetbrains.annotations.NotNull String key) { -+ return language.getOrDefault(key); -+ } -+ }); -+ // Purpur end - - // Register all the Enchantments and PotionTypes now so we can stop new registration immediately after - Enchantments.SHARPNESS.getClass(); diff --git a/patches/server/0284-Milk-Keeps-Beneficial-Effects.patch b/patches/server/0284-Milk-Keeps-Beneficial-Effects.patch deleted file mode 100644 index 5677b8be7..000000000 --- a/patches/server/0284-Milk-Keeps-Beneficial-Effects.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Rhythmic -Date: Thu, 6 Oct 2022 10:41:01 -0700 -Subject: [PATCH] Milk Keeps Beneficial Effects - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 9012b5b918bca409166db9eb29c201a793886dbb..0b08cf7b26731ffd6f3909342f4e3a92fe0c813e 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1124,6 +1124,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - for (flag = false; iterator.hasNext(); flag = true) { - // CraftBukkit start - MobEffectInstance effect = (MobEffectInstance) iterator.next(); -+ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level().purpurConfig.milkClearsBeneficialEffects && effect.getEffect().isBeneficial()) continue; // Purpur - EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED); - if (event.isCancelled()) { - continue; -diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -index 970b1f6eaeeddf9928ba61239248ecd2234eda9a..0e5fae26bf89749e446050c92dc7c23d788f5edb 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -110,7 +110,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return this.canDrinkPotion && this.level().isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API - })); - this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> { -- return this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API -+ return level().purpurConfig.milkClearsBeneficialEffects && this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API // Purpur - })); - this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); - this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a4c5c469fed51b46727783ddefa293e53b9c66ca..ad8f733ded15d6db3adabccd35bd035f87b2777c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -128,6 +128,7 @@ public class PurpurWorldConfig { - public boolean fireballsBypassMobGriefing = false; - public boolean imposeTeleportRestrictionsOnGateways = false; - public boolean milkCuresBadOmen = true; -+ public boolean milkClearsBeneficialEffects = true; - public boolean noteBlockIgnoreAbove = false; - public boolean persistentDroppableEntityDisplayNames = true; - public boolean persistentTileEntityDisplayNames = false; -@@ -154,6 +155,7 @@ public class PurpurWorldConfig { - fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); - imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); -+ milkClearsBeneficialEffects = getBoolean("gameplay-mechanics.milk-clears-beneficial-effects", milkClearsBeneficialEffects); - noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); - persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames); - persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); diff --git a/patches/server/0285-MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch b/patches/server/0285-MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch deleted file mode 100644 index 399485ead..000000000 --- a/patches/server/0285-MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Krakenied -Date: Sun, 9 Oct 2022 01:50:39 +0200 -Subject: [PATCH] MC-121706 - Fix mobs not looking up and down when strafing - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java -index 87fb10096fc9dade33c663234b1cecc34d3d77bb..874c7b29a261b1b5ad6e86ca219ff935870aecb0 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java -@@ -119,9 +119,9 @@ public class RangedBowAttackGoal extends Go - } - - this.mob.lookAt(livingEntity, 30.0F, 30.0F); -- } else { -+ } //else { // Purpur - fix MC-121706 - this.mob.getLookControl().setLookAt(livingEntity, 30.0F, 30.0F); -- } -+ //} // Purpur - - if (this.mob.isUsingItem()) { - if (!bl && this.seeTime < -60) { diff --git a/patches/server/0286-Add-log-suppression-for-LibraryLoader.patch b/patches/server/0286-Add-log-suppression-for-LibraryLoader.patch deleted file mode 100644 index 6a689fce9..000000000 --- a/patches/server/0286-Add-log-suppression-for-LibraryLoader.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Krakenied -Date: Fri, 14 Oct 2022 23:11:16 +0200 -Subject: [PATCH] Add log suppression for LibraryLoader - - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 29dd74f28c66cce2c25dc9aa916f230cdfbe2da3..cf0a52a037ac255c3f9e36dbff3cedda0fd8f49e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -465,11 +465,14 @@ public class PurpurConfig { - public static boolean loggerSuppressIgnoredAdvancementWarnings = false; - public static boolean loggerSuppressUnrecognizedRecipeErrors = false; - public static boolean loggerSuppressSetBlockFarChunk = false; -+ public static boolean loggerSuppressLibraryLoader = false; - private static void loggerSettings() { - loggerSuppressInitLegacyMaterialError = getBoolean("settings.logger.suppress-init-legacy-material-errors", loggerSuppressInitLegacyMaterialError); - loggerSuppressIgnoredAdvancementWarnings = getBoolean("settings.logger.suppress-ignored-advancement-warnings", loggerSuppressIgnoredAdvancementWarnings); - loggerSuppressUnrecognizedRecipeErrors = getBoolean("settings.logger.suppress-unrecognized-recipe-errors", loggerSuppressUnrecognizedRecipeErrors); - loggerSuppressSetBlockFarChunk = getBoolean("settings.logger.suppress-setblock-in-far-chunk-errors", loggerSuppressSetBlockFarChunk); -+ loggerSuppressLibraryLoader = getBoolean("settings.logger.suppress-library-loader", loggerSuppressLibraryLoader); -+ org.bukkit.plugin.java.JavaPluginLoader.SuppressLibraryLoaderLogger = loggerSuppressLibraryLoader; - } - - public static boolean tpsCatchup = true; diff --git a/patches/server/0287-Add-option-to-allow-creeper-to-encircle-target-when-.patch b/patches/server/0287-Add-option-to-allow-creeper-to-encircle-target-when-.patch deleted file mode 100644 index 6bdde9518..000000000 --- a/patches/server/0287-Add-option-to-allow-creeper-to-encircle-target-when-.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jared Seville -Date: Sat, 15 Oct 2022 16:01:03 -0700 -Subject: [PATCH] Add option to allow creeper to encircle target when fusing. - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java -index e241ae250f4f04a17ef2c583d00b065a4ca56a4c..7b99c3446b50939241d3e220d93e05649f72a6df 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/SwellGoal.java -@@ -54,6 +54,14 @@ public class SwellGoal extends Goal { - this.creeper.setSwellDir(-1); - } else { - this.creeper.setSwellDir(1); -+ // Purpur start -+ if (this.creeper.level().purpurConfig.creeperEncircleTarget) { -+ net.minecraft.world.phys.Vec3 relative = this.creeper.position().subtract(this.target.position()); -+ relative = relative.yRot((float) Math.PI / 3).normalize().multiply(2, 2, 2); -+ net.minecraft.world.phys.Vec3 destination = this.target.position().add(relative); -+ this.creeper.getNavigation().moveTo(destination.x, destination.y, destination.z, 1); -+ } -+ // Purpur end - } - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ad8f733ded15d6db3adabccd35bd035f87b2777c..0e7666ddae8e0dc76047fb2f0661a8e63bf2cef2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1361,6 +1361,7 @@ public class PurpurWorldConfig { - public boolean creeperHealthRadius = false; - public boolean creeperAlwaysDropExp = false; - public double creeperHeadVisibilityPercent = 0.5D; -+ public boolean creeperEncircleTarget = false; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -1379,6 +1380,7 @@ public class PurpurWorldConfig { - creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius); - creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp); - creeperHeadVisibilityPercent = getDouble("mobs.creeper.head-visibility-percent", creeperHeadVisibilityPercent); -+ creeperEncircleTarget = getBoolean("mobs.creeper.encircle-target", creeperEncircleTarget); - } - - public boolean dolphinRidable = false; diff --git a/patches/server/0288-Fire-Immunity-API.patch b/patches/server/0288-Fire-Immunity-API.patch deleted file mode 100644 index b213495c8..000000000 --- a/patches/server/0288-Fire-Immunity-API.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Racci <90304606+DaRacci@users.noreply.github.com> -Date: Fri, 4 Feb 2022 16:10:21 +1100 -Subject: [PATCH] Fire Immunity API - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e634f9b7998979e00e4de4899214863e32f1a776..606b29a752176288234ce0082a9b58b79007e4e1 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -418,6 +418,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - public boolean freezeLocked = false; // Paper - Freeze Tick Lock API - public boolean collidingWithWorldBorder; // Paper - public boolean fixedPose = false; // Paper -+ public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API - - public void setOrigin(@javax.annotation.Nonnull Location location) { - this.origin = location.toVector(); -@@ -1822,7 +1823,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public boolean fireImmune() { -- return this.getType().fireImmune(); -+ return this.immuneToFire != null ? immuneToFire : this.getType().fireImmune(); // Purpur - add fire immune API - } - - public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) { -@@ -2492,6 +2493,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - nbt.putBoolean("Paper.FreezeLock", true); - } - // Paper end -+ // Purpur start -+ if (immuneToFire != null) { -+ nbt.putBoolean("Purpur.FireImmune", immuneToFire); -+ } -+ // Purpur end - return nbt; - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); -@@ -2660,6 +2666,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - freezeLocked = nbt.getBoolean("Paper.FreezeLock"); - } - // Paper end -+ // Purpur start -+ if (nbt.contains("Purpur.FireImmune")) { -+ immuneToFire = nbt.getBoolean("Purpur.FireImmune"); -+ } -+ // Purpur end - - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index feeca8d318fa0b7b22e728baa3bf3269d42bf0a6..8973c8a3bad120e55269bf1b7b810284ad0fe14c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -224,6 +224,16 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - this.entityType = (type != null) ? type : EntityType.UNKNOWN; - } - -+ @Override -+ public boolean isImmuneToFire() { -+ return getHandle().fireImmune(); -+ } -+ -+ @Override -+ public void setImmuneToFire(Boolean fireImmune) { -+ getHandle().immuneToFire = fireImmune; -+ } -+ - @Override - public boolean isInDaylight() { - return getHandle().isSunBurnTick(); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -index 8fcee0e426cd598ddfd7e12df4382d57d2016780..4ffb4046b63cbc140c76721f51c9a7a09e81844d 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -@@ -176,9 +176,14 @@ public class CraftItem extends CraftEntity implements Item { - return item.immuneToExplosion; - } - -+ @Override -+ public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) { -+ item.immuneToFire = (immuneToFire != null && immuneToFire); -+ } -+ - @Override - public void setImmuneToFire(boolean immuneToFire) { -- item.immuneToFire = immuneToFire; -+ this.setImmuneToFire((Boolean) immuneToFire); - } - - @Override diff --git a/patches/server/0289-Add-option-to-teleport-to-spawn-on-nether-ceiling-da.patch b/patches/server/0289-Add-option-to-teleport-to-spawn-on-nether-ceiling-da.patch deleted file mode 100644 index 2a8459580..000000000 --- a/patches/server/0289-Add-option-to-teleport-to-spawn-on-nether-ceiling-da.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sun, 13 Nov 2022 05:05:34 -0600 -Subject: [PATCH] Add option to teleport to spawn on nether ceiling damage - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 606b29a752176288234ce0082a9b58b79007e4e1..cf0d8da4c4b5f4aa4e4ef15897ca252a2b52ec8d 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -939,6 +939,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v) - && (!(this instanceof Player player) || !player.getAbilities().invulnerable))) { - // Paper end -+ if (this.level().purpurConfig.teleportOnNetherCeilingDamage && this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this instanceof ServerPlayer player) player.teleport(io.papermc.paper.util.MCUtil.toLocation(this.level, this.level.getSharedSpawnPos())); else // Purpur - this.onBelowWorld(); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0e7666ddae8e0dc76047fb2f0661a8e63bf2cef2..059865f4aed093b0a6ee27243305462a31458ef0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -419,6 +419,7 @@ public class PurpurWorldConfig { - public String playerDeathExpDropEquation = "expLevel * 7"; - public int playerDeathExpDropMax = 100; - public boolean teleportIfOutsideBorder = false; -+ public boolean teleportOnNetherCeilingDamage = false; - public boolean totemOfUndyingWorksInInventory = false; - public boolean playerFixStuckPortal = false; - public boolean creativeOnePunch = false; -@@ -450,6 +451,7 @@ public class PurpurWorldConfig { - playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation); - playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); - teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); -+ teleportOnNetherCeilingDamage = getBoolean("gameplay-mechanics.player.teleport-on-nether-ceiling-damage", teleportOnNetherCeilingDamage); - totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); - playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); - creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); diff --git a/patches/server/0290-Added-got-ram-event.patch b/patches/server/0290-Added-got-ram-event.patch deleted file mode 100644 index bd0d587a1..000000000 --- a/patches/server/0290-Added-got-ram-event.patch +++ /dev/null @@ -1,18 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Sat, 29 Oct 2022 00:06:41 +0200 -Subject: [PATCH] Added got ram event - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 31cfc2c8c5188a266c9e9993e5cf322e000df397..b04af8b72357b455597d80592c8b8b0c9da744cd 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -423,6 +423,7 @@ public class Goat extends Animal { - - // Paper start - Goat ram API - public void ram(net.minecraft.world.entity.LivingEntity entity) { -+ if(!new org.purpurmc.purpur.event.entity.GoatRamEntityEvent((org.bukkit.entity.Goat) getBukkitEntity(), (org.bukkit.entity.LivingEntity) entity.getBukkitLivingEntity()).callEvent()) return; // Purpur - Brain brain = this.getBrain(); - brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position()); - brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS); diff --git a/patches/server/0291-Log-skipped-entity-s-position.patch b/patches/server/0291-Log-skipped-entity-s-position.patch deleted file mode 100644 index cef4c4549..000000000 --- a/patches/server/0291-Log-skipped-entity-s-position.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Thu, 24 Nov 2022 11:00:37 -0600 -Subject: [PATCH] Log skipped entity's position - - -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index e413aa4650297ce2109beb6319f52fb476e291fe..a8ffa403ac48041e17aa4ce057be0539e5468fd4 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -612,6 +612,12 @@ public class EntityType implements FeatureElement, EntityTypeT - entity.load(nbt); - }, () -> { - EntityType.LOGGER.warn("Skipping Entity with id {}", nbt.getString("id")); -+ // Purpur start - log skipped entity's position -+ try { -+ ListTag pos = nbt.getList("Pos", 6); -+ EntityType.LOGGER.warn("Location: {} {},{},{}", world.getWorld().getName(), pos.getDouble(0), pos.getDouble(1), pos.getDouble(2)); -+ } catch (Throwable ignore) {} -+ // Purpur end - }); - } - diff --git a/patches/server/0292-End-Crystal-Cramming.patch b/patches/server/0292-End-Crystal-Cramming.patch deleted file mode 100644 index 41657c6c8..000000000 --- a/patches/server/0292-End-Crystal-Cramming.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Thu, 15 Dec 2022 11:42:15 -0600 -Subject: [PATCH] End Crystal Cramming - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -index 564d17bc460e2a04947ff9676fbf4c8b1569659c..440add62fcfa62d483409e1aecfc91591ba476c3 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -@@ -99,6 +99,7 @@ public class EndCrystal extends Entity { - } - } - // Paper end -+ if (this.level().purpurConfig.endCrystalCramming > 0 && this.level().getEntitiesOfClass(EndCrystal.class, getBoundingBox()).size() > this.level().purpurConfig.endCrystalCramming) this.hurt(this.damageSources().cramming(), 6.0F); // Purpur - } - - // Purpur start -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 059865f4aed093b0a6ee27243305462a31458ef0..fa0b64f60e0e90147dd924272547695af56790a3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -881,6 +881,7 @@ public class PurpurWorldConfig { - public double basedEndCrystalExplosionPower = 6.0D; - public boolean basedEndCrystalExplosionFire = false; - public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; -+ public int endCrystalCramming = 0; - private void endCrystalSettings() { - if (PurpurConfig.version < 31) { - if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) { -@@ -908,6 +909,7 @@ public class PurpurWorldConfig { - log(Level.SEVERE, "Unknown value for `blocks.end-crystal.base.explosion-effect`! Using default of `BLOCK`"); - basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; - } -+ endCrystalCramming = getInt("blocks.end-crystal.cramming-amount", endCrystalCramming); - } - - public boolean farmlandBypassMobGriefing = false; diff --git a/patches/server/0293-Option-to-allow-beacon-effects-when-covered-by-tinte.patch b/patches/server/0293-Option-to-allow-beacon-effects-when-covered-by-tinte.patch deleted file mode 100644 index 2f8140d9a..000000000 --- a/patches/server/0293-Option-to-allow-beacon-effects-when-covered-by-tinte.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: 12emin34 -Date: Mon, 26 Dec 2022 19:10:43 +0100 -Subject: [PATCH] Option to allow beacon effects when covered by tinted glass - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -index 12578b377b6e939971fb2dcba08637df60643e37..2e6220c5a6c871401ce9734adfab710dcf86ad44 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -@@ -165,6 +165,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name - int j = pos.getY(); - int k = pos.getZ(); - BlockPos blockposition1; -+ boolean isTintedGlass = false; - - if (blockEntity.lastCheckY < j) { - blockposition1 = pos; -@@ -198,6 +199,9 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name - } - } - } else { -+ if (world.purpurConfig.beaconAllowEffectsWithTintedGlass && block.equals(Blocks.TINTED_GLASS)) { -+ isTintedGlass = true; -+ } - if (tileentitybeacon_beaconcolortracker == null || iblockdata1.getLightBlock(world, blockposition1) >= 15 && !iblockdata1.is(Blocks.BEDROCK)) { - blockEntity.checkingBeamSections.clear(); - blockEntity.lastCheckY = l; -@@ -217,7 +221,7 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name - blockEntity.levels = BeaconBlockEntity.updateBase(world, i, j, k); - } - -- if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) { -+ if (blockEntity.levels > 0 && (!blockEntity.beamSections.isEmpty() || (world.purpurConfig.beaconAllowEffectsWithTintedGlass && isTintedGlass))) { - BeaconBlockEntity.applyEffects(world, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - BeaconBlockEntity.playSound(world, pos, SoundEvents.BEACON_AMBIENT); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fa0b64f60e0e90147dd924272547695af56790a3..785e4f56c82beb993fc980b9039f184eec98a409 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -780,11 +780,13 @@ public class PurpurWorldConfig { - public int beaconLevelTwo = 30; - public int beaconLevelThree = 40; - public int beaconLevelFour = 50; -+ public boolean beaconAllowEffectsWithTintedGlass = false; - private void beaconSettings() { - beaconLevelOne = getInt("blocks.beacon.effect-range.level-1", beaconLevelOne); - beaconLevelTwo = getInt("blocks.beacon.effect-range.level-2", beaconLevelTwo); - beaconLevelThree = getInt("blocks.beacon.effect-range.level-3", beaconLevelThree); - beaconLevelFour = getInt("blocks.beacon.effect-range.level-4", beaconLevelFour); -+ beaconAllowEffectsWithTintedGlass = getBoolean("blocks.beacon.allow-effects-with-tinted-glass", beaconAllowEffectsWithTintedGlass); - } - - public boolean bedExplode = true; diff --git a/patches/server/0294-Add-attribute-clamping-and-armor-limit-config.patch b/patches/server/0294-Add-attribute-clamping-and-armor-limit-config.patch deleted file mode 100644 index c68108985..000000000 --- a/patches/server/0294-Add-attribute-clamping-and-armor-limit-config.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Thu, 27 Oct 2022 23:12:45 -0400 -Subject: [PATCH] Add attribute clamping and armor limit config - - -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatRules.java b/src/main/java/net/minecraft/world/damagesource/CombatRules.java -index ccbfcef3e83b1bef364447657bfd08a92d615cf6..aa2331c6df4e79d4bb0add071a0b11d2a3a08b88 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatRules.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatRules.java -@@ -11,12 +11,12 @@ public class CombatRules { - - public static float getDamageAfterAbsorb(float damage, float armor, float armorToughness) { - float f = 2.0F + armorToughness / 4.0F; -- float g = Mth.clamp(armor - damage / f, armor * 0.2F, 20.0F); -+ float g = Mth.clamp(armor - damage / f, armor * 0.2F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur - return damage * (1.0F - g / 25.0F); - } - - public static float getDamageAfterMagicAbsorb(float damageDealt, float protection) { -- float f = Mth.clamp(protection, 0.0F, 20.0F); -+ float f = Mth.clamp(protection, 0.0F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur - return damageDealt * (1.0F - f / 25.0F); - } - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java -index f0703302e7dbbda88de8c648d20d87c55ed9b1e0..a913ebabaa5f443afa987b972355a8f8d1723c78 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/RangedAttribute.java -@@ -29,6 +29,7 @@ public class RangedAttribute extends Attribute { - - @Override - public double sanitizeValue(double value) { -+ if (!org.purpurmc.purpur.PurpurConfig.clampAttributes) return Double.isNaN(value) ? this.minValue : value; // Purpur - return Double.isNaN(value) ? this.minValue : Mth.clamp(value, this.minValue, this.maxValue); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index cf0a52a037ac255c3f9e36dbff3cedda0fd8f49e..f10679ef2cbd73397b64eb69516c8a46e4d35a4e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -577,6 +577,16 @@ public class PurpurConfig { - fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer); - } - -+ public static boolean clampAttributes = true; -+ private static void clampAttributes() { -+ clampAttributes = getBoolean("settings.clamp-attributes", clampAttributes); -+ } -+ -+ public static boolean limitArmor = true; -+ private static void limitArmor() { -+ limitArmor = getBoolean("settings.limit-armor", limitArmor); -+ } -+ - private static void blastResistanceSettings() { - getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> { - Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(blockId)); diff --git a/patches/server/0295-Config-to-remove-explosion-radius-clamp.patch b/patches/server/0295-Config-to-remove-explosion-radius-clamp.patch deleted file mode 100644 index 45c8be7ba..000000000 --- a/patches/server/0295-Config-to-remove-explosion-radius-clamp.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Nico314159 -Date: Mon, 9 Jan 2023 19:45:55 -0500 -Subject: [PATCH] Config to remove explosion radius clamp - - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index a907930813b0a3176354b4b307576d9ff3b0c5ca..ec595b74a6376adb65840035cdaa7502c9a3fe50 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -109,6 +109,7 @@ public class EnderDragon extends Mob implements Enemy { - - public EnderDragon(EntityType entitytypes, Level world) { - super(EntityType.ENDER_DRAGON, world); -+ this.explosionSource = new Explosion(world, this, null, null, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, Explosion.BlockInteraction.DESTROY); // Purpur - moved instantiation from field - this.fightOrigin = BlockPos.ZERO; - this.growlTime = 100; - this.nodes = new Node[24]; -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index e35ebb5b923c2f63e37e5dae006bc4a030e49bb7..dcc3b333f3647631e2fb695d0854036d3bc009be 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -86,7 +86,7 @@ public class Explosion { - this.hitPlayers = Maps.newHashMap(); - this.level = world; - this.source = entity; -- this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values -+ this.radius = (float) (world == null || world.purpurConfig.explosionClampRadius ? Math.max(power, 0.0) : power); // CraftBukkit - clamp bad values // Purpur - this.x = x; - this.y = y; - this.z = z; -@@ -137,7 +137,7 @@ public class Explosion { - - public void explode() { - // CraftBukkit start -- if (this.radius < 0.1F) { -+ if ((this.level == null || this.level.purpurConfig.explosionClampRadius) && this.radius < 0.1F) { // Purpur - return; - } - // CraftBukkit end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 785e4f56c82beb993fc980b9039f184eec98a409..2bafe37662ee22dc9f24cf3d5dd198416b495ee7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -213,6 +213,11 @@ public class PurpurWorldConfig { - entitySharedRandom = getBoolean("settings.entity.shared-random", entitySharedRandom); - } - -+ public boolean explosionClampRadius = true; -+ private void explosionSettings() { -+ explosionClampRadius = getBoolean("gameplay-mechanics.clamp-explosion-radius", explosionClampRadius); -+ } -+ - public boolean infinityWorksWithoutArrows = false; - public boolean infinityWorksWithNormalArrows = true; - public boolean infinityWorksWithSpectralArrows = false; diff --git a/patches/server/0296-bonemealable-sugarcane-cactus-and-netherwart.patch b/patches/server/0296-bonemealable-sugarcane-cactus-and-netherwart.patch deleted file mode 100644 index f3b894b65..000000000 --- a/patches/server/0296-bonemealable-sugarcane-cactus-and-netherwart.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Thu, 9 Feb 2023 00:28:03 -0800 -Subject: [PATCH] bonemealable sugarcane, cactus, and netherwart - - -diff --git a/src/main/java/net/minecraft/world/level/block/CactusBlock.java b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -index c2ca3432a47124d02e1aaf8ffb621f9a2c7d7a62..0d5f87d24231f6d2b8639825bcd62dd2f8791c8e 100644 ---- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -@@ -22,7 +22,7 @@ import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit - --public class CactusBlock extends Block { -+public class CactusBlock extends Block implements BonemealableBlock { // Purpur - - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; - public static final int MAX_AGE = 15; -@@ -129,4 +129,34 @@ public class CactusBlock extends Block { - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { - return false; - } -+ -+ // Purpur start -+ @Override -+ public boolean isValidBonemealTarget(LevelReader world, BlockPos pos, BlockState state, boolean isClient) { -+ if (!((Level) world).purpurConfig.cactusAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false; -+ -+ int cactusHeight = 0; -+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) { -+ cactusHeight++; -+ } -+ -+ return cactusHeight < ((Level) world).paperConfig().maxGrowthHeight.cactus; -+ } -+ -+ @Override -+ public boolean isBonemealSuccess(Level world, RandomSource random, BlockPos pos, BlockState state) { -+ return true; -+ } -+ -+ @Override -+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { -+ int cactusHeight = 0; -+ while (world.getBlockState(pos.below(cactusHeight)).is(this)) { -+ cactusHeight++; -+ } -+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.cactus - cactusHeight; i++) { -+ world.setBlockAndUpdate(pos.above(i), state.setValue(CactusBlock.AGE, 0)); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -index 74fedd3e401c6d58c03c0579f4b919114404fd78..4310d62c45c776eea81987809309da08c242c7b0 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -@@ -14,7 +14,7 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty; - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class NetherWartBlock extends BushBlock { -+public class NetherWartBlock extends BushBlock implements BonemealableBlock { // Purpur - - public static final int MAX_AGE = 3; - public static final IntegerProperty AGE = BlockStateProperties.AGE_3; -@@ -70,5 +70,22 @@ public class NetherWartBlock extends BushBlock { - super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops); - } - } -+ -+ @Override -+ public boolean isValidBonemealTarget(net.minecraft.world.level.LevelReader world, BlockPos pos, BlockState state, boolean isClient) { -+ return ((net.minecraft.world.level.Level) world).purpurConfig.netherWartAffectedByBonemeal && state.getValue(NetherWartBlock.AGE) < 3; -+ } -+ -+ @Override -+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) { -+ return true; -+ } -+ -+ @Override -+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { -+ int i = Math.min(3, state.getValue(NetherWartBlock.AGE) + 1); -+ state = state.setValue(NetherWartBlock.AGE, i); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, pos, state, 2); // CraftBukkit -+ } - // Purpur end - } -diff --git a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java -index c3f500580d257e1397f2eb7c47b063a6fe6bb405..0d5c6bdfd4aeda472804b493315bf21ac3067e9d 100644 ---- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java -@@ -19,7 +19,7 @@ import net.minecraft.world.level.material.FluidState; - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class SugarCaneBlock extends Block { -+public class SugarCaneBlock extends Block implements BonemealableBlock { // Purpur - - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; - protected static final float AABB_OFFSET = 6.0F; -@@ -106,4 +106,34 @@ public class SugarCaneBlock extends Block { - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(SugarCaneBlock.AGE); - } -+ -+ // Purpur start -+ @Override -+ public boolean isValidBonemealTarget(LevelReader world, BlockPos pos, BlockState state, boolean isClient) { -+ if (!((net.minecraft.world.level.Level) world).purpurConfig.sugarCanAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false; -+ -+ int reedHeight = 0; -+ while (world.getBlockState(pos.below(reedHeight)).is(this)) { -+ reedHeight++; -+ } -+ -+ return reedHeight < ((net.minecraft.world.level.Level) world).paperConfig().maxGrowthHeight.reeds; -+ } -+ -+ @Override -+ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) { -+ return true; -+ } -+ -+ @Override -+ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { -+ int reedHeight = 0; -+ while (world.getBlockState(pos.below(reedHeight)).is(this)) { -+ reedHeight++; -+ } -+ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.reeds - reedHeight; i++) { -+ world.setBlockAndUpdate(pos.above(i), state.setValue(SugarCaneBlock.AGE, 0)); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2bafe37662ee22dc9f24cf3d5dd198416b495ee7..9365469584fd647dbc52176efb8c282c21dd28d6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -834,8 +834,20 @@ public class PurpurWorldConfig { - } - - public boolean cactusBreaksFromSolidNeighbors = true; -+ public boolean cactusAffectedByBonemeal = false; - private void cactusSettings() { - cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors); -+ cactusAffectedByBonemeal = getBoolean("blocks.cactus.affected-by-bonemeal", cactusAffectedByBonemeal); -+ } -+ -+ public boolean sugarCanAffectedByBonemeal = false; -+ private void sugarCaneSettings() { -+ sugarCanAffectedByBonemeal = getBoolean("blocks.sugar_cane.affected-by-bonemeal", sugarCanAffectedByBonemeal); -+ } -+ -+ public boolean netherWartAffectedByBonemeal = false; -+ private void netherWartSettings() { -+ netherWartAffectedByBonemeal = getBoolean("blocks.nether_wart.affected-by-bonemeal", netherWartAffectedByBonemeal); - } - - public boolean campFireLitWhenPlaced = true; diff --git a/patches/server/0297-Add-PreExplodeEvents.patch b/patches/server/0297-Add-PreExplodeEvents.patch deleted file mode 100644 index ccc7f7b93..000000000 --- a/patches/server/0297-Add-PreExplodeEvents.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: SageSphinx63920 -Date: Mon, 26 Dec 2022 23:42:37 +0100 -Subject: [PATCH] Add PreExplodeEvents - - -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index dcc3b333f3647631e2fb695d0854036d3bc009be..4932374ab9a3d8582fb0ef024d817ad896dd23c4 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -141,6 +141,23 @@ public class Explosion { - return; - } - // CraftBukkit end -+ -+ // Purpur start - add PreExplodeEvents -+ if(this.source != null){ -+ Location location = new Location(this.level.getWorld(), this.x, this.y, this.z); -+ if(!new org.purpurmc.purpur.event.entity.PreEntityExplodeEvent(this.source.getBukkitEntity(), location, this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F).callEvent()) { -+ this.wasCanceled = true; -+ return; -+ } -+ }else { -+ Location location = new Location(this.level.getWorld(), this.x, this.y, this.z); -+ if(!new org.purpurmc.purpur.event.PreBlockExplodeEvent(location.getBlock(), this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, this.damageSource.explodedBlockState).callEvent()) { -+ this.wasCanceled = true; -+ return; -+ } -+ } -+ //Purpur end -+ - this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z)); - Set set = Sets.newHashSet(); - boolean flag = true; diff --git a/patches/server/0298-Improve-output-of-plugins-command.patch b/patches/server/0298-Improve-output-of-plugins-command.patch deleted file mode 100644 index acee37c0a..000000000 --- a/patches/server/0298-Improve-output-of-plugins-command.patch +++ /dev/null @@ -1,131 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Parker Hawke -Date: Sat, 27 Jun 2020 18:43:37 -0400 -Subject: [PATCH] Improve output of plugins command - -Co-authored-by: Oharass -Co-authored-by: granny - -diff --git a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java -index f0fce4113fb07c64adbec029d177c236cbdcbae8..e94224ed280247ee69dfdff8dc960f2b8729be33 100644 ---- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java -+++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java -@@ -78,10 +78,10 @@ public class PaperPluginsCommand extends BukkitCommand { - this.setAliases(Arrays.asList("pl")); - } - -- private static List formatProviders(TreeMap> plugins) { -+ private static List formatProviders(TreeMap> plugins, @NotNull CommandSender sender) { // Purpur - List components = new ArrayList<>(plugins.size()); - for (PluginProvider entry : plugins.values()) { -- components.add(formatProvider(entry)); -+ components.add(formatProvider(entry, sender)); // Purpur - } - - boolean isFirst = true; -@@ -109,7 +109,7 @@ public class PaperPluginsCommand extends BukkitCommand { - return formattedSublists; - } - -- private static Component formatProvider(PluginProvider provider) { -+ private static Component formatProvider(PluginProvider provider, @NotNull CommandSender sender) { // Purpur - TextComponent.Builder builder = Component.text(); - if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) { - builder.append(LEGACY_PLUGIN_STAR); -@@ -117,12 +117,64 @@ public class PaperPluginsCommand extends BukkitCommand { - - String name = provider.getMeta().getName(); - Component pluginName = Component.text(name, fromStatus(provider)) -- .clickEvent(ClickEvent.runCommand("/version " + name)); -+ // Purpur start -+ .clickEvent(ClickEvent.suggestCommand("/version " + name)); -+ -+ if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) { -+ // Event components -+ String description = provider.getMeta().getDescription(); -+ TextComponent.Builder hover = Component.text(); -+ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(provider.getMeta().getVersion(), NamedTextColor.GREEN)); -+ -+ if (description != null) { -+ hover.append(Component.newline()) -+ .append(Component.text("Description: ", NamedTextColor.WHITE)) -+ .append(Component.text(description, NamedTextColor.GREEN)); -+ } -+ -+ if (provider.getMeta().getWebsite() != null) { -+ hover.append(Component.newline()) -+ .append(Component.text("Website: ", NamedTextColor.WHITE)) -+ .append(Component.text(provider.getMeta().getWebsite(), NamedTextColor.GREEN)); -+ } -+ -+ if (!provider.getMeta().getAuthors().isEmpty()) { -+ hover.append(Component.newline()); -+ if (provider.getMeta().getAuthors().size() == 1) { -+ hover.append(Component.text("Author: ")); -+ } else { -+ hover.append(Component.text("Authors: ")); -+ } -+ -+ hover.append(getAuthors(provider.getMeta())); -+ } -+ -+ pluginName.hoverEvent(hover.build()); -+ } - - builder.append(pluginName); -+ // Purpur end -+ -+ return builder.build(); -+ } -+ -+ // Purpur start -+ @NotNull -+ private static TextComponent getAuthors(@NotNull final PluginMeta pluginMeta) { -+ TextComponent.Builder builder = Component.text(); -+ List authors = pluginMeta.getAuthors(); -+ -+ for (int i = 0; i < authors.size(); i++) { -+ if (i > 0) { -+ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE)); -+ } -+ -+ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN)); -+ } - - return builder.build(); - } -+ // Purpur end - - private static Component asPlainComponents(String strings) { - net.kyori.adventure.text.TextComponent.Builder builder = Component.text(); -@@ -182,24 +234,24 @@ public class PaperPluginsCommand extends BukkitCommand { - } - } - -- Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE); -+ //Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE); - //.append(INFO_ICON_START.hoverEvent(SERVER_PLUGIN_INFO)); TODO: Add docs - -- sender.sendMessage(infoMessage); -+ //sender.sendMessage(infoMessage); // Purpur - - if (!paperPlugins.isEmpty()) { -- sender.sendMessage(PAPER_HEADER); -+ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur - } - -- for (Component component : formatProviders(paperPlugins)) { -+ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur - sender.sendMessage(component); - } - - if (!spigotPlugins.isEmpty()) { -- sender.sendMessage(BUKKIT_HEADER); -+ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur - } - -- for (Component component : formatProviders(spigotPlugins)) { -+ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur - sender.sendMessage(component); - } - diff --git a/patches/server/0300-Make-GUI-Great-Again.patch b/patches/server/0300-Make-GUI-Great-Again.patch deleted file mode 100644 index a7001817f..000000000 --- a/patches/server/0300-Make-GUI-Great-Again.patch +++ /dev/null @@ -1,412 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 16 Jan 2020 14:59:16 -0600 -Subject: [PATCH] Make GUI Great Again - - -diff --git a/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java -new file mode 100644 -index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8ace07630c ---- /dev/null -+++ b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java -@@ -0,0 +1,85 @@ -+package org.purpurmc.purpur.gui.util; -+ -+import org.apache.logging.log4j.Level; -+import org.apache.logging.log4j.core.LogEvent; -+import org.apache.logging.log4j.core.config.Configuration; -+import org.apache.logging.log4j.core.config.plugins.Plugin; -+import org.apache.logging.log4j.core.layout.PatternLayout; -+import org.apache.logging.log4j.core.pattern.ConverterKeys; -+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; -+import org.apache.logging.log4j.core.pattern.PatternConverter; -+import org.apache.logging.log4j.core.pattern.PatternFormatter; -+import org.apache.logging.log4j.core.pattern.PatternParser; -+import org.apache.logging.log4j.util.PerformanceSensitive; -+ -+import java.util.List; -+ -+@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY) -+@ConverterKeys({"highlightGUIError"}) -+@PerformanceSensitive("allocation") -+public final class HighlightErrorConverter extends LogEventPatternConverter { -+ private static final String ERROR = "\u00A74\u00A7l"; // Bold Red -+ private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow -+ -+ private final List formatters; -+ -+ private HighlightErrorConverter(List formatters) { -+ super("highlightGUIError", null); -+ this.formatters = formatters; -+ } -+ -+ @Override -+ public void format(LogEvent event, StringBuilder toAppendTo) { -+ Level level = event.getLevel(); -+ if (level.isMoreSpecificThan(Level.ERROR)) { -+ format(ERROR, event, toAppendTo); -+ return; -+ } else if (level.isMoreSpecificThan(Level.WARN)) { -+ format(WARN, event, toAppendTo); -+ return; -+ } -+ for (PatternFormatter formatter : formatters) { -+ formatter.format(event, toAppendTo); -+ } -+ } -+ -+ private void format(String style, LogEvent event, StringBuilder toAppendTo) { -+ int start = toAppendTo.length(); -+ toAppendTo.append(style); -+ int end = toAppendTo.length(); -+ -+ for (PatternFormatter formatter : formatters) { -+ formatter.format(event, toAppendTo); -+ } -+ -+ if (toAppendTo.length() == end) { -+ toAppendTo.setLength(start); -+ } -+ } -+ -+ @Override -+ public boolean handlesThrowable() { -+ for (final PatternFormatter formatter : formatters) { -+ if (formatter.handlesThrowable()) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ public static HighlightErrorConverter newInstance(Configuration config, String[] options) { -+ if (options.length != 1) { -+ LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length); -+ return null; -+ } -+ -+ if (options[0] == null) { -+ LOGGER.error("No pattern supplied on highlightGUIError"); -+ return null; -+ } -+ -+ PatternParser parser = PatternLayout.createPatternParser(config); -+ List formatters = parser.parse(options[0]); -+ return new HighlightErrorConverter(formatters); -+ } -+} -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 41db72915baa4c02b11a701dfdde09b66ba72476..12124d6fbc0406bb62bd95a0f7bab68afa43377c 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -99,6 +99,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - return; - } - // Paper start - Use TerminalConsoleAppender -+ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - has no GUI or has console (did not double-click) - new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); - /* - jline.console.ConsoleReader bufferedreader = reader; -diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -index dd9f611efc95f7d06fd3011fedd5d0317b1d0a85..be7b3fe2dc84493dcde9e185717b0b7c7c2e9822 100644 ---- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -@@ -43,6 +43,11 @@ public class MinecraftServerGui extends JComponent { - private Thread logAppenderThread; - private final Collection finalizers = Lists.newArrayList(); - final AtomicBoolean isClosing = new AtomicBoolean(); -+ // Purpur start -+ private final CommandHistory history = new CommandHistory(); -+ private String currentCommand = ""; -+ private int historyIndex = 0; -+ // Purpur end - - public static MinecraftServerGui showFrameFor(final DedicatedServer server) { - try { -@@ -51,7 +56,7 @@ public class MinecraftServerGui extends JComponent { - ; - } - -- final JFrame jframe = new JFrame("Minecraft server"); -+ final JFrame jframe = new JFrame("Purpur Minecraft server"); // Purpur - final MinecraftServerGui servergui = new MinecraftServerGui(server); - - jframe.setDefaultCloseOperation(2); -@@ -59,7 +64,7 @@ public class MinecraftServerGui extends JComponent { - jframe.pack(); - jframe.setLocationRelativeTo((Component) null); - jframe.setVisible(true); -- jframe.setName("Minecraft server"); // Paper -+ jframe.setName("Purpur Minecraft server"); // Paper // Purpur - - // Paper start - Add logo as frame image - try { -@@ -71,7 +76,7 @@ public class MinecraftServerGui extends JComponent { - jframe.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent windowevent) { - if (!servergui.isClosing.getAndSet(true)) { -- jframe.setTitle("Minecraft server - shutting down!"); -+ jframe.setTitle("Purpur Minecraft server - shutting down!"); // Purpur - server.halt(true); - servergui.runFinalizers(); - } -@@ -125,7 +130,7 @@ public class MinecraftServerGui extends JComponent { - - private JComponent buildChatPanel() { - JPanel jpanel = new JPanel(new BorderLayout()); -- JTextArea jtextarea = new JTextArea(); -+ org.purpurmc.purpur.gui.JColorTextPane jtextarea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur - JScrollPane jscrollpane = new JScrollPane(jtextarea, 22, 30); - - jtextarea.setEditable(false); -@@ -137,10 +142,43 @@ public class MinecraftServerGui extends JComponent { - - if (!s.isEmpty()) { - this.server.handleConsoleInput(s, this.server.createCommandSourceStack()); -+ // Purpur start -+ history.add(s); -+ historyIndex = -1; -+ // Purpur end - } - - jtextfield.setText(""); - }); -+ // Purpur start -+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up"); -+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down"); -+ jtextfield.getActionMap().put("up", new javax.swing.AbstractAction() { -+ @Override -+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) { -+ if (historyIndex < 0) { -+ currentCommand = jtextfield.getText(); -+ } -+ if (historyIndex < history.size() - 1) { -+ jtextfield.setText(history.get(historyIndex)); -+ } -+ } -+ }); -+ jtextfield.getActionMap().put("down", new javax.swing.AbstractAction() { -+ @Override -+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) { -+ if (historyIndex >= 0) { -+ if (historyIndex == 0) { -+ --historyIndex; -+ jtextfield.setText(currentCommand); -+ } else { -+ --historyIndex; -+ jtextfield.setText(history.get(historyIndex)); -+ } -+ } -+ } -+ }); -+ // Purpur end - jtextarea.addFocusListener(new FocusAdapter() { - public void focusGained(FocusEvent focusevent) {} - }); -@@ -176,7 +214,7 @@ public class MinecraftServerGui extends JComponent { - } - - private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper -- public void print(JTextArea textArea, JScrollPane scrollPane, String message) { -+ public void print(org.purpurmc.purpur.gui.JColorTextPane textArea, JScrollPane scrollPane, String message) { // Purpur - if (!SwingUtilities.isEventDispatchThread()) { - SwingUtilities.invokeLater(() -> { - this.print(textArea, scrollPane, message); -@@ -190,11 +228,14 @@ public class MinecraftServerGui extends JComponent { - flag = (double) jscrollbar.getValue() + jscrollbar.getSize().getHeight() + (double) (MinecraftServerGui.MONOSPACED.getSize() * 4) > (double) jscrollbar.getMaximum(); - } - -+ /* // Purpur - try { - document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(message).replaceAll(""), (AttributeSet) null); // CraftBukkit - } catch (BadLocationException badlocationexception) { - ; - } -+ */ // Purpur -+ textArea.append(message); // Purpur - - if (flag) { - jscrollbar.setValue(Integer.MAX_VALUE); -@@ -202,4 +243,16 @@ public class MinecraftServerGui extends JComponent { - - } - } -+ -+ // Purpur start -+ public static class CommandHistory extends java.util.LinkedList { -+ @Override -+ public boolean add(String command) { -+ if (size() > 1000) { -+ remove(); -+ } -+ return super.offerFirst(command); -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/gui/GUIColor.java b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0f2e7e0b81620c8581949bd5f0bdb567cd93c17e ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java -@@ -0,0 +1,54 @@ -+package org.purpurmc.purpur.gui; -+ -+import net.md_5.bungee.api.ChatColor; -+ -+import java.awt.Color; -+import java.util.HashMap; -+import java.util.Map; -+ -+public enum GUIColor { -+ BLACK(ChatColor.BLACK, new Color(0x000000)), -+ DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)), -+ DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)), -+ DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)), -+ DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)), -+ DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)), -+ GOLD(ChatColor.GOLD, new Color(0xBB8800)), -+ GRAY(ChatColor.GRAY, new Color(0x888888)), -+ DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)), -+ BLUE(ChatColor.BLUE, new Color(0x5555FF)), -+ GREEN(ChatColor.GREEN, new Color(0x55FF55)), -+ AQUA(ChatColor.AQUA, new Color(0x55DDDD)), -+ RED(ChatColor.RED, new Color(0xFF5555)), -+ LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)), -+ YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)), -+ WHITE(ChatColor.WHITE, new Color(0xBBBBBB)); -+ -+ private final ChatColor chat; -+ private final Color color; -+ -+ private static final Map BY_CHAT = new HashMap<>(); -+ -+ GUIColor(ChatColor chat, Color color) { -+ this.chat = chat; -+ this.color = color; -+ } -+ -+ public Color getColor() { -+ return color; -+ } -+ -+ public String getCode() { -+ return chat.toString(); -+ } -+ -+ public static GUIColor getColor(ChatColor chat) { -+ return BY_CHAT.get(chat); -+ } -+ -+ static { -+ for (GUIColor color : values()) { -+ BY_CHAT.put(color.chat, color); -+ } -+ } -+} -diff --git a/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java -new file mode 100644 -index 0000000000000000000000000000000000000000..33e89b4c00fa8318506b36cbe49fe4e412e0a9a1 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java -@@ -0,0 +1,78 @@ -+package org.purpurmc.purpur.gui; -+ -+import com.google.common.collect.Sets; -+import net.md_5.bungee.api.ChatColor; -+import net.md_5.bungee.api.chat.BaseComponent; -+import net.md_5.bungee.api.chat.TextComponent; -+ -+import javax.swing.JTextPane; -+import javax.swing.Timer; -+import javax.swing.text.AttributeSet; -+import javax.swing.text.BadLocationException; -+import javax.swing.text.SimpleAttributeSet; -+import javax.swing.text.StyleConstants; -+import javax.swing.text.StyleContext; -+import java.util.Set; -+ -+public class JColorTextPane extends JTextPane { -+ private static final GUIColor DEFAULT_COLOR = GUIColor.BLACK; -+ -+ public void append(String msg) { -+ // TODO: update to use adventure instead -+ BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, ChatColor.BLACK); -+ for (BaseComponent component : components) { -+ String text = component.toPlainText(); -+ if (text == null || text.isEmpty()) { -+ continue; -+ } -+ -+ GUIColor guiColor = GUIColor.getColor(component.getColor()); -+ -+ StyleContext context = StyleContext.getDefaultStyleContext(); -+ AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor()); -+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR); -+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic()); -+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined()); -+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough()); -+ //attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly -+ -+ try { -+ int pos = getDocument().getLength(); -+ getDocument().insertString(pos, text, attr); -+ -+ if (component.isObfuscated()) { -+ // dirty hack to blink some text -+ Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground())); -+ BLINKS.add(blink); -+ } -+ } catch (BadLocationException ignore) { -+ } -+ } -+ } -+ -+ private static final Set BLINKS = Sets.newHashSet(); -+ private static boolean SYNC_BLINK; -+ -+ static { -+ new Timer(500, e -> { -+ SYNC_BLINK = !SYNC_BLINK; -+ BLINKS.forEach(Blink::blink); -+ }).start(); -+ } -+ -+ public class Blink { -+ private final int start, length; -+ private final AttributeSet attr1, attr2; -+ -+ private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) { -+ this.start = start; -+ this.length = length; -+ this.attr1 = attr1; -+ this.attr2 = attr2; -+ } -+ -+ private void blink() { -+ getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true); -+ } -+ } -+} -diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml -index 675cd61221e807aadf28322b46c3daa1370241b5..0769f5c4711a3b7f59489e611ed01ad8367e5db1 100644 ---- a/src/main/resources/log4j2.xml -+++ b/src/main/resources/log4j2.xml -@@ -2,7 +2,16 @@ - - - -- -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - diff --git a/patches/server/0301-Stored-Bee-API.patch b/patches/server/0301-Stored-Bee-API.patch deleted file mode 100644 index 26c594b17..000000000 --- a/patches/server/0301-Stored-Bee-API.patch +++ /dev/null @@ -1,271 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: EOT3000 -Date: Sat, 10 Jun 2023 20:27:12 -0400 -Subject: [PATCH] Stored Bee API - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -index 7b82842b97ce795745cf6ee6399f618c55acbbf3..5344393f62af19c3591f54a6ebc40b2e4c3b6226 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -@@ -130,6 +130,36 @@ public class BeehiveBlockEntity extends BlockEntity { - return list; - } - -+ // Purpur start -+ public List releaseBees(BlockState iblockdata, Level level, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) { -+ List list = Lists.newArrayList(); -+ -+ this.stored.removeIf((tileentitybeehive_hivebee) -> { -+ return BeehiveBlockEntity.releaseBee(level, this.worldPosition, iblockdata, tileentitybeehive_hivebee, list, tileentitybeehive_releasestatus, this.savedFlowerPos, force); -+ }); -+ -+ if (!list.isEmpty()) { -+ super.setChanged(); -+ } -+ -+ return list; -+ } -+ -+ public List releaseBee(BlockState iblockdata, Level level, BeeData data, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) { -+ List list = Lists.newArrayList(); -+ -+ BeehiveBlockEntity.releaseBee(level, this.worldPosition, iblockdata, data, list, tileentitybeehive_releasestatus, this.savedFlowerPos, force); -+ -+ if (!list.isEmpty()) { -+ stored.remove(data); -+ -+ super.setChanged(); -+ } -+ -+ return list; -+ } -+ // Purpur end -+ - public void addOccupant(Entity entity, boolean hasNectar) { - this.addOccupantWithPresetTicks(entity, hasNectar, 0); - } -@@ -139,6 +169,12 @@ public class BeehiveBlockEntity extends BlockEntity { - return this.stored.size(); - } - -+ // Purpur start -+ public List getStored() { -+ return stored; -+ } -+ // Purpur end -+ - // Paper start - Add EntityBlockStorage clearEntities - public void clearBees() { - this.stored.clear(); -@@ -425,9 +461,9 @@ public class BeehiveBlockEntity extends BlockEntity { - private BeeReleaseStatus() {} - } - -- private static class BeeData { -+ public static class BeeData { // Purpur - change from private to public - -- final CompoundTag entityData; -+ public final CompoundTag entityData; // Purpur - make public - int ticksInHive; - int exitTickCounter; // Paper - separate counter for checking if bee should exit to reduce exit attempts - final int minOccupationTicks; -diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java -index 380897c010521f368848a3e6986d307cf47ff319..3c5eae31dc229b8c5f15215be9462410bcb41f07 100644 ---- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java -+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java -@@ -16,8 +16,15 @@ import org.bukkit.entity.Bee; - - public class CraftBeehive extends CraftBlockEntityState implements Beehive { - -+ private final List> storage = new ArrayList<>(); // Purpur -+ - public CraftBeehive(World world, BeehiveBlockEntity tileEntity) { - super(world, tileEntity); -+ // Purpur start - load bees to be able to modify them individually -+ for(BeehiveBlockEntity.BeeData data : getSnapshot().getStored()) { -+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this)); -+ } -+ // Purpur end - } - - @Override -@@ -66,25 +73,67 @@ public class CraftBeehive extends CraftBlockEntityState impl - List bees = new ArrayList<>(); - - if (isPlaced()) { -- BeehiveBlockEntity beehive = ((BeehiveBlockEntity) this.getTileEntityFromWorld()); -- for (Entity bee : beehive.releaseBees(this.getHandle(), BeeReleaseStatus.BEE_RELEASED, true)) { -+ // Purpur start - change which releaseBees method is called, and use beehive snapshot -+ BeehiveBlockEntity beehive = ((BeehiveBlockEntity) this.getSnapshot()); -+ for (Entity bee : beehive.releaseBees(this.getHandle(), getWorldHandle().getMinecraftWorld(), BeeReleaseStatus.BEE_RELEASED, true)) { -+ // Purpur end - bees.add((Bee) bee.getBukkitEntity()); - } - } -- -+ storage.clear(); // Purpur - return bees; - } - -+ // Purpur start -+ @Override -+ public Bee releaseEntity(org.purpurmc.purpur.entity.StoredEntity entity) { -+ ensureNoWorldGeneration(); -+ -+ if(!getEntities().contains(entity)) { -+ return null; -+ } -+ -+ if(isPlaced()) { -+ BeehiveBlockEntity beehive = this.getSnapshot(); -+ BeehiveBlockEntity.BeeData data = ((org.purpurmc.purpur.entity.PurpurStoredBee) entity).getHandle(); -+ -+ List list = beehive.releaseBee(getHandle(), getWorldHandle().getMinecraftWorld(), data, BeeReleaseStatus.BEE_RELEASED, true); -+ -+ if (list.size() == 1) { -+ storage.remove(entity); -+ -+ return (Bee) list.get(0).getBukkitEntity(); -+ } -+ } -+ -+ return null; -+ } -+ -+ @Override -+ public List> getEntities() { -+ return new ArrayList<>(storage); -+ } -+ // Purpur end -+ - @Override - public void addEntity(Bee entity) { - Preconditions.checkArgument(entity != null, "Entity must not be null"); - -+ int length = getSnapshot().getStored().size(); // Purpur - getSnapshot().addOccupant(((CraftBee) entity).getHandle(), false); -+ -+ // Purpur start - check if new bee was added, and if yes, add to stored bees -+ List s = getSnapshot().getStored(); -+ if(length < s.size()) { -+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(s.get(s.size() - 1), this)); -+ } -+ // Purpur end - } - // Paper start - Add EntityBlockStorage clearEntities - @Override - public void clearEntities() { - getSnapshot().clearBees(); -+ storage.clear(); // Purpur - } - // Paper end - } -diff --git a/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java b/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8efca1d91188ac4db911a8eb0fa9ea2cc3c48e28 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java -@@ -0,0 +1,102 @@ -+package org.purpurmc.purpur.entity; -+ -+import io.papermc.paper.adventure.PaperAdventure; -+import net.kyori.adventure.text.Component; -+import net.minecraft.nbt.Tag; -+import net.minecraft.world.level.block.entity.BeehiveBlockEntity; -+import org.bukkit.block.EntityBlockStorage; -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; -+import org.bukkit.entity.Bee; -+import org.bukkit.entity.EntityType; -+import org.bukkit.persistence.PersistentDataContainer; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.Locale; -+ -+public class PurpurStoredBee implements StoredEntity { -+ private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); -+ -+ private final EntityBlockStorage blockStorage; -+ private final BeehiveBlockEntity.BeeData handle; -+ private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(PurpurStoredBee.DATA_TYPE_REGISTRY); -+ -+ private Component customName; -+ -+ public PurpurStoredBee(BeehiveBlockEntity.BeeData data, EntityBlockStorage blockStorage) { -+ this.handle = data; -+ this.blockStorage = blockStorage; -+ -+ this.customName = handle.entityData.contains("CustomName", Tag.TAG_STRING) -+ ? PaperAdventure.asAdventure(net.minecraft.network.chat.Component.Serializer.fromJson(handle.entityData.getString("CustomName"))) -+ : null; -+ -+ if(handle.entityData.contains("BukkitValues", Tag.TAG_COMPOUND)) { -+ this.persistentDataContainer.putAll(handle.entityData.getCompound("BukkitValues")); -+ } -+ } -+ -+ public BeehiveBlockEntity.BeeData getHandle() { -+ return handle; -+ } -+ -+ @Override -+ public @Nullable Component customName() { -+ return customName; -+ } -+ -+ @Override -+ public void customName(@Nullable Component customName) { -+ this.customName = customName; -+ } -+ -+ @Override -+ public @Nullable String getCustomName() { -+ return PaperAdventure.asPlain(customName, Locale.US); -+ } -+ -+ @Override -+ public void setCustomName(@Nullable String name) { -+ customName(name != null ? Component.text(name) : null); -+ } -+ -+ @Override -+ public @NotNull PersistentDataContainer getPersistentDataContainer() { -+ return persistentDataContainer; -+ } -+ -+ @Override -+ public boolean hasBeenReleased() { -+ return !blockStorage.getEntities().contains(this); -+ } -+ -+ @Override -+ public @Nullable Bee release() { -+ return blockStorage.releaseEntity(this); -+ } -+ -+ @Override -+ public @Nullable EntityBlockStorage getBlockStorage() { -+ if(hasBeenReleased()) { -+ return null; -+ } -+ -+ return blockStorage; -+ } -+ -+ @Override -+ public @NotNull EntityType getType() { -+ return EntityType.BEE; -+ } -+ -+ @Override -+ public void update() { -+ handle.entityData.put("BukkitValues", this.persistentDataContainer.toTagCompound()); -+ if(customName == null) { -+ handle.entityData.remove("CustomName"); -+ } else { -+ handle.entityData.putString("CustomName", net.minecraft.network.chat.Component.Serializer.toJson(PaperAdventure.asVanilla(customName))); -+ } -+ } -+} diff --git a/patches/server/0302-Shears-can-defuse-TNT.patch b/patches/server/0302-Shears-can-defuse-TNT.patch deleted file mode 100644 index e88cf775c..000000000 --- a/patches/server/0302-Shears-can-defuse-TNT.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MrFishCakes -Date: Sun, 2 Jul 2023 00:50:14 +0100 -Subject: [PATCH] Shears can defuse TNT - -Shears can now defuse TNT. Each world can have a configured chance for the TNT to be defused when right clicking with a set of shears and damage dealt to the shears accordingly. If the TNT is defused then it will drop the TNT item instead and the entity removed from the world no longer existing. All the interaction is handled within the net.minecraft.world.entity.item.PrimedTnt class similar to how it is handled for when sheep are sheared. - -By default the option is disabled to avoid breaking any possible vanilla mechanics. - -diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -index 4ce3e69970dd9eb251d0538a2d233ca30e9e5e47..afc65b8bb7e7f7f70a25f2d869412ed325b658da 100644 ---- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -175,4 +175,29 @@ public class PrimedTnt extends Entity implements TraceableEntity { - return !level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid(); - } - // Paper end -+ // Purpur start - Shears can defuse TNT -+ @Override -+ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { -+ if (!level().isClientSide && level().purpurConfig.shearsCanDefuseTnt) { -+ final net.minecraft.world.item.ItemStack inHand = player.getItemInHand(hand); -+ -+ if (!inHand.is(net.minecraft.world.item.Items.SHEARS) || !player.getBukkitEntity().hasPermission("purpur.tnt.defuse") || -+ level().random.nextFloat() > level().purpurConfig.shearsCanDefuseTntChance) return net.minecraft.world.InteractionResult.PASS; -+ -+ net.minecraft.world.entity.item.ItemEntity tntItem = new net.minecraft.world.entity.item.ItemEntity(level(), getX(), getY(), getZ(), -+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.TNT)); -+ tntItem.setPickUpDelay(10); -+ -+ inHand.hurtAndBreak(1, player, entity -> entity.broadcastBreakEvent(hand)); -+ level().addFreshEntity(tntItem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CUSTOM); -+ -+ this.playSound(net.minecraft.sounds.SoundEvents.SHEEP_SHEAR); -+ -+ this.kill(); -+ return net.minecraft.world.InteractionResult.SUCCESS; -+ } -+ -+ return super.interact(player, hand); -+ } -+ // Purpur end - Shears can defuse TNT - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 76216ce3a481b7430bd5a3d60bb41798216fa692..f9711c31fae1e27158f7323639ce279237516c91 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3223,4 +3223,11 @@ public class PurpurWorldConfig { - cauldronDripstoneWaterFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-water", cauldronDripstoneWaterFillChance); - cauldronDripstoneLavaFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-lava", cauldronDripstoneLavaFillChance); - } -+ -+ public float shearsCanDefuseTntChance = 0.00F; -+ public boolean shearsCanDefuseTnt = false; -+ private void shearsCanDefuseTntSettings() { -+ shearsCanDefuseTntChance = (float) getDouble("gameplay-mechanics.item.shears.defuse-tnt-chance", 0.00D); -+ shearsCanDefuseTnt = shearsCanDefuseTntChance > 0.00F; -+ } - } diff --git a/patches/server/0303-Explorer-Map-API.patch b/patches/server/0303-Explorer-Map-API.patch deleted file mode 100644 index 1e1393e24..000000000 --- a/patches/server/0303-Explorer-Map-API.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Wed, 5 Jul 2023 12:48:15 -0500 -Subject: [PATCH] Explorer Map API - - -diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java -index d3c29e6bf8b3c2dd628809177dac50220a7de415..735486b46581bb3b91c85f57490b560ff490787e 100644 ---- a/src/main/java/net/minecraft/world/item/MapItem.java -+++ b/src/main/java/net/minecraft/world/item/MapItem.java -@@ -243,6 +243,7 @@ public class MapItem extends ComplexItem { - MapItemSavedData worldmap = MapItem.getSavedData(map, world); - - if (worldmap != null) { -+ worldmap.isExplorerMap = true; // Purpur - if (world.dimension() == worldmap.dimension) { - int i = 1 << worldmap.scale; - int j = worldmap.centerX; -diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java -index 50713f03c783c63f93710d986d94af544be0615a..e64921f454c88e2ca9e1396fcb1ca7c179ff71a7 100644 ---- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java -+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java -@@ -66,6 +66,7 @@ public class MapItemSavedData extends SavedData { - private final Map frameMarkers = Maps.newHashMap(); - private int trackedDecorationCount; - private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper -+ public boolean isExplorerMap; // Purpur - - // CraftBukkit start - public final CraftMapView mapView; -diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java -index 9683d7d103af66fffd68c11abc38fb4fd2f99482..18a4de76021ff8b500801ef0121003f8dd62a961 100644 ---- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java -+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java -@@ -46,4 +46,10 @@ public class CraftMapRenderer extends MapRenderer { - } - } - -+ // Purpur - start -+ @Override -+ public boolean isExplorerMap() { -+ return this.worldMap.isExplorerMap; -+ } -+ // Purpur - end - } diff --git a/patches/server/0304-Option-Ocelot-Spawn-Under-Sea-Level.patch b/patches/server/0304-Option-Ocelot-Spawn-Under-Sea-Level.patch deleted file mode 100644 index 801d131e2..000000000 --- a/patches/server/0304-Option-Ocelot-Spawn-Under-Sea-Level.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 21 Jul 2023 11:04:47 -0500 -Subject: [PATCH] Option Ocelot Spawn Under Sea Level - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -index c72d5e7c2a19c5690a8065c95c75f0415358c2a9..45fb438065b1db2a9baa648f4d397472847ed638 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -@@ -293,7 +293,7 @@ public class Ocelot extends Animal { - if (world.isUnobstructed(this) && !world.containsAnyLiquid(this.getBoundingBox())) { - BlockPos blockposition = this.blockPosition(); - -- if (blockposition.getY() < world.getSeaLevel()) { -+ if (!level().purpurConfig.ocelotSpawnUnderSeaLevel && blockposition.getY() < world.getSeaLevel()) { - return false; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f9711c31fae1e27158f7323639ce279237516c91..383473859796d28a86dfd5fa5e407b6b5ea6aeb7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2040,6 +2040,7 @@ public class PurpurWorldConfig { - public int ocelotBreedingTicks = 6000; - public boolean ocelotTakeDamageFromWater = false; - public boolean ocelotAlwaysDropExp = false; -+ public boolean ocelotSpawnUnderSeaLevel = false; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -@@ -2053,6 +2054,7 @@ public class PurpurWorldConfig { - ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks); - ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater); - ocelotAlwaysDropExp = getBoolean("mobs.ocelot.always-drop-exp", ocelotAlwaysDropExp); -+ ocelotSpawnUnderSeaLevel = getBoolean("mobs.ocelot.spawn-below-sea-level", ocelotSpawnUnderSeaLevel); - } - - public boolean pandaRidable = false; diff --git a/patches/server/0305-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch b/patches/server/0305-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch deleted file mode 100644 index e0789dede..000000000 --- a/patches/server/0305-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sun, 13 Aug 2023 06:26:08 -0700 -Subject: [PATCH] add an option for piglins to ignore gold-trimmed armor - - -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java -index d98c526676202741e628d5e317b8cdd3f4d3be0a..edf9208ed49a179dc15308dd72ee47367c0a9ded 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java -@@ -597,20 +597,33 @@ public class PiglinAi { - Iterator iterator = iterable.iterator(); - - Item item; -+ ItemStack itemstack; // Purpur - - do { - if (!iterator.hasNext()) { - return false; - } - -- ItemStack itemstack = (ItemStack) iterator.next(); -+ itemstack = (ItemStack) iterator.next(); // Purpur - - item = itemstack.getItem(); -- } while (!(item instanceof ArmorItem) || ((ArmorItem) item).getMaterial() != ArmorMaterials.GOLD); -+ } while (!(item instanceof ArmorItem) || ((ArmorItem) item).getMaterial() != ArmorMaterials.GOLD && (!entity.level().purpurConfig.piglinIgnoresArmorWithGoldTrim || !isWearingGoldTrim(entity, itemstack))); // Purpur - - return true; - } - -+ // Purpur start -+ private static boolean isWearingGoldTrim(LivingEntity entity, ItemStack itemstack) { -+ Optional optionalArmorTrim = net.minecraft.world.item.armortrim.ArmorTrim.getTrim(entity.level().registryAccess(), itemstack); -+ -+ if (optionalArmorTrim.isEmpty()) return false; -+ -+ net.minecraft.world.item.armortrim.ArmorTrim armorTrim = optionalArmorTrim.get(); -+ -+ return armorTrim.material().is(net.minecraft.world.item.armortrim.TrimMaterials.GOLD); -+ } -+ // Purpur end -+ - private static void stopWalking(Piglin piglin) { - piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET); - piglin.getNavigation().stop(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 383473859796d28a86dfd5fa5e407b6b5ea6aeb7..ec6c29638935835d590823c5cc35141a7f4d32f2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2209,6 +2209,7 @@ public class PurpurWorldConfig { - public int piglinPortalSpawnModifier = 2000; - public boolean piglinAlwaysDropExp = false; - public double piglinHeadVisibilityPercent = 0.5D; -+ public boolean piglinIgnoresArmorWithGoldTrim = false; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -2224,6 +2225,7 @@ public class PurpurWorldConfig { - piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier); - piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp); - piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent); -+ piglinIgnoresArmorWithGoldTrim = getBoolean("mobs.piglin.ignores-armor-with-gold-trim", piglinIgnoresArmorWithGoldTrim); - } - - public boolean piglinBruteRidable = false; diff --git a/patches/server/0306-Add-option-for-always-showing-item-in-player-death-m.patch b/patches/server/0306-Add-option-for-always-showing-item-in-player-death-m.patch deleted file mode 100644 index c9c2a1c99..000000000 --- a/patches/server/0306-Add-option-for-always-showing-item-in-player-death-m.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Tue, 22 Aug 2023 22:18:26 -0700 -Subject: [PATCH] Add option for always showing item in player death messages - - -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -index 45e4717ba832edceeafdba575323c2527c350193..45a1cbe958c1a626df97022afe22484f2b387233 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -59,7 +59,7 @@ public class CombatTracker { - } - - ItemStack itemStack = var10000; -- return !itemStack.isEmpty() && itemStack.hasCustomHoverName() ? Component.translatable(itemDeathTranslationKey, this.mob.getDisplayName(), attackerDisplayName, itemStack.getDisplayName()) : Component.translatable(deathTranslationKey, this.mob.getDisplayName(), attackerDisplayName); -+ return !itemStack.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemStack.hasCustomHoverName()) ? Component.translatable(itemDeathTranslationKey, this.mob.getDisplayName(), attackerDisplayName, itemStack.getDisplayName()) : Component.translatable(deathTranslationKey, this.mob.getDisplayName(), attackerDisplayName); - } - - private Component getFallMessage(CombatEntry damageRecord, @Nullable Entity attacker) { -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index fc1fb63ee0e28b8d1f065bfad716b465cde5a69f..c8860f20956a2819da001e93938249452bf7cb49 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -126,7 +126,7 @@ public class DamageSource { - - ItemStack itemstack1 = itemstack; - -- return !itemstack1.isEmpty() && itemstack1.hasCustomHoverName() ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent); -+ return !itemstack1.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemstack1.hasCustomHoverName()) ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent); - } - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index f10679ef2cbd73397b64eb69516c8a46e4d35a4e..3633574e112f217b412217dd243a631dc4e9c40c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -647,4 +647,9 @@ public class PurpurConfig { - block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue(); - }); - } -+ -+ public static boolean playerDeathsAlwaysShowItem = false; -+ private static void playerDeathsAlwaysShowItem() { -+ playerDeathsAlwaysShowItem = getBoolean("settings.player-deaths-always-show-item", playerDeathsAlwaysShowItem); -+ } - } diff --git a/patches/server/0307-place-end-crystal-on-any-block.patch b/patches/server/0307-place-end-crystal-on-any-block.patch deleted file mode 100644 index 40cf9aa56..000000000 --- a/patches/server/0307-place-end-crystal-on-any-block.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Wed, 23 Aug 2023 01:39:14 -0700 -Subject: [PATCH] place end crystal on any block - - -diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -index 3688e9f8c6c6d1239095e3a87060ccca90386d0c..34254eec36d34ae343733fa1abbaaba60be41a3b 100644 ---- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java -+++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -@@ -26,7 +26,7 @@ public class EndCrystalItem extends Item { - BlockPos blockposition = context.getClickedPos(); - BlockState iblockdata = world.getBlockState(blockposition); - -- if (!iblockdata.is(Blocks.OBSIDIAN) && !iblockdata.is(Blocks.BEDROCK)) { -+ if (!world.purpurConfig.endCrystalPlaceAnywhere && !iblockdata.is(Blocks.OBSIDIAN) && !iblockdata.is(Blocks.BEDROCK)) { - return InteractionResult.FAIL; - } else { - BlockPos blockposition1 = blockposition.above(); final BlockPos aboveBlockPosition = blockposition1; // Paper - OBFHELPER -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ec6c29638935835d590823c5cc35141a7f4d32f2..078102e636803f38facc049952813ff2f8b63594 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -903,6 +903,7 @@ public class PurpurWorldConfig { - public boolean basedEndCrystalExplosionFire = false; - public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; - public int endCrystalCramming = 0; -+ public boolean endCrystalPlaceAnywhere = false; - private void endCrystalSettings() { - if (PurpurConfig.version < 31) { - if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) { -@@ -931,6 +932,7 @@ public class PurpurWorldConfig { - basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; - } - endCrystalCramming = getInt("blocks.end-crystal.cramming-amount", endCrystalCramming); -+ endCrystalPlaceAnywhere = getBoolean("gameplay-mechanics.item.end-crystal.place-anywhere", endCrystalPlaceAnywhere); - } - - public boolean farmlandBypassMobGriefing = false; diff --git a/patches/server/0308-Disable-pufferfish-suffocation-optimization-for-with.patch b/patches/server/0308-Disable-pufferfish-suffocation-optimization-for-with.patch deleted file mode 100644 index 61678d9c4..000000000 --- a/patches/server/0308-Disable-pufferfish-suffocation-optimization-for-with.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wefhy <85190077+wefhy@users.noreply.github.com> -Date: Sat, 26 Aug 2023 23:48:05 -0700 -Subject: [PATCH] Disable pufferfish suffocation optimization for withers - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 0b08cf7b26731ffd6f3909342f4e3a92fe0c813e..4cd04661b39a7055f5767a3dff88f9a3e197d810 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -419,7 +419,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - boolean flag = this instanceof net.minecraft.world.entity.player.Player; - - if (!this.level().isClientSide) { -- if ((!gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F))) && this.isInWall()) { // Pufferfish - optimize suffocation -+ if (shouldCheckForSuffocation() && this.isInWall()) { // Pufferfish - optimize suffocation // Purpur - this.hurt(this.damageSources().inWall(), 1.0F); - } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { - double d0 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); -@@ -1412,6 +1412,11 @@ public abstract class LivingEntity extends Entity implements Attackable { - return true; - } - // Pufferfish end -+ // Purpur start -+ public boolean shouldCheckForSuffocation() { -+ return (!gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F))); -+ } -+ // Purpur end - - @Override - public boolean hurt(DamageSource source, float amount) { -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 71f8cea4a33b30fdafa41714a424b3092a3a7cd0..4952a7daa1aad68e3ba53123093cd879e0d544b5 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -291,6 +291,13 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - this.bossEvent.setName(this.getDisplayName()); - } - -+ // Purpur start - optimize suffocation -+ @Override -+ public boolean shouldCheckForSuffocation() { -+ return true; -+ } -+ // Purpur end -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.WITHER_AMBIENT; diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/unapplied-api/0001-Pufferfish-API-Changes.patch similarity index 93% rename from patches/api/0001-Pufferfish-API-Changes.patch rename to patches/unapplied-api/0001-Pufferfish-API-Changes.patch index 7b92d9cbd..1259ec0fb 100644 --- a/patches/api/0001-Pufferfish-API-Changes.patch +++ b/patches/unapplied-api/0001-Pufferfish-API-Changes.patch @@ -20,18 +20,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/build.gradle.kts b/build.gradle.kts -index fb47a794893d5fef026a35a7c573becf74420aa0..0330a20576c372c29ca4d98a2bc5b01e28303ac3 100644 +index 571534b42cd9c33d6a7bb6fe3bf3a28e33f8e5de..5ceaca1bd75335f85b4876a394ea8c2643cda694 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -46,6 +46,7 @@ dependencies { +@@ -66,6 +66,7 @@ dependencies { apiAndDocs("net.kyori:adventure-text-logger-slf4j") api("org.apache.logging.log4j:log4j-api:$log4jVersion") api("org.slf4j:slf4j-api:$slf4jVersion") + api("io.sentry:sentry:5.4.0") // Pufferfish - implementation("org.ow2.asm:asm:9.4") - implementation("org.ow2.asm:asm-commons:9.4") -@@ -89,6 +90,13 @@ val generateApiVersioningFile by tasks.registering { + implementation("org.ow2.asm:asm:9.7.1") + implementation("org.ow2.asm:asm-commons:9.7.1") +@@ -150,6 +151,13 @@ val generateApiVersioningFile by tasks.registering { } } @@ -214,7 +214,7 @@ index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8e +} diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java new file mode 100644 -index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704ff713345 +index 0000000000000000000000000000000000000000..3441cdad70da1bd523c5933b1a914688718c2657 --- /dev/null +++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java @@ -0,0 +1,40 @@ @@ -235,7 +235,7 @@ index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704 + @Deprecated + public static boolean canEnable(Logger logger) { + try { -+ if (SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19) { ++ if (SIMDDetection.getJavaVersion() < 17 || SIMDDetection.getJavaVersion() > 21) { + return false; + } else { + SIMDDetection.testRun = true; @@ -389,7 +389,7 @@ index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be + +} diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java -index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69a6f92891 100644 +index 6995f9cc08d162e3adcd3a28f6bfa6d329661999..b9edb96fc5bac8793477657dbb45ccf98ab17f27 100644 --- a/src/main/java/org/bukkit/map/MapPalette.java +++ b/src/main/java/org/bukkit/map/MapPalette.java @@ -1,6 +1,7 @@ @@ -400,7 +400,7 @@ index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69 import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; -@@ -40,7 +41,7 @@ public final class MapPalette { +@@ -45,7 +46,7 @@ public final class MapPalette { } @NotNull @@ -409,7 +409,7 @@ index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69 c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 0, 0, 0), c(0, 0, 0, 0), c(89, 125, 39), c(109, 153, 48), c(127, 178, 56), c(67, 94, 29), c(174, 164, 115), c(213, 201, 140), c(247, 233, 163), c(130, 123, 86), -@@ -211,9 +212,15 @@ public final class MapPalette { +@@ -216,9 +217,15 @@ public final class MapPalette { temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth()); byte[] result = new byte[temp.getWidth() * temp.getHeight()]; @@ -426,10 +426,10 @@ index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69 } diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -index fc2dae69165776d08274e34a69962cc70445f411..899d67fa782fac639fe7fb096e05c551d75bd647 100644 +index 001465eedafa51ac027a4db51cba6223edfe1171..2e6d62c4f3687e299c34e876c503b400e13be05a 100644 --- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -@@ -584,7 +584,9 @@ public final class SimplePluginManager implements PluginManager { +@@ -597,7 +597,9 @@ public final class SimplePluginManager implements PluginManager { // Paper start private void handlePluginException(String msg, Throwable ex, Plugin plugin) { @@ -439,7 +439,7 @@ index fc2dae69165776d08274e34a69962cc70445f411..899d67fa782fac639fe7fb096e05c551 callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin))); } // Paper end -@@ -654,9 +656,11 @@ public final class SimplePluginManager implements PluginManager { +@@ -667,9 +669,11 @@ public final class SimplePluginManager implements PluginManager { )); } } catch (Throwable ex) { @@ -452,7 +452,7 @@ index fc2dae69165776d08274e34a69962cc70445f411..899d67fa782fac639fe7fb096e05c551 callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event))); } diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..301e82369603f3dd6e6c1bd380da4bacacd7ef6c 100644 +index b412aaf08901d169ac9fc89b36f9d6ccb95c53d3..45a9ca8969f635d20cc44c062fda85bbccd8f8ff 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -336,7 +336,13 @@ public final class JavaPluginLoader implements PluginLoader { @@ -480,10 +480,10 @@ index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..301e82369603f3dd6e6c1bd380da4bac if (cloader instanceof PluginClassLoader) { diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java -index 13da387d3b59bc67c0d73e3fbd3a4034b1281527..7572a0bf6614b02be3cbccc7b86e52ee1b8df621 100644 +index 7e4f7cb2afbc145e532285c793573ad107bc3033..12449e18180d604e9cbbc744da74a8b222a18e1f 100644 --- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java -@@ -48,6 +48,8 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm +@@ -50,6 +50,8 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm private io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup classLoaderGroup; // Paper public io.papermc.paper.plugin.provider.entrypoint.DependencyContext dependencyContext; // Paper @@ -492,7 +492,7 @@ index 13da387d3b59bc67c0d73e3fbd3a4034b1281527..7572a0bf6614b02be3cbccc7b86e52ee static { ClassLoader.registerAsParallelCapable(); } -@@ -183,6 +185,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm +@@ -197,6 +199,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm throw new ClassNotFoundException(name); } @@ -500,7 +500,7 @@ index 13da387d3b59bc67c0d73e3fbd3a4034b1281527..7572a0bf6614b02be3cbccc7b86e52ee @Override protected Class findClass(String name) throws ClassNotFoundException { if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { -@@ -190,7 +193,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm +@@ -204,7 +207,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm } Class result = classes.get(name); @@ -509,7 +509,7 @@ index 13da387d3b59bc67c0d73e3fbd3a4034b1281527..7572a0bf6614b02be3cbccc7b86e52ee String path = name.replace('.', '/').concat(".class"); JarEntry entry = jar.getJarEntry(path); -@@ -237,6 +240,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm +@@ -251,6 +254,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm this.setClass(name, result); // Paper } @@ -517,7 +517,7 @@ index 13da387d3b59bc67c0d73e3fbd3a4034b1281527..7572a0bf6614b02be3cbccc7b86e52ee return result; } -@@ -251,6 +255,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm +@@ -265,6 +269,7 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm // Paper end super.close(); } finally { diff --git a/patches/api/0002-Fix-pufferfish-issues.patch b/patches/unapplied-api/0002-Fix-pufferfish-issues.patch similarity index 54% rename from patches/api/0002-Fix-pufferfish-issues.patch rename to patches/unapplied-api/0002-Fix-pufferfish-issues.patch index 08bd8a159..d3cd67b69 100644 --- a/patches/api/0002-Fix-pufferfish-issues.patch +++ b/patches/unapplied-api/0002-Fix-pufferfish-issues.patch @@ -4,8 +4,26 @@ Date: Tue, 4 Jan 2022 23:05:41 -0600 Subject: [PATCH] Fix pufferfish issues +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java +index 652ff54e7c50412503725d628bfe72ed03059790..a7c0936a1ee80618f9a504087c88814f738f4693 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfo.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java +@@ -19,6 +19,13 @@ public interface ServerBuildInfo { + */ + Key BRAND_PAPER_ID = Key.key("papermc", "paper"); + ++ // Purpur start ++ /** ++ * The brand id for Pufferfish. ++ */ ++ Key BRAND_PUFFERFISH_ID = Key.key("pufferfish", "pufferfish"); ++ // Purpur end ++ + /** + * Gets the {@code ServerBuildInfo}. + * diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java -index 06e96e5c98f1a7a68c8b4b5e527314c1aa774e38..49bba9a7a02b9cf3a552583315eff2b7dbe060c0 100644 +index f9e4b16a21d6cc6c9cbbe06d20c8af25e72e3ddb..4028b230e7fe1c78520f227a377a2a61e8381ecc 100644 --- a/src/test/java/org/bukkit/AnnotationTest.java +++ b/src/test/java/org/bukkit/AnnotationTest.java @@ -47,6 +47,10 @@ public class AnnotationTest { diff --git a/patches/unapplied-server/0001-Pufferfish-Server-Changes.patch b/patches/unapplied-server/0001-Pufferfish-Server-Changes.patch new file mode 100644 index 000000000..ab554b199 --- /dev/null +++ b/patches/unapplied-server/0001-Pufferfish-Server-Changes.patch @@ -0,0 +1,2005 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Raneri +Date: Wed, 3 Feb 2021 23:02:38 -0600 +Subject: [PATCH] Pufferfish Server Changes + +Pufferfish +Copyright (C) 2022 Pufferfish Studios LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +diff --git a/build.gradle.kts b/build.gradle.kts +index 2da91ed6363c0851e4c459188f5e8ef5475e0c97..96d831791edbe6ae07325008b760f70f75c4d713 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -25,7 +25,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider { + // Paper end - configure mockito agent that is needed in newer java versions + + dependencies { +- implementation(project(":paper-api")) ++ implementation(project(":pufferfish-api")) // Pufferfish // Paper + implementation("ca.spottedleaf:concurrentutil:0.0.2") // Paper - Add ConcurrentUtil dependency + // Paper start + implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ +@@ -61,6 +61,13 @@ dependencies { + runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") + runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") + ++ // Pufferfish start ++ implementation("org.yaml:snakeyaml:1.32") ++ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { ++ exclude(group="org.yaml", module="snakeyaml") ++ } ++ // Pufferfish end ++ + testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test + testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") + testImplementation("org.junit.platform:junit-platform-suite-engine:1.10.0") +@@ -87,6 +94,14 @@ paperweight { + craftBukkitPackageVersion.set("v1_21_R3") // also needs to be updated in MappingEnvironment + } + ++ ++// Pufferfish Start ++tasks.withType { ++ val compilerArgs = options.compilerArgs ++ compilerArgs.add("--add-modules=jdk.incubator.vector") ++} ++// Pufferfish End ++ + tasks.jar { + archiveClassifier.set("dev") + +@@ -100,14 +115,14 @@ tasks.jar { + val gitBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() // Paper + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", +- "Implementation-Title" to "Paper", ++ "Implementation-Title" to "Pufferfish", // Pufferfish + "Implementation-Version" to implementationVersion, + "Implementation-Vendor" to date, // Paper +- "Specification-Title" to "Paper", ++ "Specification-Title" to "Pufferfish", // Pufferfish + "Specification-Version" to project.version, +- "Specification-Vendor" to "Paper Team", +- "Brand-Id" to "papermc:paper", +- "Brand-Name" to "Paper", ++ "Specification-Vendor" to "Pufferfish Studios LLC", // Pufferfish ++ "Brand-Id" to "pufferfish:pufferfish", // Pufferfish ++ "Brand-Name" to "Pufferfish", // Pufferfish + "Build-Number" to (build ?: ""), + "Build-Time" to Instant.now().toString(), + "Git-Branch" to gitBranch, // Paper +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java +index c21e00812f1aaa1279834a0562d360d6b89e146c..877d2095a066854939f260ca4b0b8c7b5abb620f 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java +@@ -18,7 +18,7 @@ public final class IteratorSafeOrderedReferenceSet { + + private final double maxFragFactor; + +- private int iteratorCount; ++ private final java.util.concurrent.atomic.AtomicInteger iteratorCount = new java.util.concurrent.atomic.AtomicInteger(); // Pufferfish - async mob spawning + + public IteratorSafeOrderedReferenceSet() { + this(16, 0.75f, 16, 0.2); +@@ -79,7 +79,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + public int createRawIterator() { +- ++this.iteratorCount; ++ this.iteratorCount.incrementAndGet(); // Pufferfish - async mob spawning + if (this.indexMap.isEmpty()) { + return -1; + } else { +@@ -100,7 +100,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + public void finishRawIterator() { +- if (--this.iteratorCount == 0) { ++ if (this.iteratorCount.decrementAndGet() == 0) { // Pufferfish - async mob spawning + if (this.getFragFactor() >= this.maxFragFactor) { + this.defrag(); + } +@@ -117,7 +117,7 @@ public final class IteratorSafeOrderedReferenceSet { + throw new IllegalStateException(); + } + this.listElements[index] = null; +- if (this.iteratorCount == 0 && this.getFragFactor() >= this.maxFragFactor) { ++ if (this.iteratorCount.get() == 0 && this.getFragFactor() >= this.maxFragFactor) { // Pufferfish - async mob spawning + this.defrag(); + } + //this.check(); +@@ -219,7 +219,7 @@ public final class IteratorSafeOrderedReferenceSet { + } + + public IteratorSafeOrderedReferenceSet.Iterator iterator(final int flags) { +- ++this.iteratorCount; ++ this.iteratorCount.incrementAndGet(); // Pufferfish - async mob spawning + return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); + } + +diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java +index 8f62879582195d8ae4f64bd23f752fa133b1c973..8433c3ac440faa969069d1929b8b77fcb1080be7 100644 +--- a/src/main/java/com/destroystokyo/paper/Metrics.java ++++ b/src/main/java/com/destroystokyo/paper/Metrics.java +@@ -592,7 +592,7 @@ public class Metrics { + boolean logFailedRequests = config.getBoolean("logFailedRequests", false); + // Only start Metrics, if it's enabled in the config + if (config.getBoolean("enabled", true)) { +- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); ++ Metrics metrics = new Metrics("Pufferfish", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); +@@ -606,11 +606,11 @@ public class Metrics { + final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); + if (implVersion != null) { + final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); +- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); ++ paperVersion = "git-Pufferfish-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); // Pufferfish + } else { + paperVersion = "unknown"; + } +- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion)); ++ metrics.addCustomChart(new Metrics.SimplePie("pufferfish_version", () -> paperVersion)); // Pufferfish + + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..020368da69b9a492155f6de6297f74732f4ab6ea +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java +@@ -0,0 +1,68 @@ ++package gg.pufferfish.pufferfish; ++ ++import java.io.IOException; ++import java.util.Collections; ++import java.util.List; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.md_5.bungee.api.ChatColor; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandSender; ++ ++public class PufferfishCommand extends Command { ++ ++ public PufferfishCommand() { ++ super("pufferfish"); ++ this.description = "Pufferfish related commands"; ++ this.usageMessage = "/pufferfish [reload | version]"; ++ this.setPermission("bukkit.command.pufferfish"); ++ } ++ ++ public static void init() { ++ MinecraftServer.getServer().server.getCommandMap().register("pufferfish", "Pufferfish", new PufferfishCommand()); ++ } ++ ++ @Override ++ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { ++ if (args.length == 1) { ++ return Stream.of("reload", "version") ++ .filter(arg -> arg.startsWith(args[0].toLowerCase())) ++ .collect(Collectors.toList()); ++ } ++ return Collections.emptyList(); ++ } ++ ++ @Override ++ public boolean execute(CommandSender sender, String commandLabel, String[] args) { ++ if (!testPermission(sender)) return true; ++ String prefix = ChatColor.of("#12fff6") + "" + ChatColor.BOLD + "Pufferfish » " + ChatColor.of("#e8f9f9"); ++ ++ if (args.length != 1) { ++ sender.sendMessage(prefix + "Usage: " + usageMessage); ++ args = new String[]{"version"}; ++ } ++ ++ if (args[0].equalsIgnoreCase("reload")) { ++ MinecraftServer console = MinecraftServer.getServer(); ++ try { ++ PufferfishConfig.load(); ++ } catch (IOException e) { ++ sender.sendMessage(Component.text("Failed to reload.", NamedTextColor.RED)); ++ e.printStackTrace(); ++ return true; ++ } ++ console.server.reloadCount++; ++ ++ Command.broadcastCommandMessage(sender, prefix + "Pufferfish configuration has been reloaded."); ++ } else if (args[0].equalsIgnoreCase("version")) { ++ Command.broadcastCommandMessage(sender, prefix + "This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); ++ } ++ ++ return true; ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f5a43a1e1a78b3eaabbcadc7af09750ee478eeb6 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +@@ -0,0 +1,280 @@ ++package gg.pufferfish.pufferfish; ++ ++import gg.pufferfish.pufferfish.simd.SIMDDetection; ++import java.io.File; ++import java.io.IOException; ++import java.util.Collections; ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.server.MinecraftServer; ++import org.apache.logging.log4j.Level; ++import org.bukkit.configuration.ConfigurationSection; ++import net.minecraft.world.entity.EntityType; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.util.List; ++import net.minecraft.server.MinecraftServer; ++import org.apache.logging.log4j.Level; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemoryConfiguration; ++import org.jetbrains.annotations.Nullable; ++import org.simpleyaml.configuration.comments.CommentType; ++import org.simpleyaml.configuration.file.YamlFile; ++import org.simpleyaml.exceptions.InvalidConfigurationException; ++ ++public class PufferfishConfig { ++ ++ private static final YamlFile config = new YamlFile(); ++ private static int updates = 0; ++ ++ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { ++ ConfigurationSection newSection = new MemoryConfiguration(); ++ for (String key : section.getKeys(false)) { ++ if (section.isConfigurationSection(key)) { ++ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); ++ } else { ++ newSection.set(key, section.get(key)); ++ } ++ } ++ return newSection; ++ } ++ ++ public static ConfigurationSection getConfigCopy() { ++ return convertToBukkit(config); ++ } ++ ++ public static int getUpdates() { ++ return updates; ++ } ++ ++ public static void load() throws IOException { ++ File configFile = new File("pufferfish.yml"); ++ ++ if (configFile.exists()) { ++ try { ++ config.load(configFile); ++ } catch (InvalidConfigurationException e) { ++ throw new IOException(e); ++ } ++ } ++ ++ getString("info.version", "1.0"); ++ setComment("info", ++ "Pufferfish Configuration", ++ "Check out Pufferfish Host for maximum performance server hosting: https://pufferfish.host", ++ "Join our Discord for support: https://discord.gg/reZw4vQV9H", ++ "Download new builds at https://ci.pufferfish.host/job/Pufferfish"); ++ ++ for (Method method : PufferfishConfig.class.getDeclaredMethods()) { ++ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && ++ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { ++ method.setAccessible(true); ++ try { ++ method.invoke(null); ++ } catch (Throwable t) { ++ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); ++ } ++ } ++ } ++ ++ updates++; ++ ++ config.save(configFile); ++ ++ // Attempt to detect vectorization ++ try { ++ SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER); ++ SIMDDetection.versionLimited = SIMDDetection.getJavaVersion() < 17 || SIMDDetection.getJavaVersion() > 21; ++ } catch (NoClassDefFoundError | Exception ignored) { ++ ignored.printStackTrace(); ++ } ++ ++ if (SIMDDetection.isEnabled) { ++ PufferfishLogger.LOGGER.info("SIMD operations detected as functional. Will replace some operations with faster versions."); ++ } else if (SIMDDetection.versionLimited) { ++ PufferfishLogger.LOGGER.warning("Will not enable SIMD! These optimizations are only safely supported on Java 17-21."); ++ } else { ++ PufferfishLogger.LOGGER.warning("SIMD operations are available for your server, but are not configured!"); ++ PufferfishLogger.LOGGER.warning("To enable additional optimizations, add \"--add-modules=jdk.incubator.vector\" to your startup flags, BEFORE the \"-jar\"."); ++ PufferfishLogger.LOGGER.warning("If you have already added this flag, then SIMD operations are not supported on your JVM or CPU."); ++ PufferfishLogger.LOGGER.warning("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun); ++ } ++ } ++ ++ private static void setComment(String key, String... comment) { ++ if (config.contains(key)) { ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static void ensureDefault(String key, Object defaultValue, String... comment) { ++ if (!config.contains(key)) { ++ config.set(key, defaultValue); ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { ++ return getBoolean(key, null, defaultValue, comment); ++ } ++ ++ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getBoolean(key, defaultValue); ++ } ++ ++ private static int getInt(String key, int defaultValue, String... comment) { ++ return getInt(key, null, defaultValue, comment); ++ } ++ ++ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getInt(key, defaultValue); ++ } ++ ++ private static double getDouble(String key, double defaultValue, String... comment) { ++ return getDouble(key, null, defaultValue, comment); ++ } ++ ++ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getDouble(key, defaultValue); ++ } ++ ++ private static String getString(String key, String defaultValue, String... comment) { ++ return getOldString(key, null, defaultValue, comment); ++ } ++ ++ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getString(key, defaultValue); ++ } ++ ++ private static List getStringList(String key, List defaultValue, String... comment) { ++ return getStringList(key, null, defaultValue, comment); ++ } ++ ++ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getStringList(key); ++ } ++ ++ public static String sentryDsn; ++ private static void sentry() { ++ String sentryEnvironment = System.getenv("SENTRY_DSN"); ++ String sentryConfig = getString("sentry-dsn", "", "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/"); ++ ++ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; ++ if (sentryDsn != null && !sentryDsn.isBlank()) { ++ gg.pufferfish.pufferfish.sentry.SentryManager.init(); ++ } ++ } ++ ++ public static boolean enableBooks; ++ private static void books() { ++ enableBooks = getBoolean("enable-books", true, ++ "Whether or not books should be writeable.", ++ "Servers that anticipate being a target for duping may want to consider", ++ "disabling this option.", ++ "This can be overridden per-player with the permission pufferfish.usebooks"); ++ } ++ ++ public static boolean tpsCatchup; ++ private static void tpsCatchup() { ++ tpsCatchup = getBoolean("tps-catchup", true, ++ "If this setting is true, the server will run faster after a lag spike in", ++ "an attempt to maintain 20 TPS. This option (defaults to true per", ++ "spigot/paper) can cause mobs to move fast after a lag spike."); ++ } ++ ++ public static boolean enableSuffocationOptimization; ++ private static void suffocationOptimization() { ++ enableSuffocationOptimization = getBoolean("enable-suffocation-optimization", true, ++ "Optimizes the suffocation check by selectively skipping", ++ "the check in a way that still appears vanilla. This should", ++ "be left enabled on most servers, but is provided as a", ++ "configuration option if the vanilla deviation is undesirable."); ++ } ++ ++ public static boolean enableAsyncMobSpawning; ++ public static boolean asyncMobSpawningInitialized; ++ private static void asyncMobSpawning() { ++ boolean temp = getBoolean("enable-async-mob-spawning", true, ++ "Whether or not asynchronous mob spawning should be enabled.", ++ "On servers with many entities, this can improve performance by up to 15%. You must have", ++ "paper's per-player-mob-spawns setting set to true for this to work.", ++ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", ++ "This just offloads some expensive calculations that are required for mob spawning."); ++ ++ // This prevents us from changing the value during a reload. ++ if (!asyncMobSpawningInitialized) { ++ asyncMobSpawningInitialized = true; ++ enableAsyncMobSpawning = temp; ++ } ++ } ++ ++ public static int maxProjectileLoadsPerTick; ++ public static int maxProjectileLoadsPerProjectile; ++ private static void projectileLoading() { ++ maxProjectileLoadsPerTick = getInt("projectile.max-loads-per-tick", 10, "Controls how many chunks are allowed", "to be sync loaded by projectiles in a tick."); ++ maxProjectileLoadsPerProjectile = getInt("projectile.max-loads-per-projectile", 10, "Controls how many chunks a projectile", "can load in its lifetime before it gets", "automatically removed."); ++ ++ setComment("projectile", "Optimizes projectile settings"); ++ } ++ ++ ++ public static boolean dearEnabled; ++ public static int startDistance; ++ public static int startDistanceSquared; ++ public static int maximumActivationPrio; ++ public static int activationDistanceMod; ++ ++ private static void dynamicActivationOfBrains() throws IOException { ++ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); ++ startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, ++ "This value determines how far away an entity has to be", ++ "from the player to start being effected by DEAR."); ++ startDistanceSquared = startDistance * startDistance; ++ maximumActivationPrio = getInt("dab.max-tick-freq", "activation-range.max-tick-freq", 20, ++ "This value defines how often in ticks, the furthest entity", ++ "will get their pathfinders and behaviors ticked. 20 = 1s"); ++ activationDistanceMod = getInt("dab.activation-dist-mod", "activation-range.activation-dist-mod", 8, ++ "This value defines how much distance modifies an entity's", ++ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", ++ "If you want further away entities to tick less often, use 7.", ++ "If you want further away entities to tick more often, try 9."); ++ ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ entityType.dabEnabled = true; // reset all, before setting the ones to true ++ } ++ getStringList("dab.blacklisted-entities", "activation-range.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") ++ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { ++ entityType.dabEnabled = false; ++ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); ++ ++ setComment("dab", "Optimizes entity brains when", "they're far away from the player"); ++ } ++ ++ public static boolean throttleInactiveGoalSelectorTick; ++ private static void inactiveGoalSelectorThrottle() { ++ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, ++ "Throttles the AI goal selector in entity inactive ticks.", ++ "This can improve performance by a few percent, but has minor gameplay implications."); ++ } ++ ++ public static boolean allowEndCrystalRespawn; ++ private static void allowEndCrystalRespawn() { ++ allowEndCrystalRespawn = getBoolean("allow-end-crystal-respawn", true, ++ "Allows end crystals to respawn the ender dragon.", ++ "On servers that expect end crystal fights in the end dimension, disabling this", ++ "will prevent the server from performing an expensive search to attempt respawning", ++ "the ender dragon whenever a player places an end crystal."); ++ } ++ ++ ++ public static boolean disableMethodProfiler; ++ private static void miscSettings() { ++ disableMethodProfiler = getBoolean("misc.disable-method-profiler", true); ++ setComment("misc", "Settings for things that don't belong elsewhere"); ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java +new file mode 100644 +index 0000000000000000000000000000000000000000..53f2df00c6809618a9ee3d2ea72e85e8052fbcf1 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishLogger.java +@@ -0,0 +1,16 @@ ++package gg.pufferfish.pufferfish; ++ ++import java.util.logging.Level; ++import java.util.logging.Logger; ++import org.bukkit.Bukkit; ++ ++public class PufferfishLogger extends Logger { ++ public static final PufferfishLogger LOGGER = new PufferfishLogger(); ++ ++ private PufferfishLogger() { ++ super("Pufferfish", null); ++ ++ setParent(Bukkit.getLogger()); ++ setLevel(Level.ALL); ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java +new file mode 100644 +index 0000000000000000000000000000000000000000..06323dcc745aed16123980fc559d7b65c42f1e1c +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java +@@ -0,0 +1,132 @@ ++package gg.pufferfish.pufferfish; ++ ++import com.destroystokyo.paper.VersionHistoryManager; ++import com.destroystokyo.paper.util.VersionFetcher; ++import com.google.gson.Gson; ++import com.google.gson.JsonObject; ++import io.papermc.paper.ServerBuildInfo; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.JoinConfiguration; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.format.TextDecoration; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.io.IOException; ++import java.net.URI; ++import java.net.http.HttpClient; ++import java.net.http.HttpRequest; ++import java.net.http.HttpResponse; ++import java.nio.charset.StandardCharsets; ++import java.util.concurrent.TimeUnit; ++import java.util.logging.Level; ++import java.util.logging.Logger; ++ ++import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.format.NamedTextColor.GREEN; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++ ++public class PufferfishVersionFetcher implements VersionFetcher { ++ ++ private static final Logger LOGGER = Logger.getLogger("PufferfishVersionFetcher"); ++ private static final HttpClient client = HttpClient.newHttpClient(); ++ ++ private static final URI JENKINS_URI = URI.create("https://ci.pufferfish.host/job/Pufferfish-1.21/lastSuccessfulBuild/buildNumber"); ++ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.21...%s"; ++ ++ private static final HttpResponse.BodyHandler JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers ++ .mapping( ++ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), ++ string -> new Gson().fromJson(string, JsonObject.class) ++ ); ++ ++ @Override ++ public long getCacheTime() { ++ return TimeUnit.MINUTES.toMillis(30); ++ } ++ ++ @Override ++ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) { ++ @NotNull Component component; ++ ++ if (ServerBuildInfo.buildInfo().buildNumber().isPresent()) { ++ component = this.fetchJenkinsVersion(ServerBuildInfo.buildInfo().buildNumber().getAsInt()); ++ } else if (ServerBuildInfo.buildInfo().gitCommit().isPresent()) { ++ component = this.fetchGithubVersion(ServerBuildInfo.buildInfo().gitCommit().get()); ++ } else { ++ component = text("Unknown server version.", RED); ++ } ++ ++ final @Nullable Component history = this.getHistory(); ++ return history != null ? Component ++ .join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component; ++ } ++ ++ private @NotNull Component fetchJenkinsVersion(final int versionNumber) { ++ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build(); ++ try { ++ final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); ++ if (response.statusCode() != 200) { ++ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); ++ } ++ ++ int latestVersionNumber; ++ try { ++ latestVersionNumber = Integer.parseInt(response.body()); ++ } catch (NumberFormatException e) { ++ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\"."); ++ return text("Received invalid response from server.", RED); ++ } ++ ++ final int versionDiff = latestVersionNumber - versionNumber; ++ return this.getResponseMessage(versionDiff); ++ } catch (IOException | InterruptedException e) { ++ LOGGER.log(Level.WARNING, "Failed to look up version from Jenkins", e); ++ return text("Failed to retrieve version from server.", RED); ++ } ++ } ++ ++ // Based off code contributed by Techcable in Paper/GH-65 ++ private @NotNull Component fetchGithubVersion(final @NotNull String hash) { ++ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash)); ++ final HttpRequest request = HttpRequest.newBuilder(uri).build(); ++ try { ++ final HttpResponse response = client.send(request, JSON_OBJECT_BODY_HANDLER); ++ if (response.statusCode() != 200) { ++ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED); ++ } ++ ++ final JsonObject obj = response.body(); ++ final int versionDiff = obj.get("behind_by").getAsInt(); ++ ++ return this.getResponseMessage(versionDiff); ++ } catch (IOException | InterruptedException e) { ++ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e); ++ return text("Failed to retrieve version from server.", RED); ++ } ++ } ++ ++ private @NotNull Component getResponseMessage(final int versionDiff) { ++ return switch (Math.max(-1, Math.min(1, versionDiff))) { ++ case -1 -> text("You are running an unsupported version of Pufferfish.", RED); ++ case 0 -> text("You are on the latest version!", GREEN); ++ default -> text("You are running " + versionDiff + " version" + (versionDiff == 1 ? "" : "s") + " behind. " + ++ "Please update your server when possible to maintain stability and security, and to receive the latest optimizations.", ++ RED); ++ }; ++ } ++ ++ private @Nullable Component getHistory() { ++ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); ++ if (data == null) { ++ return null; ++ } ++ ++ final String oldVersion = data.getOldVersion(); ++ if (oldVersion == null) { ++ return null; ++ } ++ ++ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java +new file mode 100644 +index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834733d0621 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java +@@ -0,0 +1,135 @@ ++package gg.pufferfish.pufferfish.sentry; ++ ++import com.google.common.reflect.TypeToken; ++import com.google.gson.Gson; ++import io.sentry.Breadcrumb; ++import io.sentry.Sentry; ++import io.sentry.SentryEvent; ++import io.sentry.SentryLevel; ++import io.sentry.protocol.Message; ++import io.sentry.protocol.User; ++import java.util.Map; ++import org.apache.logging.log4j.Level; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Marker; ++import org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.Logger; ++import org.apache.logging.log4j.core.appender.AbstractAppender; ++import org.apache.logging.log4j.core.filter.AbstractFilter; ++ ++public class PufferfishSentryAppender extends AbstractAppender { ++ ++ private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(PufferfishSentryAppender.class); ++ private static final Gson GSON = new Gson(); ++ ++ public PufferfishSentryAppender() { ++ super("PufferfishSentryAdapter", new SentryFilter(), null); ++ } ++ ++ @Override ++ public void append(LogEvent logEvent) { ++ if (logEvent.getThrown() != null && logEvent.getLevel().isMoreSpecificThan(Level.WARN)) { ++ try { ++ logException(logEvent); ++ } catch (Exception e) { ++ logger.warn("Failed to log event with sentry", e); ++ } ++ } else { ++ try { ++ logBreadcrumb(logEvent); ++ } catch (Exception e) { ++ logger.warn("Failed to log event with sentry", e); ++ } ++ } ++ } ++ ++ private void logException(LogEvent e) { ++ SentryEvent event = new SentryEvent(e.getThrown()); ++ ++ Message sentryMessage = new Message(); ++ sentryMessage.setMessage(e.getMessage().getFormattedMessage()); ++ ++ event.setThrowable(e.getThrown()); ++ event.setLevel(getLevel(e.getLevel())); ++ event.setLogger(e.getLoggerName()); ++ event.setTransaction(e.getLoggerName()); ++ event.setExtra("thread_name", e.getThreadName()); ++ ++ boolean hasContext = e.getContextData() != null; ++ ++ if (hasContext && e.getContextData().containsKey("pufferfishsentry_playerid")) { ++ User user = new User(); ++ user.setId(e.getContextData().getValue("pufferfishsentry_playerid")); ++ user.setUsername(e.getContextData().getValue("pufferfishsentry_playername")); ++ event.setUser(user); ++ } ++ ++ if (hasContext && e.getContextData().containsKey("pufferfishsentry_pluginname")) { ++ event.setExtra("plugin.name", e.getContextData().getValue("pufferfishsentry_pluginname")); ++ event.setExtra("plugin.version", e.getContextData().getValue("pufferfishsentry_pluginversion")); ++ event.setTransaction(e.getContextData().getValue("pufferfishsentry_pluginname")); ++ } ++ ++ if (hasContext && e.getContextData().containsKey("pufferfishsentry_eventdata")) { ++ Map eventFields = GSON.fromJson((String) e.getContextData().getValue("pufferfishsentry_eventdata"), new TypeToken>() {}.getType()); ++ if (eventFields != null) { ++ event.setExtra("event", eventFields); ++ } ++ } ++ ++ Sentry.captureEvent(event); ++ } ++ ++ private void logBreadcrumb(LogEvent e) { ++ Breadcrumb breadcrumb = new Breadcrumb(); ++ ++ breadcrumb.setLevel(getLevel(e.getLevel())); ++ breadcrumb.setCategory(e.getLoggerName()); ++ breadcrumb.setType(e.getLoggerName()); ++ breadcrumb.setMessage(e.getMessage().getFormattedMessage()); ++ ++ Sentry.addBreadcrumb(breadcrumb); ++ } ++ ++ private SentryLevel getLevel(Level level) { ++ switch (level.getStandardLevel()) { ++ case TRACE: ++ case DEBUG: ++ return SentryLevel.DEBUG; ++ case WARN: ++ return SentryLevel.WARNING; ++ case ERROR: ++ return SentryLevel.ERROR; ++ case FATAL: ++ return SentryLevel.FATAL; ++ case INFO: ++ default: ++ return SentryLevel.INFO; ++ } ++ } ++ ++ private static class SentryFilter extends AbstractFilter { ++ ++ @Override ++ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, String msg, ++ Object... params) { ++ return this.filter(logger.getName()); ++ } ++ ++ @Override ++ public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, Object msg, Throwable t) { ++ return this.filter(logger.getName()); ++ } ++ ++ @Override ++ public Result filter(LogEvent event) { ++ return this.filter(event == null ? null : event.getLoggerName()); ++ } ++ ++ private Result filter(String loggerName) { ++ return loggerName != null && loggerName.startsWith("gg.castaway.pufferfish.sentry") ? Result.DENY ++ : Result.NEUTRAL; ++ } ++ ++ } ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1b29210ad0bbb4ada150f23357f0c80d331c996d +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java +@@ -0,0 +1,40 @@ ++package gg.pufferfish.pufferfish.sentry; ++ ++import gg.pufferfish.pufferfish.PufferfishConfig; ++import io.sentry.Sentry; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++ ++public class SentryManager { ++ ++ private static final Logger logger = LogManager.getLogger(SentryManager.class); ++ ++ private SentryManager() { ++ ++ } ++ ++ private static boolean initialized = false; ++ ++ public static synchronized void init() { ++ if (initialized) { ++ return; ++ } ++ try { ++ initialized = true; ++ ++ Sentry.init(options -> { ++ options.setDsn(PufferfishConfig.sentryDsn); ++ options.setMaxBreadcrumbs(100); ++ }); ++ ++ PufferfishSentryAppender appender = new PufferfishSentryAppender(); ++ appender.start(); ++ ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addAppender(appender); ++ logger.info("Sentry logging started!"); ++ } catch (Exception e) { ++ logger.warn("Failed to initialize sentry!", e); ++ initialized = false; ++ } ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8e5323d5d9af25c8a85c4b34a6be76cfc54384cf +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java +@@ -0,0 +1,73 @@ ++package gg.pufferfish.pufferfish.util; ++ ++import com.google.common.collect.Queues; ++import gg.pufferfish.pufferfish.PufferfishLogger; ++import java.util.Queue; ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++import java.util.concurrent.locks.ReentrantLock; ++import java.util.logging.Level; ++ ++public class AsyncExecutor implements Runnable { ++ ++ private final Queue jobs = Queues.newArrayDeque(); ++ private final Lock mutex = new ReentrantLock(); ++ private final Condition cond = mutex.newCondition(); ++ private final Thread thread; ++ private volatile boolean killswitch = false; ++ ++ public AsyncExecutor(String threadName) { ++ this.thread = new Thread(this, threadName); ++ } ++ ++ public void start() { ++ thread.start(); ++ } ++ ++ public void kill() { ++ killswitch = true; ++ cond.signalAll(); ++ } ++ ++ public void submit(Runnable runnable) { ++ mutex.lock(); ++ try { ++ jobs.offer(runnable); ++ cond.signalAll(); ++ } finally { ++ mutex.unlock(); ++ } ++ } ++ ++ @Override ++ public void run() { ++ while (!killswitch) { ++ try { ++ Runnable runnable = takeRunnable(); ++ if (runnable != null) { ++ runnable.run(); ++ } ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (Exception e) { ++ PufferfishLogger.LOGGER.log(Level.SEVERE, e, () -> "Failed to execute async job for thread " + thread.getName()); ++ } ++ } ++ } ++ ++ private Runnable takeRunnable() throws InterruptedException { ++ mutex.lock(); ++ try { ++ while (jobs.isEmpty() && !killswitch) { ++ cond.await(); ++ } ++ ++ if (jobs.isEmpty()) return null; // We've set killswitch ++ ++ return jobs.remove(); ++ } finally { ++ mutex.unlock(); ++ } ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c1929840254a3e6d721816f4a20415bea1742580 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/util/IterableWrapper.java +@@ -0,0 +1,20 @@ ++package gg.pufferfish.pufferfish.util; ++ ++import java.util.Iterator; ++import org.jetbrains.annotations.NotNull; ++ ++public class IterableWrapper implements Iterable { ++ ++ private final Iterator iterator; ++ ++ public IterableWrapper(Iterator iterator) { ++ this.iterator = iterator; ++ } ++ ++ @NotNull ++ @Override ++ public Iterator iterator() { ++ return iterator; ++ } ++ ++} +diff --git a/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..facd55463d44cb7e3d2ca6892982f5497b8dded1 +--- /dev/null ++++ b/src/main/java/gg/pufferfish/pufferfish/util/Long2ObjectOpenHashMapWrapper.java +@@ -0,0 +1,40 @@ ++package gg.pufferfish.pufferfish.util; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import java.util.Map; ++import org.jetbrains.annotations.Nullable; ++ ++public class Long2ObjectOpenHashMapWrapper extends Long2ObjectOpenHashMap { ++ ++ private final Map backingMap; ++ ++ public Long2ObjectOpenHashMapWrapper(Map map) { ++ backingMap = map; ++ } ++ ++ @Override ++ public V put(Long key, V value) { ++ return backingMap.put(key, value); ++ } ++ ++ @Override ++ public V get(Object key) { ++ return backingMap.get(key); ++ } ++ ++ @Override ++ public V remove(Object key) { ++ return backingMap.remove(key); ++ } ++ ++ @Nullable ++ @Override ++ public V putIfAbsent(Long key, V value) { ++ return backingMap.putIfAbsent(key, value); ++ } ++ ++ @Override ++ public int size() { ++ return backingMap.size(); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index ae4ebf509837e8d44255781c61d02873f8b74be8..312edb4c47a404c1d20e6bdf748a4ccb49a330f4 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -333,6 +333,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function serverFactory) { + ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system +@@ -1342,6 +1343,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + this.executeBlocking(() -> { +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 17a158ff6ce6520b69a5a0032ba4c05449dd0cf8..d62f7375394409a278bc565c8263506c598ceeba 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -236,6 +236,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now ++ gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish ++ gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); +@@ -356,6 +358,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + DedicatedServer.LOGGER.info("JMX monitoring enabled"); + } + ++ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) mobSpawnExecutor.start(); // Pufferfish + return true; + } + } +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 1c87904bb99cc40bafc9357fb2fc1703b759c3df..aea9a45c0916501f71018d3250b56da435f5664e 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -183,6 +183,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + // Paper end - chunk tick iteration optimisations + + ++ public boolean firstRunSpawnCounts = true; // Pufferfish ++ public final java.util.concurrent.atomic.AtomicBoolean _pufferfish_spawnCountsReady = new java.util.concurrent.atomic.AtomicBoolean(false); // Pufferfish - optimize countmobs ++ + public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory) { + this.level = world; + this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(world); +@@ -511,6 +514,43 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + this.broadcastChangedChunks(gameprofilerfiller); + gameprofilerfiller.pop(); + } ++ ++ // Pufferfish start - optimize mob spawning ++ if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { ++ for (ServerPlayer player : this.level.players) { ++ // Paper start - per player mob spawning backoff ++ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) { ++ player.mobCounts[ii] = 0; ++ ++ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm? ++ if (newBackoff < 0) { ++ newBackoff = 0; ++ } ++ player.mobBackoffCounts[ii] = newBackoff; ++ } ++ // Paper end - per player mob spawning backoff ++ } ++ if (firstRunSpawnCounts) { ++ firstRunSpawnCounts = false; ++ _pufferfish_spawnCountsReady.set(true); ++ } ++ if (_pufferfish_spawnCountsReady.getAndSet(false)) { ++ net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { ++ int mapped = distanceManager.getNaturalSpawnChunkCount(); ++ ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator objectiterator = ++ level.entityTickList.entities.iterator(ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); ++ try { ++ gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = ++ new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); ++ lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); ++ } finally { ++ objectiterator.finishedIterating(); ++ } ++ _pufferfish_spawnCountsReady.set(true); ++ }); ++ } ++ } ++ // Pufferfish end + } + + private void broadcastChangedChunks(ProfilerFiller profiler) { +@@ -560,6 +600,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + final int naturalSpawnChunkCount = j; + NaturalSpawner.SpawnState spawnercreature_d; // moved down + if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) { // Pufferfish - moved down when async processing + // re-set mob counts + for (ServerPlayer player : this.level.players) { + // Paper start - per player mob spawning backoff +@@ -574,13 +615,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + // Paper end - per player mob spawning backoff + } +- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); ++ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); // Pufferfish - async mob spawning ++ } // Pufferfish - (endif) moved down when async processing + } else { +- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ // Pufferfish start - async mob spawning ++ lastSpawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); ++ _pufferfish_spawnCountsReady.set(true); ++ // Pufferfish end + } + // Paper end - Optional per player mob spawns + +- this.lastSpawnState = spawnercreature_d; ++ // this.lastSpawnState = spawnercreature_d; // Pufferfish - this is managed asynchronously + profiler.popPush("spawnAndTick"); + boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit + int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); +@@ -597,7 +642,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + // Paper end - PlayerNaturallySpawnCreaturesEvent + boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit + +- list1 = NaturalSpawner.getFilteredSpawningCategories(spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1, this.level); // CraftBukkit ++ list1 = NaturalSpawner.getFilteredSpawningCategories(lastSpawnState, this.spawnFriendlies, this.spawnEnemies, flag1, this.level); // CraftBukkit // Pufferfish + } else { + list1 = List.of(); + } +@@ -609,8 +654,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + ChunkPos chunkcoordintpair = chunk.getPos(); + + chunk.incrementInhabitedTime(timeDelta); +- if (!list1.isEmpty() && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot +- NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, list1); ++ if (!list1.isEmpty() && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && (!gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot // Pufferfish ++ NaturalSpawner.spawnForChunk(this.level, chunk, lastSpawnState, list1); // Pufferfish + } + + if (true) { // Paper - rewrite chunk system +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 103e2c414780be66324bcb9cd4ea539bbdfe12ad..c563326d3131bc726c7f43311c3eaa82131c6745 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -207,6 +207,7 @@ public class ServerEntity { + boolean flag5 = i < -32768L || i > 32767L || j < -32768L || j > 32767L || k < -32768L || k > 32767L; + + if (!this.forceStateResync && !flag5 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()) { // Paper - fix desync when a player is added to the tracker ++ if (flag2 || flag3 || this.entity instanceof AbstractArrow) { // Pufferfish + if ((!flag2 || !flag) && !(this.entity instanceof AbstractArrow)) { + if (flag2) { + packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) i), (short) ((int) j), (short) ((int) k), this.entity.onGround()); +@@ -220,6 +221,7 @@ public class ServerEntity { + flag3 = true; + flag4 = true; + } ++ } // Pufferfish + } else { + this.wasOnGround = this.entity.onGround(); + this.teleportDelay = 0; +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 1f898500d0e9b18a880645ceb0a8ff0fe75f4e48..fe295515b8043d988eb87c2caf516a4ae4169451 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -793,6 +793,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + this.entityTickList.forEach((entity) -> { ++ entity.activatedPriorityReset = false; // Pufferfish - DAB + if (!entity.isRemoved()) { + if (!tickratemanager.isEntityFrozen(entity)) { + gameprofilerfiller.push("checkDespawn"); +@@ -810,7 +811,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + gameprofilerfiller.push("tick"); +- this.guardEntityTick(this::tickNonPassenger, entity); ++ // Pufferfish start - copied from this.guardEntityTick ++ try { ++ this.tickNonPassenger(entity); // Pufferfish - changed ++ } catch (Throwable throwable) { ++ if (throwable instanceof ThreadDeath) throw throwable; // Paper ++ // Paper start - Prevent tile entity and entity crashes ++ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); ++ MinecraftServer.LOGGER.error(msg, throwable); ++ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ // Paper end ++ } ++ this.moonrise$midTickTasks(); // Paper - rewrite chunk system ++ // Pufferfish end + gameprofilerfiller.pop(); + } + } +@@ -931,7 +945,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("thunder"); +- if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && simpleRandom.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder // Paper - optimise random ticking ++ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*simpleRandom.nextInt(this.spigotConfig.thunderChance) == 0*/ chunk.shouldDoLightning(this.simpleRandom)) { // Spigot // Paper - Option to disable thunder // Paper - optimise random ticking // Pufferfish - replace random with shouldDoLightning + BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); + + if (this.isRainingAt(blockposition)) { +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 84fa24880d02dc7ba1ec8bda3575be38447fd4b2..dedd8b3644699c4bdb33c9a7046342b620889b87 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1226,6 +1226,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + @Override + public void handleEditBook(ServerboundEditBookPacket packet) { ++ if (!gg.pufferfish.pufferfish.PufferfishConfig.enableBooks && !this.player.getBukkitEntity().hasPermission("pufferfish.usebooks")) return; // Pufferfish + // Paper start - Book size limits + final io.papermc.paper.configuration.type.number.IntOr.Disabled pageMax = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax; + if (!this.cserver.isPrimaryThread() && pageMax.enabled()) { +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 1b547be0fe97119edf4f29666cfe0037e0c778e0..0348a458493c4fe22552ae2404272dd9b989d53e 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -389,6 +389,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public boolean freezeLocked = false; // Paper - Freeze Tick Lock API + public boolean fixedPose = false; // Paper - Expand Pose API + private final int despawnTime; // Paper - entity despawn time limit ++ public boolean activatedPriorityReset = false; // Pufferfish - DAB ++ public int activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; // Pufferfish - DAB (golf score) ++ public final BlockPos.MutableBlockPos cachedBlockPos = new BlockPos.MutableBlockPos(); // Pufferfish - reduce entity allocations + + public void setOrigin(@javax.annotation.Nonnull Location location) { + this.origin = location.toVector(); +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index d23914a3ab3723d532ae867db6b954c843030f75..8c1dad4b7f6461aca5ac37a25c880c550b0dcf5f 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -385,6 +385,7 @@ public class EntityType implements FeatureElement, EntityTypeT + private final int clientTrackingRange; + private final int updateInterval; + private final String descriptionId; ++ public boolean dabEnabled = false; // Pufferfish + @Nullable + private Component description; + private final Optional> lootTable; +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 96b4fbe4a4655777ff10b32e3257e2fac2aba12a..ee2c88638f058172ef730de9b112ce6506211d3b 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -465,7 +465,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + + if (world1 instanceof ServerLevel) { + worldserver1 = (ServerLevel) world1; +- if (this.isInWall()) { ++ if (shouldCheckForSuffocation() && this.isInWall()) { // Pufferfish - optimize suffocation + this.hurtServer(worldserver1, this.damageSources().inWall(), 1.0F); + } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { + double d1 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); +@@ -564,6 +564,19 @@ public abstract class LivingEntity extends Entity implements Attackable { + gameprofilerfiller.pop(); + } + ++ // Pufferfish start - optimize suffocation ++ public boolean couldPossiblyBeHurt(float amount) { ++ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && amount <= this.lastHurt) { ++ return false; ++ } ++ return true; ++ } ++ ++ public boolean shouldCheckForSuffocation() { ++ return !gg.pufferfish.pufferfish.PufferfishConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F)); ++ } ++ // Pufferfish end ++ + @Override + protected float getBlockSpeedFactor() { + return Mth.lerp((float) this.getAttributeValue(Attributes.MOVEMENT_EFFICIENCY), super.getBlockSpeedFactor(), 1.0F); +@@ -2135,6 +2148,20 @@ public abstract class LivingEntity extends Entity implements Attackable { + return this.lastClimbablePos; + } + ++ ++ // Pufferfish start ++ private boolean cachedOnClimable = false; ++ private BlockPos lastClimbingPosition = null; ++ ++ public boolean onClimableCached() { ++ if (!this.blockPosition().equals(this.lastClimbingPosition)) { ++ this.cachedOnClimable = this.onClimbable(); ++ this.lastClimbingPosition = this.blockPosition(); ++ } ++ return this.cachedOnClimable; ++ } ++ // Pufferfish end ++ + public boolean onClimbable() { + if (this.isSpectator()) { + return false; +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 5a0b51342f4a646101f4588697bcae7d1ca8a010..8ebe26c46db485ee0bdf64a313681d465051f436 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -231,14 +231,16 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + return this.lookControl; + } + ++ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking + // Paper start + @Override + public void inactiveTick() { + super.inactiveTick(); +- if (this.goalSelector.inactiveTick()) { ++ boolean isThrottled = gg.pufferfish.pufferfish.PufferfishConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking ++ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking + this.goalSelector.tick(); + } +- if (this.targetSelector.inactiveTick()) { ++ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority + this.targetSelector.tick(); + } + } +@@ -927,16 +929,20 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + if (i % 2 != 0 && this.tickCount > 1) { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + } else { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tick(); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tick(); + gameprofilerfiller.pop(); + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index fb967ac7b3e7828301f08a7fe9b039441cf7da30..b4cfae23709b7c3aed28317846e37c9311c948c1 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -23,9 +23,11 @@ public class AttributeMap { + private final Set attributesToSync = new ObjectOpenHashSet<>(); + private final Set attributesToUpdate = new ObjectOpenHashSet<>(); + private final AttributeSupplier supplier; ++ private final java.util.function.Function, AttributeInstance> createInstance; // Pufferfish + + public AttributeMap(AttributeSupplier defaultAttributes) { + this.supplier = defaultAttributes; ++ this.createInstance = attributex -> this.supplier.createInstance(this::onAttributeModified, attributex); // Pufferfish + } + + private void onAttributeModified(AttributeInstance instance) { +@@ -47,9 +49,10 @@ public class AttributeMap { + return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable()).collect(Collectors.toList()); + } + ++ + @Nullable + public AttributeInstance getInstance(Holder attribute) { +- return this.attributes.computeIfAbsent(attribute, attributex -> this.supplier.createInstance(this::onAttributeModified, attributex)); ++ return this.attributes.computeIfAbsent(attribute, this.createInstance); // Pufferfish - cache lambda, as for some reason java allocates it anyways + } + + public boolean hasAttribute(Holder attribute) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +index 758f62416ca9c02351348ac0d41deeb4624abc0e..69130969c9a434ec2361e573c9a1ec9f462dfda2 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +@@ -36,7 +36,11 @@ public class VillagerPanicTrigger extends Behavior { + + @Override + protected void tick(ServerLevel world, Villager entity, long time) { +- if (time % 100L == 0L) { ++ // Pufferfish start ++ if (entity.nextGolemPanic < 0) entity.nextGolemPanic = time + 100; ++ if (--entity.nextGolemPanic < time) { ++ entity.nextGolemPanic = -1; ++ // Pufferfish end + entity.spawnGolemIfNeeded(world, time, 3); + } + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 29ae74339a4831ccef3d01e8054931715ba192ad..5a439e3b0fdc1010884634c1e046e49d8b9aee17 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -38,9 +38,12 @@ public class GoalSelector { + } + + // Paper start - EAR 2 +- public boolean inactiveTick() { ++ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start ++ if (inactive && !gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled) tickRate = 4; // reset to Paper's ++ tickRate = Math.min(tickRate, 3); + this.curRate++; +- return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ return this.curRate % tickRate == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ // Pufferfish end + } + public boolean hasTasks() { + for (WrappedGoal task : this.availableGoals) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +index aee0147649d458b87d92496eda0c1723ebe570d2..89e9ea999d2fbd81a1d74382ef3fcd675fc8b94e 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +@@ -121,6 +121,7 @@ public abstract class MoveToBlockGoal extends Goal { + for (int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { + for (int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { + mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); ++ if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Pufferfish - if this block isn't loaded, continue + if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { + this.blockPos = mutableBlockPos; + this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper +diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +index 60c2868f255d372226e0c1389caaa5477bbef41e..3de177a40649183b5b210e5f0c610a527287e9ec 100644 +--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java ++++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +@@ -242,13 +242,22 @@ public class Bat extends AmbientCreature { + } + } + ++ // Pufferfish start - only check for spooky season once an hour ++ private static boolean isSpookySeason = false; ++ private static final int ONE_HOUR = 20 * 60 * 60; ++ private static int lastSpookyCheck = -ONE_HOUR; + private static boolean isHalloween() { ++ if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); + int j = localdate.get(ChronoField.MONTH_OF_YEAR); + +- return j == 10 && i >= 20 || j == 11 && i <= 3; ++ isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3; ++ lastSpookyCheck = net.minecraft.server.MinecraftServer.currentTick; ++ } ++ return isSpookySeason; + } ++ // Pufferfish end + + private void setupAnimationStates() { + if (this.isResting()) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +index b86f638390d386c838318a4d9b6571ac5514df8f..f4788104b1bb73810fdf0dc7f5311d5b078a81d5 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +@@ -223,11 +223,13 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + return 0.4F; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("allayBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + gameprofilerfiller.push("allayActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 31b10cd404b672d7ce21c2107d8f83e32de26ef4..cb47876a13cb1990bb0ab4cff1bbe57b3b2d0a5e 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -292,11 +292,13 @@ public class Axolotl extends Animal implements VariantHolder, B + return true; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("axolotlBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + gameprofilerfiller.push("axolotlActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +index ca04e5d829331551a2c2f44e223ff05c6ce04e76..db91b8018591fe248efda417fcde7fd2071c4cb6 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java +@@ -184,10 +184,12 @@ public class Frog extends Animal implements VariantHolder> { + .ifPresent(this::setVariant); + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("frogBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + profilerFiller.pop(); + profilerFiller.push("frogActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +index 48ac8c3f6e00c3c2dc67b6c994be7c0ac6dfcf81..cf326ef35bac732e7addf75537963593d5b268ae 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -83,11 +83,13 @@ public class Tadpole extends AbstractFish { + return SoundEvents.TADPOLE_FLOP; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("tadpoleBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + gameprofilerfiller.push("tadpoleActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +index 76aca47d8638d5c37c57d3a59fa7f8ceaa5a53b4..fb92cd4b0c15b614c0c06d2867039aee1a6212a2 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +@@ -192,11 +192,13 @@ public class Goat extends Animal { + return (Brain) super.getBrain(); // CraftBukkit - decompile error + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("goatBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goatActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +index bd9e10f79eaf0d23908229b3ebc2227946a14843..2ce65ef77e4b28e1fd5ac7bd6a304cc115b8aca2 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -151,6 +151,13 @@ public class WitherBoss extends Monster implements RangedAttackMob { + this.bossEvent.setName(this.getDisplayName()); + } + ++ // Pufferfish start - optimize suffocation ++ @Override ++ public boolean shouldCheckForSuffocation() { ++ return true; ++ } ++ // Pufferfish end ++ + @Override + protected SoundEvent getAmbientSound() { + return SoundEvents.WITHER_AMBIENT; +diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +index 2a394381a4ad46359359ba402b65c62b331480b4..4e4dab5bc202f6f421dcff98f0e36e8e70378b49 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java ++++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +@@ -306,11 +306,17 @@ public class EnderMan extends Monster implements NeutralMob { + private boolean teleport(double x, double y, double z) { + BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z); + +- while (blockposition_mutableblockposition.getY() > this.level().getMinY() && !this.level().getBlockState(blockposition_mutableblockposition).blocksMotion()) { ++ // Pufferfish start - single chunk lookup ++ net.minecraft.world.level.chunk.LevelChunk chunk = this.level().getChunkIfLoaded(blockposition_mutableblockposition); ++ if (chunk == null) { ++ return false; ++ } ++ // Pufferfish end ++ while (blockposition_mutableblockposition.getY() > this.level().getMinY() && !chunk.getBlockState(blockposition_mutableblockposition).blocksMotion()) { // Pufferfish + blockposition_mutableblockposition.move(Direction.DOWN); + } + +- BlockState iblockdata = this.level().getBlockState(blockposition_mutableblockposition); ++ BlockState iblockdata = chunk.getBlockState(blockposition_mutableblockposition); // Pufferfish + boolean flag = iblockdata.blocksMotion(); + boolean flag1 = iblockdata.getFluidState().is(FluidTags.WATER); + +diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 92270912ef26924f611a1df7cb3d5b485b0a262d..9c20651b74157582e60793ceba8adde2c354f2a8 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -138,11 +138,13 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + return (Brain) super.getBrain(); // CraftBukkit - decompile error + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("hoglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + HoglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index 2121d2a2e1aa1d0f0390cc515317096431f6dcb0..74ab50723056fef2a96dcc9e2de0e58526738011 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -307,11 +307,13 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + return !this.cannotHunt; + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("piglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + PiglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +index c47ed605f0822effd58df4f875297ed015e1e57e..4331ada8bed7ade7b53fd8ba000c1c1b34fa4331 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +@@ -275,11 +275,13 @@ public class Warden extends Monster implements VibrationSystem { + + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("wardenBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(world, this); + gameprofilerfiller.pop(); + super.customServerAiStep(world); +diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java +index 2d8ba55906c8da16fde850e3412f4a6bda3d56e7..3fd86782134a674f58ef37288c8963a4a92f685c 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -142,6 +142,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return holder.is(PoiTypes.MEETING); + }); + ++ public long nextGolemPanic = -1; // Pufferfish ++ + public Villager(EntityType entityType, Level world) { + this(entityType, world, VillagerType.PLAINS); + } +@@ -245,6 +247,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Spigot End + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep(ServerLevel world) { + // Paper start - EAR 2 +@@ -255,7 +258,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + ProfilerFiller gameprofilerfiller = Profiler.get(); + + gameprofilerfiller.push("villagerBrain"); +- if (!inactive) this.getBrain().tick(world, this); ++ // Pufferfish start ++ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { ++ this.getBrain().tick((ServerLevel) this.level(), this); // Paper ++ } ++ // Pufferfish end + gameprofilerfiller.pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +diff --git a/src/main/java/net/minecraft/world/entity/player/Inventory.java b/src/main/java/net/minecraft/world/entity/player/Inventory.java +index 110456deaa662bc1c0f6ba7878bb3074869a4350..58c8e8f06f5cf028b158350327bf42984fcb4d38 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Inventory.java ++++ b/src/main/java/net/minecraft/world/entity/player/Inventory.java +@@ -636,6 +636,8 @@ public class Inventory implements Container, Nameable { + } + + public boolean contains(ItemStack stack) { ++ // Pufferfish start - don't allocate iterators ++ /* + Iterator iterator = this.compartments.iterator(); + + while (iterator.hasNext()) { +@@ -650,6 +652,18 @@ public class Inventory implements Container, Nameable { + } + } + } ++ */ ++ for (int i = 0; i < this.compartments.size(); i++) { ++ List list = this.compartments.get(i); ++ for (int j = 0; j < list.size(); j++) { ++ ItemStack itemstack1 = list.get(j); ++ ++ if (!itemstack1.isEmpty() && ItemStack.isSameItemSameComponents(itemstack1, stack)) { ++ return true; ++ } ++ } ++ } ++ // Pufferfish end + + return false; + } +diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +index 9a7b56b653848974e1194eb4f6d40cb99a96ff57..593b343b7cd5a24e34ad19a9e93eb9258f862357 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -58,6 +58,36 @@ public abstract class Projectile extends Entity implements TraceableEntity { + super(type, world); + } + ++ // Pufferfish start ++ private static int loadedThisTick = 0; ++ private static int loadedTick; ++ ++ private int loadedLifetime = 0; ++ @Override ++ public void setPos(double x, double y, double z) { ++ int currentTick = net.minecraft.server.MinecraftServer.currentTick; ++ if (loadedTick != currentTick) { ++ loadedTick = currentTick; ++ loadedThisTick = 0; ++ } ++ int previousX = Mth.floor(this.getX()) >> 4, previousZ = Mth.floor(this.getZ()) >> 4; ++ int newX = Mth.floor(x) >> 4, newZ = Mth.floor(z) >> 4; ++ if (previousX != newX || previousZ != newZ) { ++ boolean isLoaded = ((net.minecraft.server.level.ServerChunkCache) this.level().getChunkSource()).getChunkAtIfLoadedImmediately(newX, newZ) != null; ++ if (!isLoaded) { ++ if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { ++ if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { ++ this.discard(); ++ } ++ return; ++ } ++ Projectile.loadedThisTick++; ++ } ++ } ++ super.setPos(x, y, z); ++ } ++ // Pufferfish end ++ + public void setOwner(@Nullable Entity entity) { + if (entity != null) { + this.ownerUUID = entity.getUUID(); +diff --git a/src/main/java/net/minecraft/world/item/EndCrystalItem.java b/src/main/java/net/minecraft/world/item/EndCrystalItem.java +index b62db8c7c8c57e43869ee239ebf4b02f112355d9..2bee342e59e600426c8681a3ce641a12f22790be 100644 +--- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java ++++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java +@@ -57,7 +57,7 @@ public class EndCrystalItem extends Item { + world.gameEvent((Entity) context.getPlayer(), (Holder) GameEvent.ENTITY_PLACE, blockposition1); + EndDragonFight enderdragonbattle = ((ServerLevel) world).getDragonFight(); + +- if (enderdragonbattle != null) { ++ if (enderdragonbattle != null && gg.pufferfish.pufferfish.PufferfishConfig.allowEndCrystalRespawn) { // Pufferfish + enderdragonbattle.tryRespawn(aboveBlockPosition); // Paper - Perf: Do crystal-portal proximity check before entity lookup + } + } +diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +index 12f95bee2a69fd5df7c4a165537e01299e60c5f6..4b3769020a92124bd5677cf75e268b8bc5ba2031 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +@@ -31,8 +31,13 @@ public class ShapelessRecipe implements CraftingRecipe { + final List ingredients; + @Nullable + private PlacementInfo placementInfo; ++ private final boolean isBukkit; // Pufferfish + ++ // Pufferfish start + public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, List ingredients) { ++ this(group, category, result, ingredients, false); ++ } ++ public ShapelessRecipe(String group, CraftingBookCategory category, ItemStack result, List ingredients, boolean isBukkit) { this.isBukkit = isBukkit; // Pufferfish end + this.group = group; + this.category = category; + this.result = result; +@@ -80,6 +85,27 @@ public class ShapelessRecipe implements CraftingRecipe { + } + + public boolean matches(CraftingInput input, Level world) { ++ // Pufferfish start ++ if (!this.isBukkit) { ++ java.util.List ingredients = com.google.common.collect.Lists.newArrayList(this.ingredients.toArray(new Ingredient[0])); ++ ++ inventory: for (int index = 0; index < input.size(); index++) { ++ ItemStack itemStack = input.getItem(index); ++ ++ if (!itemStack.isEmpty()) { ++ for (int i = 0; i < ingredients.size(); i++) { ++ if (ingredients.get(i).test(itemStack)) { ++ ingredients.remove(i); ++ continue inventory; ++ } ++ } ++ return false; ++ } ++ } ++ ++ return ingredients.isEmpty(); ++ } ++ // Pufferfish end + // Paper start - Improve exact choice recipe ingredients & unwrap ternary + if (input.ingredientCount() != this.ingredients.size()) { + return false; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 27f9d167b5ae9ce5117798ea44324107df59425f..6470f145e2470574a40ddce6ca5bf924c1bb094c 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -1488,16 +1488,16 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + public void guardEntityTick(Consumer tickConsumer, T entity) { + try { + tickConsumer.accept(entity); +- } catch (Throwable throwable) { ++ } catch (Throwable throwable) { // Pufferfish - diff on change ServerLevel.tick + if (throwable instanceof ThreadDeath) throw throwable; // Paper + // Paper start - Prevent block entity and entity crashes + final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); + MinecraftServer.LOGGER.error(msg, throwable); + getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); // Paper - ServerExceptionEvent +- entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Pufferfish - diff on change ServerLevel.tick + // Paper end - Prevent block entity and entity crashes + } +- this.moonrise$midTickTasks(); // Paper - rewrite chunk system ++ this.moonrise$midTickTasks(); // Paper - rewrite chunk system // Pufferfish - diff on change ServerLevel.tick + } + // Paper start - Option to prevent armor stands from doing entity lookups + @Override +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 97937e3bd211997f0a0a3e9e671a1c59712d0003..99a95d77f44dddfd7be6c2ebd60e827d9de956b7 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -89,6 +89,18 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + private final LevelChunkTicks fluidTicks; + private LevelChunk.UnsavedListener unsavedListener; + ++ // Pufferfish start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively ++ private int lightningTick; ++ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline ++ public final boolean shouldDoLightning(net.minecraft.util.RandomSource random) { ++ if (this.lightningTick-- <= 0) { ++ this.lightningTick = random.nextInt(this.level.spigotConfig.thunderChance) << 1; ++ return true; ++ } ++ return false; ++ } ++ // Pufferfish end ++ + public LevelChunk(Level world, ChunkPos pos) { + this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); + } +@@ -122,6 +134,8 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + this.debug = !empty && this.level.isDebug(); + this.defaultBlockState = empty ? VOID_AIR_BLOCKSTATE : AIR_BLOCKSTATE; + // Paper end - get block chunk optimisation ++ ++ this.lightningTick = new java.util.Random().nextInt(100000) << 1; // Pufferfish - initialize lightning tick + } + + // CraftBukkit start +diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +index d8b4196adf955f8d414688dc451caac2d9c609d9..80a43def4912a3228cd95117d5c2aac68798b4ec 100644 +--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java ++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +@@ -9,7 +9,7 @@ import javax.annotation.Nullable; + import net.minecraft.world.entity.Entity; + + public class EntityTickList { +- private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system ++ public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system // Pufferfish - private->public + + private void ensureActiveIsNotIterated() { + // Paper - rewrite chunk system +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +index 7c989318dc7ad89bb0d9143fcaac1e4bba6f5907..143a4d4efcc989ed4a4c73cc304e1978ad8f0699 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +@@ -44,6 +44,6 @@ public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe + data.add(this.toNMS(i, true)); + } + +- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data))); ++ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data, true))); // Pufferfish + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 15892c7769caa15f3d52a1ee2147cf9615aa0e25..6ef651e6dc16bad45aeb76f8e17b97871c506a64 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -505,7 +505,7 @@ public final class CraftMagicNumbers implements UnsafeValues { + // Paper start + @Override + public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { +- return new com.destroystokyo.paper.PaperVersionFetcher(); ++ return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +index 774556a62eb240da42e84db4502e2ed43495be17..80553face9c70c2a3d897681e7761df85b22d464 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +@@ -11,7 +11,7 @@ public final class Versioning { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + +- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/io.papermc.paper/paper-api/pom.properties"); ++ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish + Properties properties = new Properties(); + + if (stream != null) { +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 1d438ef44cbe4d1eedfba36d8fe5d2ad53464921..bc525c7843b9cc0f7705c0dc6795c05f4e5b4bb1 100644 +--- a/src/main/java/org/spigotmc/ActivationRange.java ++++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -37,6 +37,10 @@ import net.minecraft.world.entity.projectile.ThrownTrident; + import net.minecraft.world.entity.raid.Raider; + import net.minecraft.world.level.Level; + import net.minecraft.world.phys.AABB; ++// Pufferfish start ++import net.minecraft.world.phys.Vec3; ++import java.util.List; ++// Pufferfish end + + public class ActivationRange + { +@@ -221,6 +225,25 @@ public class ActivationRange + } + // Paper end - Configurable marker ticking + ActivationRange.activateEntity(entity); ++ ++ // Pufferfish start ++ if (gg.pufferfish.pufferfish.PufferfishConfig.dearEnabled && entity.getType().dabEnabled) { ++ if (!entity.activatedPriorityReset) { ++ entity.activatedPriorityReset = true; ++ entity.activatedPriority = gg.pufferfish.pufferfish.PufferfishConfig.maximumActivationPrio; ++ } ++ Vec3 playerVec = player.position(); ++ Vec3 entityVec = entity.position(); ++ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; ++ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); ++ entity.activatedPriority = squaredDistance > gg.pufferfish.pufferfish.PufferfishConfig.startDistanceSquared ? ++ Math.max(1, Math.min(squaredDistance >> gg.pufferfish.pufferfish.PufferfishConfig.activationDistanceMod, entity.activatedPriority)) : ++ 1; ++ } else { ++ entity.activatedPriority = 1; ++ } ++ // Pufferfish end ++ + } + // Paper end + } +@@ -236,12 +259,12 @@ public class ActivationRange + if ( MinecraftServer.currentTick > entity.activatedTick ) + { + if ( entity.defaultActivationState ) +- { ++ { // Pufferfish - diff on change + entity.activatedTick = MinecraftServer.currentTick; + return; + } + if ( entity.activationType.boundingBox.intersects( entity.getBoundingBox() ) ) +- { ++ { // Pufferfish - diff on change + entity.activatedTick = MinecraftServer.currentTick; + } + } +@@ -295,7 +318,7 @@ public class ActivationRange + if ( entity instanceof LivingEntity ) + { + LivingEntity living = (LivingEntity) entity; +- if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing()) // Paper ++ if ( living.onClimableCached() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing() ) // Paper // Pufferfish - use cached + { + return 1; // Paper + } diff --git a/patches/unapplied-server/0002-Fix-pufferfish-issues.patch b/patches/unapplied-server/0002-Fix-pufferfish-issues.patch new file mode 100644 index 000000000..def3bace7 --- /dev/null +++ b/patches/unapplied-server/0002-Fix-pufferfish-issues.patch @@ -0,0 +1,147 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Tue, 4 Jan 2022 23:05:41 -0600 +Subject: [PATCH] Fix pufferfish issues + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 96d831791edbe6ae07325008b760f70f75c4d713..83e2c8784eaa77851e9efc6b889d673c1fd046d0 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -63,7 +63,7 @@ dependencies { + + // Pufferfish start + implementation("org.yaml:snakeyaml:1.32") +- implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { ++ implementation ("com.github.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { // Purpur - Fix pufferfish issues + exclude(group="org.yaml", module="snakeyaml") + } + // Pufferfish end +diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +index f5a43a1e1a78b3eaabbcadc7af09750ee478eeb6..3ff4f092a59242a8cb930c084915a774db881652 100644 +--- a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java ++++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java +@@ -25,6 +25,7 @@ public class PufferfishConfig { + + private static final YamlFile config = new YamlFile(); + private static int updates = 0; ++ public static File pufferfishFile; // Purpur - Fix pufferfish issues + + private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { + ConfigurationSection newSection = new MemoryConfiguration(); +@@ -47,7 +48,7 @@ public class PufferfishConfig { + } + + public static void load() throws IOException { +- File configFile = new File("pufferfish.yml"); ++ File configFile = pufferfishFile; // Purpur - Fix pufferfish issues + + if (configFile.exists()) { + try { +@@ -229,7 +230,7 @@ public class PufferfishConfig { + public static int activationDistanceMod; + + private static void dynamicActivationOfBrains() throws IOException { +- dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", true); ++ dearEnabled = getBoolean("dab.enabled", "activation-range.enabled", false); // Purpur - Fix pufferfish issues + startDistance = getInt("dab.start-distance", "activation-range.start-distance", 12, + "This value determines how far away an entity has to be", + "from the player to start being effected by DEAR."); +@@ -256,7 +257,7 @@ public class PufferfishConfig { + + public static boolean throttleInactiveGoalSelectorTick; + private static void inactiveGoalSelectorThrottle() { +- throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true, ++ throttleInactiveGoalSelectorTick = getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", false, // Purpur - Fix pufferfish issues + "Throttles the AI goal selector in entity inactive ticks.", + "This can improve performance by a few percent, but has minor gameplay implications."); + } +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +index 790bad0494454ca12ee152e3de6da3da634d9b20..19d602e4e6fa5abf59559eab4132677b09a967a1 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +@@ -31,6 +31,7 @@ public record ServerBuildInfoImpl( + private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit"; + + private static final String BRAND_PAPER_NAME = "Paper"; ++ private static final String BRAND_PUFFERFISH_NAME = "Pufferfish"; // Purpur - Fix pufferfish issues + + private static final String BUILD_DEV = "DEV"; + +@@ -42,9 +43,9 @@ public record ServerBuildInfoImpl( + this( + getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) + .map(Key::key) +- .orElse(BRAND_PAPER_ID), ++ .orElse(BRAND_PUFFERFISH_ID), // Purpur - Fix pufferfish issues + getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) +- .orElse(BRAND_PAPER_NAME), ++ .orElse(BRAND_PUFFERFISH_NAME), // Purpur - Fix pufferfish issues + SharedConstants.getCurrentVersion().getId(), + SharedConstants.getCurrentVersion().getName(), + getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) +@@ -61,7 +62,7 @@ public record ServerBuildInfoImpl( + + @Override + public boolean isBrandCompatible(final @NotNull Key brandId) { +- return brandId.equals(this.brandId); ++ return brandId.equals(this.brandId) || brandId.equals(BRAND_PAPER_ID); // Purpur - Fix pufferfish issues + } + + @Override +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index d62f7375394409a278bc565c8263506c598ceeba..8ccf355a2541c8fb725c312c6955bb6cf624ff0f 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -236,6 +236,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now ++ gg.pufferfish.pufferfish.PufferfishConfig.pufferfishFile = (java.io.File) options.valueOf("pufferfish-settings"); // Purpur - Fix pufferfish issues + gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish + gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +index 593b343b7cd5a24e34ad19a9e93eb9258f862357..80ff364514d071d1862c4b3aa5d01b9c39062946 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -77,7 +77,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { + if (!isLoaded) { + if (Projectile.loadedThisTick > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerTick) { + if (++this.loadedLifetime > gg.pufferfish.pufferfish.PufferfishConfig.maxProjectileLoadsPerProjectile) { +- this.discard(); ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Purpur - Fix pufferfish issues + } + return; + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index 99a95d77f44dddfd7be6c2ebd60e827d9de956b7..00ea1c2037c7c7780764bfcc3e07b6554e910db2 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -135,7 +135,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + this.defaultBlockState = empty ? VOID_AIR_BLOCKSTATE : AIR_BLOCKSTATE; + // Paper end - get block chunk optimisation + +- this.lightningTick = new java.util.Random().nextInt(100000) << 1; // Pufferfish - initialize lightning tick ++ this.lightningTick = java.util.concurrent.ThreadLocalRandom.current().nextInt(100000) << 1; // Pufferfish - initialize lightning tick // Purpur - any random will do - Fix pufferfish issues + } + + // CraftBukkit start +diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java +index 1c2439ffc1e407ff69286817d22f127470ce07ba..d74f885fcdf686359f761aa7356fe03cf293ee59 100644 +--- a/src/main/java/org/bukkit/craftbukkit/Main.java ++++ b/src/main/java/org/bukkit/craftbukkit/Main.java +@@ -176,6 +176,13 @@ public class Main { + .describedAs("Jar file"); + // Paper end + ++ // Purpur start - Fix pufferfish issues ++ acceptsAll(asList("pufferfish", "pufferfish-settings"), "File for pufferfish settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("pufferfish.yml")) ++ .describedAs("Yml file"); ++ // Purpur end - Fix pufferfish issues + // Paper start + acceptsAll(asList("server-name"), "Name of the server") + .withRequiredArg() diff --git a/purpur-api/build.gradle.kts.patch b/purpur-api/build.gradle.kts.patch new file mode 100644 index 000000000..29b8a2f14 --- /dev/null +++ b/purpur-api/build.gradle.kts.patch @@ -0,0 +1,55 @@ +--- a/paper-api/build.gradle.kts ++++ b/paper-api/build.gradle.kts +@@ -91,7 +_,7 @@ + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + } + +-val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() ++val generatedDir: java.nio.file.Path = rootProject.layout.projectDirectory.dir("paper-api/src/generated/java").asFile.toPath() + idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) +@@ -101,6 +_,18 @@ + main { + java { + srcDir(generatedDir) ++ srcDir(file("../paper-api/src/main/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/main/resources")) ++ } ++ } ++ test { ++ java { ++ srcDir(file("../paper-api/src/test/java")) ++ } ++ resources { ++ srcDir(file("../paper-api/src/test/resources")) + } + } + } +@@ -182,8 +_,9 @@ + val services = objects.newInstance() + + tasks.withType().configureEach { ++ (options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet") // Purpur - silence Paper's bajillion javadoc warnings + val options = options as StandardJavadocDocletOptions +- options.overview = "src/main/javadoc/overview.html" ++ options.overview = "../paper-api/src/main/javadoc/overview.html" + options.use() + options.isDocFilesSubDirs = true + options.links( +@@ -216,11 +_,11 @@ + } + + // workaround for https://github.com/gradle/gradle/issues/4046 +- inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset") ++ inputs.dir("../paper-api/src/main/javadoc").withPropertyName("javadoc-sourceset") + val fsOps = services.fileSystemOperations + doLast { + fsOps.copy { +- from("src/main/javadoc") { ++ from("../paper-api/src/main/javadoc") { + include("**/doc-files/**") + } + into("build/docs/javadoc") diff --git a/purpur-api/paper-patches/features/0001-Rebrand.patch b/purpur-api/paper-patches/features/0001-Rebrand.patch new file mode 100644 index 000000000..9153a5d0c --- /dev/null +++ b/purpur-api/paper-patches/features/0001-Rebrand.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: granny +Date: Thu, 16 May 2024 19:11:29 -0700 +Subject: [PATCH] Rebrand + + +diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java +index 652ff54e7c50412503725d628bfe72ed03059790..fb1fe2651e53a9bf46b3632c638e13eea9dcda93 100644 +--- a/src/main/java/io/papermc/paper/ServerBuildInfo.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java +@@ -19,6 +19,12 @@ public interface ServerBuildInfo { + */ + Key BRAND_PAPER_ID = Key.key("papermc", "paper"); + ++ // Purpur start ++ /** ++ * The brand id for Purpur. ++ */ ++ Key BRAND_PURPUR_ID = Key.key("purpurmc", "purpur"); ++ // Purpur end + /** + * Gets the {@code ServerBuildInfo}. + * diff --git a/patches/api/0003-Purpur-config-files.patch b/purpur-api/paper-patches/features/0002-Purpur-config-files.patch similarity index 85% rename from patches/api/0003-Purpur-config-files.patch rename to purpur-api/paper-patches/features/0002-Purpur-config-files.patch index 52ccd2d70..aac57e6d6 100644 --- a/patches/api/0003-Purpur-config-files.patch +++ b/purpur-api/paper-patches/features/0002-Purpur-config-files.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Purpur config files diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 8d8fe04e6b09d2a5b1cc05002073df5c58cdcb96..c71576fea7e0e7c3ad54912ed61d1ff20aaea4c2 100644 +index bd8123503132b742d873c18486c3d19024fb9898..a17790d2da3008927b79814629e073b2091ce421 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java -@@ -2121,6 +2121,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2425,6 +2425,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi } // Paper end diff --git a/patches/api/0055-lightning-transforms-blocks.patch b/purpur-api/paper-patches/features/0003-lightning-transforms-blocks.patch similarity index 66% rename from patches/api/0055-lightning-transforms-blocks.patch rename to purpur-api/paper-patches/features/0003-lightning-transforms-blocks.patch index 40c102857..146455e99 100644 --- a/patches/api/0055-lightning-transforms-blocks.patch +++ b/purpur-api/paper-patches/features/0003-lightning-transforms-blocks.patch @@ -1,42 +1,43 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sun, 27 Aug 2023 02:09:51 -0700 +From: granny +Date: Wed, 17 Dec 2025 20:20:00 -0800 Subject: [PATCH] lightning transforms blocks diff --git a/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java b/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java -index c98f07f82d179dffe162bb5cd85efe97de1b6175..2bed9997f14a42729c90db92fbf202482a94f8be 100644 +index a16aadadae7b70a8f3140478feb8846b7fec13ff..e79db0f2e80fc2a5521f9ccc34009f31c4baa04f 100644 --- a/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java +++ b/src/main/java/org/bukkit/event/weather/LightningStrikeEvent.java -@@ -14,6 +14,7 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { - private boolean canceled; +@@ -16,6 +16,7 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { + private final LightningStrike bolt; private final Cause cause; -+ private final java.util.List blocks; // Purpur ++ private final java.util.List blocks; // Purpur - lightning transforms blocks - @Deprecated - public LightningStrikeEvent(@NotNull final World world, @NotNull final LightningStrike bolt) { -@@ -21,9 +22,15 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { - } + private boolean cancelled; +@@ -27,9 +28,16 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { + + @ApiStatus.Internal public LightningStrikeEvent(@NotNull final World world, @NotNull final LightningStrike bolt, @NotNull final Cause cause) { -+ // Purpur start ++ // Purpur start - lightning transforms blocks + this(world, bolt, cause, new java.util.ArrayList<>()); + } ++ @ApiStatus.Internal + public LightningStrikeEvent(@NotNull final World world, @NotNull final LightningStrike bolt, @NotNull final Cause cause, @NotNull final java.util.List blocks) { -+ // Purpur end ++ // Purpur end - lightning transforms blocks super(world); this.bolt = bolt; this.cause = cause; -+ this.blocks = blocks; // Purpur ++ this.blocks = blocks; // Purpur - lightning transforms blocks } - @Override -@@ -56,6 +63,21 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { - return cause; + /** +@@ -62,6 +70,21 @@ public class LightningStrikeEvent extends WeatherEvent implements Cancellable { + this.cancelled = cancel; } -+ // Purpur start ++ // Purpur start - lightning transforms blocks + /** + * Get a list of all blocks that will be modified by the lightning strike. + *
@@ -49,7 +50,7 @@ index c98f07f82d179dffe162bb5cd85efe97de1b6175..2bed9997f14a42729c90db92fbf20248 + public java.util.List getBlocks() { + return blocks; + } -+ // Purpur end ++ // Purpur end - lightning transforms blocks + @NotNull @Override diff --git a/purpur-api/paper-patches/files/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch b/purpur-api/paper-patches/files/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch new file mode 100644 index 000000000..2d9bb04ae --- /dev/null +++ b/purpur-api/paper-patches/files/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch @@ -0,0 +1,29 @@ +--- a/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java ++++ b/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java +@@ -450,6 +_,26 @@ + + GoalKey ZOMBIE_ATTACK_TURTLE_EGG = create("zombie_attack_turtle_egg", Zombie.class); + ++ // Purpur start - Ridables ++ GoalKey MOB_HAS_RIDER = GoalKey.of(Mob.class, NamespacedKey.minecraft("has_rider")); ++ GoalKey HORSE_HAS_RIDER = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("horse_has_rider")); ++ GoalKey LLAMA_HAS_RIDER = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_has_rider")); ++ // Purpur end - Ridables ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms ++ GoalKey FIND_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("find_crystal")); ++ GoalKey ORBIT_CRYSTAL = GoalKey.of(Phantom.class, NamespacedKey.minecraft("orbit_crystal")); ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms ++ // Purpur start - Add option to disable zombie aggressiveness towards villagers when lagging ++ GoalKey DROWNED_ATTACK_VILLAGER = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack_villager")); ++ GoalKey ZOMBIE_ATTACK_VILLAGER = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_villager")); ++ // Purpur end - Add option to disable zombie aggressiveness towards villagers when lagging ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ GoalKey AVOID_RABID_WOLF = GoalKey.of(Wolf.class, NamespacedKey.minecraft("avoid_rabid_wolf")); ++ // Purpur end - Configurable chance for wolves to spawn rabid ++ // Purpur start - Iron golem poppy calms anger ++ GoalKey RECEIVE_FLOWER = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("receive_flower")); ++ // Purpur end - Iron golem poppy calms anger ++ + private static GoalKey create(final String key, final Class type) { + return GoalKey.of(type, NamespacedKey.minecraft(key)); + } diff --git a/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimedEventExecutor.java.patch b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimedEventExecutor.java.patch new file mode 100644 index 000000000..382cc7915 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimedEventExecutor.java.patch @@ -0,0 +1,14 @@ +--- a/src/main/java/co/aikar/timings/TimedEventExecutor.java ++++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java +@@ -80,9 +_,9 @@ + executor.execute(listener, event); + return; + } +- try (Timing ignored = timings.startTiming()){ ++ //try (Timing ignored = timings.startTiming()){ // Purpur - Remove Timings + executor.execute(listener, event); +- } ++ //} // Purpur - Remove Timings + } + + @Override diff --git a/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timing.java.patch b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timing.java.patch new file mode 100644 index 000000000..d971ba0c2 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timing.java.patch @@ -0,0 +1,48 @@ +--- a/src/main/java/co/aikar/timings/Timing.java ++++ b/src/main/java/co/aikar/timings/Timing.java +@@ -39,6 +_,7 @@ + * @return Timing + */ + @NotNull ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + Timing startTiming(); + + /** +@@ -46,6 +_,7 @@ + * + * Will automatically be called when this Timing is used with try-with-resources + */ ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + void stopTiming(); + + /** +@@ -56,6 +_,7 @@ + * @return Timing + */ + @NotNull ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + Timing startTimingIfSync(); + + /** +@@ -65,12 +_,14 @@ + * + * But only if we are on the primary thread. + */ ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + void stopTimingIfSync(); + + /** + * @deprecated Doesn't do anything - Removed + */ + @Deprecated ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + void abort(); + + /** +@@ -82,5 +_,6 @@ + TimingHandler getTimingHandler(); + + @Override ++ @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings + void close(); + } diff --git a/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timings.java.patch b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timings.java.patch new file mode 100644 index 000000000..beab0b68d --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/Timings.java.patch @@ -0,0 +1,25 @@ +--- a/src/main/java/co/aikar/timings/Timings.java ++++ b/src/main/java/co/aikar/timings/Timings.java +@@ -124,7 +_,7 @@ + @NotNull + public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) { + Timing timing = of(plugin, name, groupHandler); +- timing.startTiming(); ++ //timing.startTiming(); // Purpur - Remove Timings + return timing; + } + +@@ -146,7 +_,7 @@ + */ + public static void setTimingsEnabled(boolean enabled) { + if (enabled && !warnedAboutDeprecationOnEnable) { +- Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); ++ //Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); // Purpur - Remove Timings + warnedAboutDeprecationOnEnable = true; + } + } +@@ -322,4 +_,3 @@ + return TimingsManager.getHandler(groupName, name, groupHandler); + } + } +- diff --git a/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimingsCommand.java.patch b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimingsCommand.java.patch new file mode 100644 index 000000000..96b13d99c --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/co/aikar/timings/TimingsCommand.java.patch @@ -0,0 +1,34 @@ +--- a/src/main/java/co/aikar/timings/TimingsCommand.java ++++ b/src/main/java/co/aikar/timings/TimingsCommand.java +@@ -47,7 +_,7 @@ + public TimingsCommand(@NotNull String name) { + super(name); + this.description = "Manages Spigot Timings data to see performance of the server."; +- this.usageMessage = "/timings "; ++ this.usageMessage = "/timings";// "; // Purpur - Remove Timings + this.setPermission("bukkit.command.timings"); + } + +@@ -57,7 +_,12 @@ + return true; + } + if (true) { +- sender.sendMessage(Timings.deprecationMessage()); ++ // Purpur start - Remove Timings ++ net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(); ++ sender.sendMessage(mm.deserialize("Purpur has removed timings to save your performance. Please use /spark instead")); ++ sender.sendMessage(mm.deserialize("For more information, view its documentation at")); ++ sender.sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage")); ++ // Purpur end - Remove Timings + return true; + } + if (args.length < 1) { +@@ -118,7 +_,7 @@ + Preconditions.checkNotNull(args, "Arguments cannot be null"); + Preconditions.checkNotNull(alias, "Alias cannot be null"); + +- if (args.length == 1) { ++ if (false && args.length == 1) { // Purpur - Remove Timings + return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, + new ArrayList(TIMINGS_SUBCOMMANDS.size())); + } diff --git a/purpur-api/paper-patches/files/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java.patch b/purpur-api/paper-patches/files/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java.patch new file mode 100644 index 000000000..25a7de1a8 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java.patch @@ -0,0 +1,15 @@ +--- a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java ++++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java +@@ -40,6 +_,12 @@ + return getVersionMessage(); + } + ++ // Purpur start ++ default int distance() { ++ return 0; ++ } ++ // Purpur end ++ + /** + * @hidden + */ diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/Bukkit.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Bukkit.java.patch new file mode 100644 index 000000000..20a83c6cf --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Bukkit.java.patch @@ -0,0 +1,150 @@ +--- a/src/main/java/org/bukkit/Bukkit.java ++++ b/src/main/java/org/bukkit/Bukkit.java +@@ -3003,4 +_,147 @@ + public static void restart() { + server.restart(); + } ++ ++ // Purpur start - Bring back server name ++ /** ++ * Get the name of this server ++ * @return the name of the server ++ */ ++ @NotNull ++ public static String getServerName() { ++ return server.getServerName(); ++ } ++ // Purpur end - Bring back server name ++ ++ // Purpur start - Lagging threshold ++ /** ++ * Check if server is lagging according to laggy threshold setting ++ * ++ * @return True if lagging ++ */ ++ public static boolean isLagging() { ++ return server.isLagging(); ++ } ++ // Purpur end - Lagging threshold ++ ++ // Purpur start - Added the ability to add combustible items ++ /** ++ * Add an Item as fuel for furnaces ++ * ++ * @param material The material that will be the fuel ++ * @param burnTime The time (in ticks) this item will burn for ++ */ ++ public static void addFuel(@NotNull Material material, int burnTime) { ++ server.addFuel(material, burnTime); ++ } ++ ++ /** ++ * Remove an item as fuel for furnaces ++ * ++ * @param material The material that will no longer be a fuel ++ */ ++ public static void removeFuel(@NotNull Material material) { ++ server.removeFuel(material); ++ } ++ // Purpur end - Added the ability to add combustible items ++ ++ // Purpur start - Debug Marker API ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration) { ++ server.sendBlockHighlight(location, duration); ++ } ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration, int argb) { ++ server.sendBlockHighlight(location, duration, argb); ++ } ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text) { ++ server.sendBlockHighlight(location, duration, text); ++ } ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb) { ++ server.sendBlockHighlight(location, duration, text, argb); ++ } ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency) { ++ server.sendBlockHighlight(location, duration, color, transparency); ++ } ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency) { ++ server.sendBlockHighlight(location, duration, text, color, transparency); ++ } ++ ++ /** ++ * Clears all debug block highlights for all players on the server. ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ public static void clearBlockHighlights() { ++ server.clearBlockHighlights(); ++ } ++ // Purpur end - Debug Marker API + } diff --git a/patches/api/0020-ChatColor-conveniences.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch similarity index 76% rename from patches/api/0020-ChatColor-conveniences.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch index 1cbcc2653..a9da296b0 100644 --- a/patches/api/0020-ChatColor-conveniences.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch @@ -1,27 +1,11 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 10 Jul 2020 12:43:25 -0500 -Subject: [PATCH] ChatColor conveniences - - -diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java -index 918a045165cdcde264bc24082b7afebb407271de..687d11619379aead7f665d4a5f8f8bcc857cb8e9 100644 --- a/src/main/java/org/bukkit/ChatColor.java +++ b/src/main/java/org/bukkit/ChatColor.java -@@ -3,6 +3,7 @@ package org.bukkit; - import com.google.common.base.Preconditions; - import com.google.common.collect.Maps; - import java.util.Map; -+import java.util.regex.Matcher; - import java.util.regex.Pattern; - import org.jetbrains.annotations.Contract; - import org.jetbrains.annotations.NotNull; -@@ -456,4 +457,77 @@ public enum ChatColor { +@@ -456,4 +_,77 @@ BY_CHAR.put(color.code, color); } } + -+ // Purpur start ++ // Purpur start - ChatColor conveniences + /** + * Convert legacy string into a string ready to be parsed by MiniMessage + * @@ -31,7 +15,7 @@ index 918a045165cdcde264bc24082b7afebb407271de..687d11619379aead7f665d4a5f8f8bcc + @NotNull + public static String toMM(@NotNull String str) { + StringBuilder sb = new StringBuilder(str); -+ Matcher m = STRIP_COLOR_PATTERN.matcher(sb); ++ java.util.regex.Matcher m = STRIP_COLOR_PATTERN.matcher(sb); + while (m.find()) { + sb.replace(m.start(), m.end(), sb.substring(m.start(), m.end()).toLowerCase()); + } @@ -92,5 +76,5 @@ index 918a045165cdcde264bc24082b7afebb407271de..687d11619379aead7f665d4a5f8f8bcc + public static String color(@Nullable String str, boolean parseHex) { + return str != null ? net.md_5.bungee.api.ChatColor.translateAlternateColorCodes('&', parseHex ? replaceHex(str) : str) : str; + } -+ // Purpur end ++ // Purpur end - ChatColor conveniences } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/Material.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Material.java.patch new file mode 100644 index 000000000..02f7f5abb --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Material.java.patch @@ -0,0 +1,43 @@ +--- a/src/main/java/org/bukkit/Material.java ++++ b/src/main/java/org/bukkit/Material.java +@@ -3708,4 +_,40 @@ + return this.asItemType().getDefaultDataTypes(); + } + // Paper end - data component API ++ ++ // Purpur start - ItemStack convenience methods ++ public boolean isArmor() { ++ switch (this) { ++ // ++ case LEATHER_BOOTS: ++ case LEATHER_CHESTPLATE: ++ case LEATHER_HELMET: ++ case LEATHER_LEGGINGS: ++ case CHAINMAIL_BOOTS: ++ case CHAINMAIL_CHESTPLATE: ++ case CHAINMAIL_HELMET: ++ case CHAINMAIL_LEGGINGS: ++ case IRON_BOOTS: ++ case IRON_CHESTPLATE: ++ case IRON_HELMET: ++ case IRON_LEGGINGS: ++ case GOLDEN_BOOTS: ++ case GOLDEN_CHESTPLATE: ++ case GOLDEN_HELMET: ++ case GOLDEN_LEGGINGS: ++ case DIAMOND_BOOTS: ++ case DIAMOND_CHESTPLATE: ++ case DIAMOND_HELMET: ++ case DIAMOND_LEGGINGS: ++ case NETHERITE_BOOTS: ++ case NETHERITE_CHESTPLATE: ++ case NETHERITE_HELMET: ++ case NETHERITE_LEGGINGS: ++ case TURTLE_HELMET: ++ return true; ++ default: ++ return false; ++ } ++ } ++ // Purpur end - ItemStack convenience methods + } diff --git a/patches/api/0036-Extended-OfflinePlayer-API.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch similarity index 61% rename from patches/api/0036-Extended-OfflinePlayer-API.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch index 467cdf300..47eb17201 100644 --- a/patches/api/0036-Extended-OfflinePlayer-API.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch @@ -1,17 +1,9 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Sun, 22 Aug 2021 05:11:09 +0200 -Subject: [PATCH] Extended OfflinePlayer API - - -diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java -index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a322acadecc 100644 --- a/src/main/java/org/bukkit/OfflinePlayer.java +++ b/src/main/java/org/bukkit/OfflinePlayer.java -@@ -522,4 +522,114 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio - */ - @Nullable - public Location getLastDeathLocation(); +@@ -592,4 +_,104 @@ + default void applySkinToPlayerHeadContents(final PlayerHeadObjectContents.@NonNull Builder builder) { + builder.id(this.getUniqueId()); + } + + // Purpur start - OfflinePlayer API + /** @@ -20,7 +12,7 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * + * @return True if the player is allowed to fly. + */ -+ public boolean getAllowFlight(); ++ boolean getAllowFlight(); + + /** + * Sets if the OfflinePlayer is allowed to fly via jump key double-tap like in @@ -28,21 +20,21 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * + * @param flight If flight should be allowed. + */ -+ public void setAllowFlight(boolean flight); ++ void setAllowFlight(boolean flight); + + /** + * Checks to see if this player is currently flying or not. + * + * @return True if the player is flying, else false. + */ -+ public boolean isFlying(); ++ boolean isFlying(); + + /** + * Makes this player start or stop flying. + * + * @param value True to fly. + */ -+ public void setFlying(boolean value); ++ void setFlying(boolean value); + + /** + * Sets the speed at which a client will fly. Negative values indicate @@ -52,7 +44,7 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @throws IllegalArgumentException If new speed is less than -1 or + * greater than 1 + */ -+ public void setFlySpeed(float value) throws IllegalArgumentException; ++ void setFlySpeed(float value) throws IllegalArgumentException; + + /** + * Sets the speed at which a client will walk. Negative values indicate @@ -62,29 +54,21 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @throws IllegalArgumentException If new speed is less than -1 or + * greater than 1 + */ -+ public void setWalkSpeed(float value) throws IllegalArgumentException; ++ void setWalkSpeed(float value) throws IllegalArgumentException; + + /** + * Gets the current allowed speed that a client can fly. + * + * @return The current allowed speed, from -1 to 1 + */ -+ public float getFlySpeed(); ++ float getFlySpeed(); + + /** + * Gets the current allowed speed that a client can walk. + * + * @return The current allowed speed, from -1 to 1 + */ -+ public float getWalkSpeed(); -+ -+ /** -+ * Gets the entity's current position -+ * -+ * @return a new copy of Location containing the position of this offline player -+ */ -+ @Nullable -+ public Location getLocation(); ++ float getWalkSpeed(); + + /** + * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleport implementation. @@ -92,7 +76,7 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @param destination + * @return true if teleportation was successful + */ -+ public boolean teleportOffline(@NotNull org.bukkit.Location destination); ++ boolean teleportOffline(org.bukkit.Location destination); + + /** + * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleport implementation. @@ -101,7 +85,7 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @param cause Teleport cause used if player is online + * @return true if teleportation was successful + */ -+ public boolean teleportOffline(@NotNull org.bukkit.Location destination, @NotNull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); ++ boolean teleportOffline(org.bukkit.Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); + + /** + * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleportAsync implementation. @@ -109,8 +93,7 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @param destination + * @return true if teleportation successful + */ -+ @NotNull -+ public java.util.concurrent.CompletableFuture teleportOfflineAsync(@NotNull Location destination); ++ java.util.concurrent.CompletableFuture teleportOfflineAsync(Location destination); + + /** + * Sets OfflinePlayer's location. If player is online, it falls back to the Player#teleportAsync implementation. @@ -119,7 +102,6 @@ index bce07d84cafca677bb6fad78c21b82097f06430c..4ef0fa4f1ef72bb784674671473c6a32 + * @param cause Teleport cause used if player is online + * @return true if teleportation successful + */ -+ @NotNull -+ public java.util.concurrent.CompletableFuture teleportOfflineAsync(@NotNull Location destination, @NotNull org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); ++ java.util.concurrent.CompletableFuture teleportOfflineAsync(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause); + // Purpur end - OfflinePlayer API } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch new file mode 100644 index 000000000..80e794576 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch @@ -0,0 +1,128 @@ +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2761,4 +_,125 @@ + */ + void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value); + // Paper end - API to check if the server is sleeping ++ ++ // Purpur start - Bring back server name ++ /** ++ * Get the name of this server ++ * @return the name of the server ++ */ ++ @NotNull ++ String getServerName(); ++ // Purpur end - Bring back server name ++ ++ // Purpur start - Lagging threshold ++ /** ++ * Check if server is lagging according to laggy threshold setting ++ * ++ * @return True if lagging ++ */ ++ boolean isLagging(); ++ // Purpur end - Lagging threshold ++ ++ // Purpur start - Added the ability to add combustible items ++ /** ++ * Add an Item as fuel for furnaces ++ * ++ * @param material The material that will be the fuel ++ * @param burnTime The time (in ticks) this item will burn for ++ */ ++ public void addFuel(@NotNull Material material, int burnTime); ++ ++ /** ++ * Remove an item as fuel for furnaces ++ * ++ * @param material The material that will no longer be a fuel ++ */ ++ public void removeFuel(@NotNull Material material); ++ // Purpur end - Added the ability to add combustible items ++ ++ // Purpur start - Debug Marker API ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on the server. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency); ++ ++ /** ++ * Clears all debug block highlights for all players on the server. ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void clearBlockHighlights(); ++ // Purpur end - Debug Marker API + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/World.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/World.java.patch new file mode 100644 index 000000000..1233b1a23 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/World.java.patch @@ -0,0 +1,103 @@ +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -4497,6 +_,100 @@ + @Nullable + public DragonBattle getEnderDragonBattle(); + ++ // Purpur start ++ /** ++ * Gets the local difficulty (based on inhabited time) at a location ++ * ++ * @param location Location to check ++ * @return The local difficulty ++ */ ++ public float getLocalDifficultyAt(@NotNull Location location); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull org.bukkit.Color color, int transparency); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to all players on this world. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void sendBlockHighlight(@NotNull Location location, int duration, @NotNull String text, @NotNull org.bukkit.Color color, int transparency); ++ ++ /** ++ * Clears all debug block highlights for all players on this world. ++ * @deprecated until further notice. NOOP since 1.21.10 ++ */ ++ @Deprecated ++ void clearBlockHighlights(); ++ // Purpur end ++ + /** + * Get all {@link FeatureFlag} enabled in this world. + * diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/block/EntityBlockStorage.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/block/EntityBlockStorage.java.patch new file mode 100644 index 000000000..94b62abaa --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/block/EntityBlockStorage.java.patch @@ -0,0 +1,28 @@ +--- a/src/main/java/org/bukkit/block/EntityBlockStorage.java ++++ b/src/main/java/org/bukkit/block/EntityBlockStorage.java +@@ -47,6 +_,25 @@ + @NotNull + List releaseEntities(); + ++ // Purpur start - Stored Bee API ++ /** ++ * Releases a stored entity, and returns the entity in the world. ++ * ++ * @param entity Entity to release ++ * @return The entity which was released, or null if the stored entity is not in the hive ++ */ ++ @org.jetbrains.annotations.Nullable ++ T releaseEntity(@NotNull org.purpurmc.purpur.entity.StoredEntity entity); ++ ++ /** ++ * Gets all the entities currently stored in the block. ++ * ++ * @return List of all entities which are stored in the block ++ */ ++ @NotNull ++ List> getEntities(); ++ // Purpur end - Stored Bee API ++ + /** + * Add an entity to the block. + * diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch new file mode 100644 index 000000000..979407b0c --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch @@ -0,0 +1,36 @@ +--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java ++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java +@@ -148,6 +_,19 @@ + return false; + } + ++ // Purpur start - ExecuteCommandEvent ++ String[] parsedArgs = Arrays.copyOfRange(args, 1, args.length); ++ org.purpurmc.purpur.event.ExecuteCommandEvent event = new org.purpurmc.purpur.event.ExecuteCommandEvent(sender, target, sentCommandLabel, parsedArgs); ++ if (!event.callEvent()) { ++ return true; // cancelled ++ } ++ ++ sender = event.getSender(); ++ target = event.getCommand(); ++ sentCommandLabel = event.getLabel(); ++ parsedArgs = event.getArgs(); ++ // Purpur end - ExecuteCommandEvent ++ + // Paper start - Plugins do weird things to workaround normal registration + if (target.timings == null) { + target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); +@@ -155,10 +_,10 @@ + // Paper end + + try { +- try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources ++ //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur - Remove Timings + // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) +- target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length)); +- } // target.timings.stopTiming(); // Spigot // Paper ++ target.execute(sender, sentCommandLabel, parsedArgs); // Purpur - ExecuteCommandEvent ++ //} // target.timings.stopTiming(); // Spigot // Paper // Purpur - Remove Timings + } catch (CommandException ex) { + server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper + //target.timings.stopTiming(); // Spigot // Paper diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch new file mode 100644 index 000000000..1f09b0518 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch @@ -0,0 +1,26 @@ +--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java ++++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java +@@ -214,7 +_,7 @@ + String version = Bukkit.getVersion(); + // Paper start + if (version.startsWith("null")) { // running from ide? +- setVersionMessage(Component.text("Unknown version, custom build?", NamedTextColor.YELLOW)); ++ setVersionMessage(Component.text("* Unknown version, custom build?", NamedTextColor.RED)); // Purpur + return; + } + setVersionMessage(getVersionFetcher().getVersionMessage(version)); +@@ -255,9 +_,11 @@ + // Paper start + private void setVersionMessage(final @NotNull Component msg) { + lastCheck = System.currentTimeMillis(); +- final Component message = Component.textOfChildren( +- Component.text(Bukkit.getVersionMessage(), NamedTextColor.WHITE), +- Component.newline(), ++ // Purpur start ++ int distance = getVersionFetcher().distance(); ++ final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), ++ ChatColor.parseMM("Current Purpur Version: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), ++ // Purpur end + msg + ); + this.versionMessage = Component.text() diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch new file mode 100644 index 000000000..23ee8f4b5 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch @@ -0,0 +1,33 @@ +--- a/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java ++++ b/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java +@@ -169,6 +_,30 @@ + public boolean includes(@NotNull Material item) { + return Tag.ITEMS_ENCHANTABLE_VANISHING.isTagged(item); + } ++ // Purpur start - Add enchantment target for bows and crossbows ++ }, ++ ++ /** ++ * Allow the Enchantment to be placed on bows and crossbows. ++ */ ++ BOW_AND_CROSSBOW { ++ @Override ++ public boolean includes(@NotNull Material item) { ++ return item.equals(Material.BOW) || item.equals(Material.CROSSBOW); ++ } ++ // Purpur end - Add enchantment target for bows and crossbows ++ // Purpur start - Shears can have looting enchantment ++ }, ++ ++ /** ++ * Allow the Enchantment to be placed on shears. ++ */ ++ WEAPON_AND_SHEARS { ++ @Override ++ public boolean includes(@NotNull Material item) { ++ return WEAPON.includes(item) || item.equals(Material.SHEARS); ++ } ++ // Purpur end - Shears can have looting enchantment + }; + + /** diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/CopperGolem.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/CopperGolem.java.patch new file mode 100644 index 000000000..31f8abbab --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/CopperGolem.java.patch @@ -0,0 +1,25 @@ +--- a/src/main/java/org/bukkit/entity/CopperGolem.java ++++ b/src/main/java/org/bukkit/entity/CopperGolem.java +@@ -39,6 +_,22 @@ + */ + void setOxidizing(Oxidizing oxidizing); + ++ // Purpur start ++ /** ++ * Get the player that summoned this iron golem ++ * ++ * @return UUID of summoner ++ */ ++ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); ++ ++ /** ++ * Set the player that summoned this iron golem ++ * ++ * @param summoner UUID of summoner ++ */ ++ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); ++ // Purpur end ++ + /** + * Represents the oxidizing state of a copper golem. + * diff --git a/patches/api/0032-Add-back-player-spawned-endermite-API.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Endermite.java.patch similarity index 70% rename from patches/api/0032-Add-back-player-spawned-endermite-API.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Endermite.java.patch index 1a57eb065..b2f7a4584 100644 --- a/patches/api/0032-Add-back-player-spawned-endermite-API.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Endermite.java.patch @@ -1,14 +1,6 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 26 Jun 2021 22:08:57 -0500 -Subject: [PATCH] Add back player spawned endermite API - - -diff --git a/src/main/java/org/bukkit/entity/Endermite.java b/src/main/java/org/bukkit/entity/Endermite.java -index 138d2530de2410f4a9424dabd3e5ce0cd1c1dcd2..10a8d64ad2da0be2c14f34c3e7d1957c6f2883d1 100644 --- a/src/main/java/org/bukkit/entity/Endermite.java +++ b/src/main/java/org/bukkit/entity/Endermite.java -@@ -3,25 +3,21 @@ package org.bukkit.entity; +@@ -3,25 +_,21 @@ public interface Endermite extends Monster { /** @@ -21,7 +13,7 @@ index 138d2530de2410f4a9424dabd3e5ce0cd1c1dcd2..10a8d64ad2da0be2c14f34c3e7d1957c * @return player spawned status - * @deprecated this functionality no longer exists */ -- @Deprecated +- @Deprecated(since = "1.17", forRemoval = true) boolean isPlayerSpawned(); /** @@ -34,7 +26,7 @@ index 138d2530de2410f4a9424dabd3e5ce0cd1c1dcd2..10a8d64ad2da0be2c14f34c3e7d1957c * @param playerSpawned player spawned status - * @deprecated this functionality no longer exists */ -- @Deprecated +- @Deprecated(since = "1.17", forRemoval = true) void setPlayerSpawned(boolean playerSpawned); // Paper start /** diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Entity.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Entity.java.patch new file mode 100644 index 000000000..72cd7af9e --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Entity.java.patch @@ -0,0 +1,62 @@ +--- a/src/main/java/org/bukkit/entity/Entity.java ++++ b/src/main/java/org/bukkit/entity/Entity.java +@@ -1322,4 +_,59 @@ + */ + void broadcastHurtAnimation(@NotNull java.util.Collection players); + // Paper end - broadcast hurt animation ++ ++ // Purpur start - Ridables ++ /** ++ * Get the riding player ++ * ++ * @return Riding player ++ */ ++ @Nullable ++ Player getRider(); ++ ++ /** ++ * Check if entity is being ridden ++ * ++ * @return True if being ridden ++ */ ++ boolean hasRider(); ++ ++ /** ++ * Check if entity is ridable ++ * ++ * @return True if ridable ++ */ ++ boolean isRidable(); ++ ++ /** ++ * Check if entity is ridable in water ++ * ++ * @return True if ridable in water ++ */ ++ boolean isRidableInWater(); ++ // Purpur end - Ridables ++ ++ // Purpur start - API for any mob to burn daylight ++ /** ++ * Checks if the entity is in daylight ++ * ++ * @return True if in daylight ++ */ ++ boolean isInDaylight(); ++ // Purpur end - API for any mob to burn daylight ++ ++ // Purpur start - Fire Immunity API ++ /** ++ * Checks if the entity is fire immune ++ * ++ * @return True if fire immune ++ */ ++ boolean isImmuneToFire(); ++ ++ /** ++ * Sets if the entity is fire immune ++ * Set this to null to restore the entity type default ++ */ ++ void setImmuneToFire(@Nullable Boolean fireImmune); ++ // Purpur end - Fire Immunity API + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/IronGolem.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/IronGolem.java.patch new file mode 100644 index 000000000..8a25a65b2 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/IronGolem.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/IronGolem.java ++++ b/src/main/java/org/bukkit/entity/IronGolem.java +@@ -19,4 +_,20 @@ + * player created, false if you want it to be a natural village golem. + */ + public void setPlayerCreated(boolean playerCreated); ++ ++ // Purpur start ++ /** ++ * Get the player that summoned this iron golem ++ * ++ * @return UUID of summoner ++ */ ++ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); ++ ++ /** ++ * Set the player that summoned this iron golem ++ * ++ * @param summoner UUID of summoner ++ */ ++ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); ++ // Purpur end + } diff --git a/patches/api/0022-Item-entity-immunities.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Item.java.patch similarity index 76% rename from patches/api/0022-Item-entity-immunities.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Item.java.patch index 9fd92a3af..294924ed9 100644 --- a/patches/api/0022-Item-entity-immunities.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Item.java.patch @@ -1,14 +1,6 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sat, 22 Aug 2020 17:42:08 -0500 -Subject: [PATCH] Item entity immunities - - -diff --git a/src/main/java/org/bukkit/entity/Item.java b/src/main/java/org/bukkit/entity/Item.java -index 58017fce436cdbda255f7172fbdadb726d4b113c..05600fc8bf2a61aca8094029bc4c208a710da952 100644 --- a/src/main/java/org/bukkit/entity/Item.java +++ b/src/main/java/org/bukkit/entity/Item.java -@@ -153,4 +153,62 @@ public interface Item extends Entity, io.papermc.paper.entity.Frictional { // Pa +@@ -153,4 +_,62 @@ */ public void setHealth(int health); // Paper end diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/LivingEntity.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/LivingEntity.java.patch new file mode 100644 index 000000000..092e2189b --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/LivingEntity.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/LivingEntity.java ++++ b/src/main/java/org/bukkit/entity/LivingEntity.java +@@ -1494,4 +_,20 @@ + */ + @ApiStatus.Experimental + @NotNull CombatTracker getCombatTracker(); ++ ++ // Purpur start - API for any mob to burn daylight ++ /** ++ * If this mob will burn in the sunlight ++ * ++ * @return True if mob will burn in sunlight ++ */ ++ boolean shouldBurnInDay(); ++ ++ /** ++ * Set if this mob should burn in the sunlight ++ * ++ * @param shouldBurnInDay True to burn in sunlight ++ */ ++ void setShouldBurnInDay(boolean shouldBurnInDay); ++ // Purpur end - API for any mob to burn daylight + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Llama.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Llama.java.patch new file mode 100644 index 000000000..43e170b4b --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Llama.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/Llama.java ++++ b/src/main/java/org/bukkit/entity/Llama.java +@@ -119,4 +_,20 @@ + @org.jetbrains.annotations.Nullable + Llama getCaravanTail(); + // Paper end ++ ++ // Purpur start ++ /** ++ * Check if this Llama should attempt to join a caravan ++ * ++ * @return True if Llama is allowed to join a caravan ++ */ ++ boolean shouldJoinCaravan(); ++ ++ /** ++ * Set if this Llama should attempt to join a caravan ++ * ++ * @param shouldJoinCaravan True to allow joining a caravan ++ */ ++ void setShouldJoinCaravan(boolean shouldJoinCaravan); ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch new file mode 100644 index 000000000..95b49e83f --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch @@ -0,0 +1,126 @@ +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -4004,4 +_,123 @@ + */ + @ApiStatus.Experimental + PlayerGameConnection getConnection(); ++ ++ // Purpur start ++ /** ++ * Allows you to get if player uses PurpurClient ++ * ++ * @return true if player uses PurpurClient ++ */ ++ boolean usesPurpurClient(); ++ ++ /** ++ * Check if player is AFK ++ * ++ * @return True if AFK ++ */ ++ boolean isAfk(); ++ ++ /** ++ * Set player as AFK ++ * ++ * @param setAfk Whether to set AFK or not ++ */ ++ void setAfk(boolean setAfk); ++ ++ /** ++ * Reset the idle timer back to 0 ++ * @deprecated Use {@link #resetIdleDuration()} instead ++ */ ++ void resetIdleTimer(); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ */ ++ void sendBlockHighlight(Location location, int duration); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ */ ++ void sendBlockHighlight(Location location, int duration, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ */ ++ void sendBlockHighlight(Location location, int duration, String text); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param argb Color of the highlight. ARGB int. Will be ignored on some versions of vanilla client ++ */ ++ void sendBlockHighlight(Location location, int duration, String text, int argb); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ */ ++ void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency); ++ ++ /** ++ * Creates debug block highlight on specified block location and show it to this player. ++ *

++ * Clients may be inconsistent in displaying it. ++ * @param location Location to highlight ++ * @param duration Duration for highlight to show in milliseconds ++ * @param text Text to show above the highlight ++ * @param color Color of the highlight. Will be ignored on some versions of vanilla client ++ * @param transparency Transparency of the highlight ++ * @throws IllegalArgumentException If transparency is outside 0-255 range ++ */ ++ void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency); ++ ++ /** ++ * Clears all debug block highlights ++ */ ++ void clearBlockHighlights(); ++ ++ /** ++ * Sends a player the death screen with a specified death message. ++ * ++ * @param message The death message to show the player ++ */ ++ void sendDeathScreen(net.kyori.adventure.text.Component message); ++ ++ /** ++ * Sends a player the death screen with a specified death message, ++ * along with the entity that caused the death. ++ * ++ * @param message The death message to show the player ++ * @param killer The entity that killed the player ++ * @deprecated Use {@link #sendDeathScreen(net.kyori.adventure.text.Component)} instead, as 1.20 removed the killer ID from the packet. ++ */ ++ @Deprecated(since = "1.20") ++ default void sendDeathScreen(net.kyori.adventure.text.Component message, @Nullable Entity killer) { ++ sendDeathScreen(message); ++ } ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Snowman.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Snowman.java.patch new file mode 100644 index 000000000..a24330d2d --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Snowman.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/Snowman.java ++++ b/src/main/java/org/bukkit/entity/Snowman.java +@@ -23,4 +_,20 @@ + * @param derpMode True to remove the pumpkin, false to add a pumpkin + */ + void setDerp(boolean derpMode); ++ ++ // Purpur start ++ /** ++ * Get the player that summoned this snowman ++ * ++ * @return UUID of summoner ++ */ ++ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); ++ ++ /** ++ * Set the player that summoned this snowman ++ * ++ * @param summoner UUID of summoner ++ */ ++ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Villager.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Villager.java.patch new file mode 100644 index 000000000..446119a47 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Villager.java.patch @@ -0,0 +1,16 @@ +--- a/src/main/java/org/bukkit/entity/Villager.java ++++ b/src/main/java/org/bukkit/entity/Villager.java +@@ -406,4 +_,13 @@ + * Demand is still updated even if all events are canceled. + */ + public void restock(); ++ ++ // Purpur start ++ /** ++ * Check if villager is currently lobotomized ++ * ++ * @return True if lobotomized ++ */ ++ boolean isLobotomized(); ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wither.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wither.java.patch new file mode 100644 index 000000000..e2f1daf25 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wither.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/Wither.java ++++ b/src/main/java/org/bukkit/entity/Wither.java +@@ -105,4 +_,20 @@ + * This is called in vanilla directly after spawning the wither. + */ + void enterInvulnerabilityPhase(); ++ ++ // Purpur start ++ /** ++ * Get the player that summoned this wither ++ * ++ * @return UUID of summoner ++ */ ++ @org.jetbrains.annotations.Nullable java.util.UUID getSummoner(); ++ ++ /** ++ * Set the player that summoned this wither ++ * ++ * @param summoner UUID of summoner ++ */ ++ void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner); ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wolf.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wolf.java.patch new file mode 100644 index 000000000..201466d6b --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Wolf.java.patch @@ -0,0 +1,23 @@ +--- a/src/main/java/org/bukkit/entity/Wolf.java ++++ b/src/main/java/org/bukkit/entity/Wolf.java +@@ -162,4 +_,20 @@ + return RegistryAccess.registryAccess().getRegistry(RegistryKey.WOLF_SOUND_VARIANT).getOrThrow(NamespacedKey.minecraft(key)); + } + } ++ ++ // Purpur start ++ /** ++ * Checks if this wolf is rabid ++ * ++ * @return whether the wolf is rabid ++ */ ++ boolean isRabid(); ++ ++ /** ++ * Sets this wolf to be rabid or not ++ * ++ * @param rabid whether the wolf should be rabid ++ */ ++ void setRabid(boolean rabid); ++ // Purpur end + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java.patch new file mode 100644 index 000000000..9796956cd --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java.patch @@ -0,0 +1,12 @@ +--- a/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java ++++ b/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java +@@ -310,7 +_,8 @@ + WORLD_BORDER, + /** + * Damage caused when an entity contacts a block such as a Cactus, +- * Dripstone (Stalagmite) or Berry Bush. ++ * Dripstone (Stalagmite) or Berry Bush. (Stonecutters too if you ++ * have the Stonecutter damage Purpur feature enabled) + *

+ * Damage: variable + */ diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java.patch new file mode 100644 index 000000000..2676d9629 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java.patch @@ -0,0 +1,15 @@ +--- a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java ++++ b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java +@@ -218,6 +_,12 @@ + * When all effects are removed due to a bucket of milk. + */ + MILK, ++ // Purpur start ++ /** ++ * When a player wears full netherite armor ++ */ ++ NETHERITE_ARMOR, ++ // Purpur end + /** + * When the entity gets the effect from a nautilus. + */ diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/inventory/InventoryType.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/inventory/InventoryType.java.patch new file mode 100644 index 000000000..77457e035 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/event/inventory/InventoryType.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/event/inventory/InventoryType.java ++++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java +@@ -170,7 +_,7 @@ + SMITHING_NEW(4, "Upgrade Gear", MenuType.SMITHING), + ; + +- private final int size; ++ private int size; @ApiStatus.Internal public void setDefaultSize(int size) { this.size = size; } // Purpur - remove final and add setter + private final String title; + private final MenuType menuType; + private final boolean isCreatable; diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/AnvilInventory.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/AnvilInventory.java.patch new file mode 100644 index 000000000..34a114418 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/AnvilInventory.java.patch @@ -0,0 +1,45 @@ +--- a/src/main/java/org/bukkit/inventory/AnvilInventory.java ++++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java +@@ -138,4 +_,42 @@ + setItem(2, result); + } + // Paper end ++ ++ // Purpur start ++ /** ++ * Gets if the player viewing the anvil inventory can bypass experience cost ++ * ++ * @return whether the player viewing the anvil inventory can bypass the experience cost ++ * @deprecated use {@link AnvilView#canBypassCost()}. ++ */ ++ @Deprecated(forRemoval = true, since = "1.21") ++ boolean canBypassCost(); ++ ++ /** ++ * Set if the player viewing the anvil inventory can bypass the experience cost ++ * ++ * @param bypassCost whether the player viewing the anvil inventory can bypass the experience cost ++ * @deprecated use {@link AnvilView#setBypassCost(boolean)}. ++ */ ++ @Deprecated(forRemoval = true, since = "1.21") ++ void setBypassCost(boolean bypassCost); ++ ++ /** ++ * Gets if the player viewing the anvil inventory can do unsafe enchants ++ * ++ * @return whether the player viewing the anvil inventory can do unsafe enchants ++ * @deprecated use {@link AnvilView#canDoUnsafeEnchants()}. ++ */ ++ @Deprecated(forRemoval = true, since = "1.21") ++ boolean canDoUnsafeEnchants(); ++ ++ /** ++ * Set if the player viewing the anvil inventory can do unsafe enchants ++ * ++ * @param canDoUnsafeEnchants whether the player viewing the anvil inventory can do unsafe enchants ++ * @deprecated use {@link AnvilView#setDoUnsafeEnchants(boolean)}. ++ */ ++ @Deprecated(forRemoval = true, since = "1.21") ++ void setDoUnsafeEnchants(boolean canDoUnsafeEnchants); ++ // Purpur end + } diff --git a/patches/api/0018-ItemStack-convenience-methods.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch similarity index 57% rename from patches/api/0018-ItemStack-convenience-methods.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch index 0b0e94a62..f1144543b 100644 --- a/patches/api/0018-ItemStack-convenience-methods.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch @@ -1,83 +1,25 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 15 Mar 2020 20:52:12 -0500 -Subject: [PATCH] ItemStack convenience methods - - -diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java -index 03b47012447430a350e152920f754d993d4023db..3ad843d519e239430c5f4f5754a8da3026ed0f8e 100644 ---- a/src/main/java/org/bukkit/Material.java -+++ b/src/main/java/org/bukkit/Material.java -@@ -11047,4 +11047,40 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla - public boolean isEnabledByFeature(@NotNull World world) { - return Bukkit.getDataPackManager().isEnabledByFeature(this, world); - } -+ -+ // Purpur start -+ public boolean isArmor() { -+ switch (this) { -+ // -+ case LEATHER_BOOTS: -+ case LEATHER_CHESTPLATE: -+ case LEATHER_HELMET: -+ case LEATHER_LEGGINGS: -+ case CHAINMAIL_BOOTS: -+ case CHAINMAIL_CHESTPLATE: -+ case CHAINMAIL_HELMET: -+ case CHAINMAIL_LEGGINGS: -+ case IRON_BOOTS: -+ case IRON_CHESTPLATE: -+ case IRON_HELMET: -+ case IRON_LEGGINGS: -+ case GOLDEN_BOOTS: -+ case GOLDEN_CHESTPLATE: -+ case GOLDEN_HELMET: -+ case GOLDEN_LEGGINGS: -+ case DIAMOND_BOOTS: -+ case DIAMOND_CHESTPLATE: -+ case DIAMOND_HELMET: -+ case DIAMOND_LEGGINGS: -+ case NETHERITE_BOOTS: -+ case NETHERITE_CHESTPLATE: -+ case NETHERITE_HELMET: -+ case NETHERITE_LEGGINGS: -+ case TURTLE_HELMET: -+ return true; -+ default: -+ return false; -+ } -+ } -+ // Purpur end - } -diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java -index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79bae12d95 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java -@@ -17,6 +17,18 @@ import org.bukkit.inventory.meta.ItemMeta; - import org.bukkit.material.MaterialData; +@@ -24,6 +_,13 @@ + import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -+// Purpur start ++// Purpur start - ItemStack convenience methods +import com.google.common.collect.Multimap; +import java.util.Collection; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.block.data.BlockData; -+import org.bukkit.inventory.meta.BlockDataMeta; -+import org.bukkit.inventory.meta.Repairable; -+import org.bukkit.persistence.PersistentDataContainer; -+import org.bukkit.persistence.PersistentDataHolder; -+import com.destroystokyo.paper.Namespaced; -+// Purpur end ++// Purpur end - ItemStack convenience methods /** * Represents a stack of items. -@@ -986,4 +998,626 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat - return livingEntity.damageItemStack(this, amount); +@@ -1373,4 +_,482 @@ + return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount); } - // Paper end + // Paper end - data component API + -+ // Purpur start ++ // Purpur start - ItemStack convenience methods + /** + * Gets the display name that is set. + *

@@ -88,7 +30,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @NotNull + public String getDisplayName() { -+ return getItemMeta().getDisplayName(); ++ return this.craftDelegate.getDisplayName(); + } + + /** @@ -97,9 +39,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param name the name to set + */ + public void setDisplayName(@Nullable String name) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setDisplayName(name); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setDisplayName(name); + } + + /** @@ -108,7 +48,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has a display name + */ + public boolean hasDisplayName() { -+ return hasItemMeta() && getItemMeta().hasDisplayName(); ++ return this.craftDelegate.hasDisplayName(); + } + + /** @@ -121,7 +61,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @NotNull + public String getLocalizedName() { -+ return getItemMeta().getLocalizedName(); ++ return this.craftDelegate.getLocalizedName(); + } + + /** @@ -130,9 +70,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param name the name to set + */ + public void setLocalizedName(@Nullable String name) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setLocalizedName(name); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setLocalizedName(name); + } + + /** @@ -141,7 +79,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has a localized name + */ + public boolean hasLocalizedName() { -+ return hasItemMeta() && getItemMeta().hasLocalizedName(); ++ return this.craftDelegate.hasLocalizedName(); + } + + /** @@ -150,7 +88,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has lore + */ + public boolean hasLore() { -+ return hasItemMeta() && getItemMeta().hasLore(); ++ return this.craftDelegate.hasLore(); + } + + /** @@ -160,7 +98,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this enchantment exists for this meta + */ + public boolean hasEnchant(@NotNull Enchantment ench) { -+ return hasItemMeta() && getItemMeta().hasEnchant(ench); ++ return this.craftDelegate.hasEnchant(ench); + } + + /** @@ -170,7 +108,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return The level that the specified enchantment has, or 0 if none + */ + public int getEnchantLevel(@NotNull Enchantment ench) { -+ return getItemMeta().getEnchantLevel(ench); ++ return this.craftDelegate.getEnchantLevel(ench); + } + + /** @@ -181,7 +119,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @NotNull + public Map getEnchants() { -+ return getItemMeta().getEnchants(); ++ return this.craftDelegate.getEnchants(); + } + + /** @@ -195,10 +133,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * otherwise + */ + public boolean addEnchant(@NotNull Enchantment ench, int level, boolean ignoreLevelRestriction) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.addEnchant(ench, level, ignoreLevelRestriction); -+ setItemMeta(itemMeta); -+ return result; ++ return this.craftDelegate.addEnchant(ench, level, ignoreLevelRestriction); + } + + /** @@ -209,10 +144,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * otherwise + */ + public boolean removeEnchant(@NotNull Enchantment ench) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.removeEnchant(ench); -+ setItemMeta(itemMeta); -+ return result; ++ return this.craftDelegate.removeEnchant(ench); + } + + /** @@ -221,7 +153,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if an enchantment exists on this meta + */ + public boolean hasEnchants() { -+ return hasItemMeta() && getItemMeta().hasEnchants(); ++ return this.craftDelegate.hasEnchants(); + } + + /** @@ -232,7 +164,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if the enchantment conflicts, false otherwise + */ + public boolean hasConflictingEnchant(@NotNull Enchantment ench) { -+ return hasItemMeta() && getItemMeta().hasConflictingEnchant(ench); ++ return this.craftDelegate.hasConflictingEnchant(ench); + } + + /** @@ -244,9 +176,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param data the data to set, or null to clear + */ + public void setCustomModelData(@Nullable Integer data) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setCustomModelData(data); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setCustomModelData(data); + } + + /** @@ -261,7 +191,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return the localized name that is set + */ + public int getCustomModelData() { -+ return getItemMeta().getCustomModelData(); ++ return this.craftDelegate.getCustomModelData(); + } + + /** @@ -273,7 +203,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has custom model data + */ + public boolean hasCustomModelData() { -+ return hasItemMeta() && getItemMeta().hasCustomModelData(); ++ return this.craftDelegate.hasCustomModelData(); + } + + /** @@ -282,7 +212,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return whether block data is already attached + */ + public boolean hasBlockData() { -+ return hasItemMeta() && ((BlockDataMeta) getItemMeta()).hasBlockData(); ++ return this.craftDelegate.hasBlockData(); + } + + /** @@ -297,7 +227,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @NotNull + public BlockData getBlockData(@NotNull Material material) { -+ return ((BlockDataMeta) getItemMeta()).getBlockData(material); ++ return this.craftDelegate.getBlockData(material); + } + + /** @@ -308,9 +238,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * this item. + */ + public void setBlockData(@NotNull BlockData blockData) { -+ ItemMeta itemMeta = getItemMeta(); -+ ((BlockDataMeta) itemMeta).setBlockData(blockData); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setBlockData(blockData); + } + + /** @@ -319,7 +247,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return the repair penalty + */ + public int getRepairCost() { -+ return ((Repairable) getItemMeta()).getRepairCost(); ++ return this.craftDelegate.getRepairCost(); + } + + /** @@ -328,9 +256,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param cost repair penalty + */ + public void setRepairCost(int cost) { -+ ItemMeta itemMeta = getItemMeta(); -+ ((Repairable) itemMeta).setRepairCost(cost); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setRepairCost(cost); + } + + /** @@ -339,7 +265,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has a repair penalty + */ + public boolean hasRepairCost() { -+ return hasItemMeta() && ((Repairable) getItemMeta()).hasRepairCost(); ++ return this.craftDelegate.hasRepairCost(); + } + + /** @@ -349,7 +275,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if the unbreakable tag is true + */ + public boolean isUnbreakable() { -+ return hasItemMeta() && getItemMeta().isUnbreakable(); ++ return this.craftDelegate.isUnbreakable(); + } + + /** @@ -358,9 +284,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param unbreakable true if set unbreakable + */ + public void setUnbreakable(boolean unbreakable) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setUnbreakable(unbreakable); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setUnbreakable(unbreakable); + } + + /** @@ -369,7 +293,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if any AttributeModifiers exist + */ + public boolean hasAttributeModifiers() { -+ return hasItemMeta() && getItemMeta().hasAttributeModifiers(); ++ return this.craftDelegate.hasAttributeModifiers(); + } + + /** @@ -382,7 +306,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @Nullable + public Multimap getAttributeModifiers() { -+ return getItemMeta().getAttributeModifiers(); ++ return this.craftDelegate.getAttributeModifiers(); + } + + /** @@ -401,7 +325,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @NotNull + public Multimap getAttributeModifiers(@Nullable EquipmentSlot slot) { -+ return getItemMeta().getAttributeModifiers(slot); ++ return this.craftDelegate.getAttributeModifiers(slot); + } + + /** @@ -415,7 +339,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + */ + @Nullable + public Collection getAttributeModifiers(@NotNull Attribute attribute) { -+ return getItemMeta().getAttributeModifiers(attribute); ++ return this.craftDelegate.getAttributeModifiers(attribute); + } + + /** @@ -435,10 +359,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @throws IllegalArgumentException if AttributeModifier already exists + */ + public boolean addAttributeModifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.addAttributeModifier(attribute, modifier); -+ setItemMeta(itemMeta); -+ return result; ++ return this.craftDelegate.addAttributeModifier(attribute, modifier); + } + + /** @@ -452,9 +373,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * and their AttributeModifiers + */ + public void setAttributeModifiers(@Nullable Multimap attributeModifiers) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setAttributeModifiers(attributeModifiers); -+ setItemMeta(itemMeta); ++ this.craftDelegate.setAttributeModifiers(attributeModifiers); + } + + /** @@ -469,10 +388,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @throws NullPointerException if Attribute is null + */ + public boolean removeAttributeModifier(@NotNull Attribute attribute) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.removeAttributeModifier(attribute); -+ setItemMeta(itemMeta); -+ return result; ++ return this.craftDelegate.removeAttributeModifier(attribute); + } + + /** @@ -487,10 +403,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * EquipmentSlot. + */ + public boolean removeAttributeModifier(@Nullable EquipmentSlot slot) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.removeAttributeModifier(slot); -+ setItemMeta(itemMeta); -+ return result; ++ return this.craftDelegate.removeAttributeModifier(slot); + } + + /** @@ -507,24 +420,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @see AttributeModifier#getUniqueId() + */ + public boolean removeAttributeModifier(@NotNull Attribute attribute, @NotNull AttributeModifier modifier) { -+ ItemMeta itemMeta = getItemMeta(); -+ boolean result = itemMeta.removeAttributeModifier(attribute, modifier); -+ setItemMeta(itemMeta); -+ return result; -+ } -+ -+ /** -+ * Returns a custom tag container capable of storing tags on the object. -+ * -+ * Note that the tags stored on this container are all stored under their -+ * own custom namespace therefore modifying default tags using this -+ * {@link PersistentDataHolder} is impossible. -+ * -+ * @return the persistent metadata container -+ */ -+ @NotNull -+ public PersistentDataContainer getPersistentDataContainer() { -+ return getItemMeta().getPersistentDataContainer(); ++ return this.craftDelegate.removeAttributeModifier(attribute, modifier); + } + + /** @@ -533,7 +429,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return true if this has damage + */ + public boolean hasDamage() { -+ return hasItemMeta() && ((Damageable) getItemMeta()).hasDamage(); ++ return this.craftDelegate.hasDamage(); + } + + /** @@ -542,7 +438,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return the damage + */ + public int getDamage() { -+ return ((Damageable) getItemMeta()).getDamage(); ++ return this.craftDelegate.getDamage(); + } + + /** @@ -551,70 +447,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @param damage item damage + */ + public void setDamage(int damage) { -+ ItemMeta itemMeta = getItemMeta(); -+ ((Damageable) itemMeta).setDamage(damage); -+ setItemMeta(itemMeta); -+ } -+ -+ /** -+ * Gets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE} -+ * -+ * @return Set of {@link com.destroystokyo.paper.Namespaced} -+ */ -+ @NotNull -+ public java.util.Set getDestroyableKeys() { -+ return getItemMeta().getDestroyableKeys(); -+ } -+ -+ /** -+ * Sets the collection of namespaced keys that the item can destroy in {@link org.bukkit.GameMode#ADVENTURE} -+ * -+ * @param canDestroy Collection of {@link com.destroystokyo.paper.Namespaced} -+ */ -+ public void setDestroyableKeys(@NotNull Collection canDestroy) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setDestroyableKeys(canDestroy); -+ setItemMeta(itemMeta); -+ } -+ -+ /** -+ * Gets the collection of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE} -+ * -+ * @return Set of {@link com.destroystokyo.paper.Namespaced} -+ */ -+ @NotNull -+ public java.util.Set getPlaceableKeys() { -+ return getItemMeta().getPlaceableKeys(); -+ } -+ -+ /** -+ * Sets the set of namespaced keys that the item can be placed on in {@link org.bukkit.GameMode#ADVENTURE} -+ * -+ * @param canPlaceOn Collection of {@link com.destroystokyo.paper.Namespaced} -+ */ -+ @NotNull -+ public void setPlaceableKeys(@NotNull Collection canPlaceOn) { -+ ItemMeta itemMeta = getItemMeta(); -+ itemMeta.setPlaceableKeys(canPlaceOn); -+ setItemMeta(itemMeta); -+ } -+ -+ /** -+ * Checks for the existence of any keys that the item can be placed on -+ * -+ * @return true if this item has placeable keys -+ */ -+ public boolean hasPlaceableKeys() { -+ return hasItemMeta() && getItemMeta().hasPlaceableKeys(); -+ } -+ -+ /** -+ * Checks for the existence of any keys that the item can destroy -+ * -+ * @return true if this item has destroyable keys -+ */ -+ public boolean hasDestroyableKeys() { -+ return hasItemMeta() && getItemMeta().hasDestroyableKeys(); ++ this.craftDelegate.setDamage(damage); + } + + /** @@ -660,42 +493,7 @@ index d15a74c38576c49df61cfab02c70fc5d8c0dd5f7..64055402076b62d32ba947830d935b79 + * @return True if damage broke the item + */ + public boolean damage(int amount, boolean ignoreUnbreaking) { -+ Damageable damageable = (Damageable) getItemMeta(); -+ if (amount > 0) { -+ int unbreaking = getEnchantLevel(Enchantment.DURABILITY); -+ int reduce = 0; -+ for (int i = 0; unbreaking > 0 && i < amount; ++i) { -+ if (reduceDamage(java.util.concurrent.ThreadLocalRandom.current(), unbreaking)) { -+ ++reduce; -+ } -+ } -+ amount -= reduce; -+ if (amount <= 0) { -+ return isBroke(damageable.getDamage()); -+ } -+ } -+ int damage = damageable.getDamage() + amount; -+ damageable.setDamage(damage); -+ setItemMeta((ItemMeta) damageable); -+ return isBroke(damage); ++ return this.craftDelegate.damage(amount, ignoreUnbreaking); + } -+ -+ public boolean isBroke(int damage) { -+ if (damage > getType().getMaxDurability()) { -+ if (getAmount() > 0) { -+ // ensure it "breaks" -+ setAmount(0); -+ } -+ return true; -+ } -+ return false; -+ } -+ -+ private boolean reduceDamage(java.util.Random random, int unbreaking) { -+ if (getType().isArmor()) { -+ return random.nextFloat() < 0.6F; -+ } -+ return random.nextInt(unbreaking + 1) > 0; -+ } -+ // Purpur end ++ // Purpur end - ItemStack convenience methods } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch new file mode 100644 index 000000000..177eef71b --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch @@ -0,0 +1,36 @@ +--- a/src/main/java/org/bukkit/inventory/RecipeChoice.java ++++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java +@@ -191,6 +_,7 @@ + public static class ExactChoice implements RecipeChoice { + + private List choices; ++ private Predicate predicate; // Purpur - Add predicate to recipe's ExactChoice ingredient + + public ExactChoice(@NotNull ItemStack stack) { + this(Arrays.asList(stack)); +@@ -241,6 +_,7 @@ + + @Override + public boolean test(@NotNull ItemStack t) { ++ if (predicate != null) return predicate.test(t); // Purpur - Add predicate to recipe's ExactChoice ingredient + for (ItemStack match : choices) { + if (t.isSimilar(match)) { + return true; +@@ -249,6 +_,17 @@ + + return false; + } ++ ++ // Purpur start - Add predicate to recipe's ExactChoice ingredient ++ @org.jetbrains.annotations.Nullable ++ public Predicate getPredicate() { ++ return predicate; ++ } ++ ++ public void setPredicate(@org.jetbrains.annotations.Nullable Predicate predicate) { ++ this.predicate = predicate; ++ } ++ // Purpur end - Add predicate to recipe's ExactChoice ingredient + + @Override + public int hashCode() { diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/view/AnvilView.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/view/AnvilView.java.patch new file mode 100644 index 000000000..3a63aa786 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/view/AnvilView.java.patch @@ -0,0 +1,37 @@ +--- a/src/main/java/org/bukkit/inventory/view/AnvilView.java ++++ b/src/main/java/org/bukkit/inventory/view/AnvilView.java +@@ -89,4 +_,34 @@ + */ + void bypassEnchantmentLevelRestriction(boolean bypassEnchantmentLevelRestriction); + // Paper end - bypass anvil level restrictions ++ ++ // Purpur start - Anvil API ++ /** ++ * Gets if the player viewing the anvil inventory can bypass experience cost ++ * ++ * @return whether the player viewing the anvil inventory can bypass the experience cost ++ */ ++ boolean canBypassCost(); ++ ++ /** ++ * Set if the player viewing the anvil inventory can bypass the experience cost ++ * ++ * @param bypassCost whether the player viewing the anvil inventory can bypass the experience cost ++ */ ++ void setBypassCost(boolean bypassCost); ++ ++ /** ++ * Gets if the player viewing the anvil inventory can do unsafe enchants ++ * ++ * @return whether the player viewing the anvil inventory can do unsafe enchants ++ */ ++ boolean canDoUnsafeEnchants(); ++ ++ /** ++ * Set if the player viewing the anvil inventory can do unsafe enchants ++ * ++ * @param canDoUnsafeEnchants whether the player viewing the anvil inventory can do unsafe enchants ++ */ ++ void setDoUnsafeEnchants(boolean canDoUnsafeEnchants); ++ // Purpur end - Anvil API + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/map/MapRenderer.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/map/MapRenderer.java.patch new file mode 100644 index 000000000..80ffd257a --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/map/MapRenderer.java.patch @@ -0,0 +1,15 @@ +--- a/src/main/java/org/bukkit/map/MapRenderer.java ++++ b/src/main/java/org/bukkit/map/MapRenderer.java +@@ -54,4 +_,12 @@ + */ + public abstract void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player); + ++ // Purpur - start - Explorer Map API ++ /** ++ * Check if this is an explorer (aka treasure) map. ++ * ++ * @return True if explorer map ++ */ ++ public abstract boolean isExplorerMap(); ++ // Purpur - end - Explorer Map API + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/permissions/PermissibleBase.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/permissions/PermissibleBase.java.patch new file mode 100644 index 000000000..e83156fc3 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/permissions/PermissibleBase.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/permissions/PermissibleBase.java ++++ b/src/main/java/org/bukkit/permissions/PermissibleBase.java +@@ -169,7 +_,7 @@ + + for (Permission perm : defaults) { + String name = perm.getName().toLowerCase(Locale.ROOT); +- permissions.put(name, new PermissionAttachmentInfo(parent, name, null, true)); ++ permissions.put(name, new PermissionAttachmentInfo(parent, name, null, perm.getDefault().getValue(isOp()))); // Purpur - Fix default permission system + Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); + calculateChildPermissions(perm.getChildren(), false, null); + } +@@ -197,7 +_,7 @@ + String name = entry.getKey(); + + Permission perm = Bukkit.getServer().getPluginManager().getPermission(name); +- boolean value = entry.getValue() ^ invert; ++ boolean value = (entry.getValue() == null && perm != null ? perm.getDefault().getValue(isOp()) : entry.getValue()) ^ invert; // Purpur - Fix default permission system + String lname = name.toLowerCase(Locale.ROOT); + + permissions.put(lname, new PermissionAttachmentInfo(parent, lname, attachment, value)); diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch new file mode 100644 index 000000000..c0117bc6c --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -53,6 +_,7 @@ + private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")}; + private final List loaders = new CopyOnWriteArrayList(); + private final LibraryLoader libraryLoader; ++ public static boolean SuppressLibraryLoaderLogger = false; // Purpur - Add log suppression for LibraryLoader + + /** + * This class was not meant to be constructed explicitly diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/LibraryLoader.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/LibraryLoader.java.patch new file mode 100644 index 000000000..29f7d6f64 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/LibraryLoader.java.patch @@ -0,0 +1,26 @@ +--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +@@ -69,6 +_,7 @@ + session.setTransferListener(new AbstractTransferListener() { + @Override + public void transferStarted(@NotNull TransferEvent event) { ++ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - Add log suppression for LibraryLoader + logger.log(Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); + } + }); +@@ -94,6 +_,7 @@ + // Paper end - plugin loader api + return null; + } ++ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - Add log suppression for LibraryLoader + logger.log(Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[] + { + java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), desc.getLibraries().size() // Paper - use configured log prefix +@@ -146,6 +_,7 @@ + } + + jarFiles.add(url); ++ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - Add log suppression for LibraryLoader + logger.log(Level.INFO, "[{0}] Loaded library {1}", new Object[] + { + java.util.Objects.requireNonNullElseGet(desc.getPrefix(), desc::getName), file // Paper - use configured log prefix diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/CommandPermissions.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/CommandPermissions.java.patch new file mode 100644 index 000000000..1b18651ec --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/CommandPermissions.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/util/permissions/CommandPermissions.java ++++ b/src/main/java/org/bukkit/util/permissions/CommandPermissions.java +@@ -18,6 +_,7 @@ + DefaultPermissions.registerPermission(PREFIX + "plugins", "Allows the user to view the list of plugins running on this server", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "reload", "Allows the user to reload the server settings", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "version", "Allows the user to view the version of the server", PermissionDefault.TRUE, commands); ++ DefaultPermissions.registerPermission(PREFIX + "purpur", "Allows the user to use the purpur command", PermissionDefault.OP, commands); // Purpur - Default permissions + + commands.recalculatePermissibles(); + return commands; diff --git a/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java.patch new file mode 100644 index 000000000..4cf992931 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java.patch @@ -0,0 +1,56 @@ +--- a/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java ++++ b/src/main/java/org/bukkit/util/permissions/DefaultPermissions.java +@@ -31,7 +_,7 @@ + + if (withLegacy) { + Permission legacy = new Permission(LEGACY_PREFIX + result.getName(), result.getDescription(), PermissionDefault.FALSE); +- legacy.getChildren().put(result.getName(), true); ++ legacy.getChildren().put(result.getName(), null); // Purpur - Fix default permission system + registerPermission(perm, false); + } + +@@ -40,7 +_,7 @@ + + @NotNull + public static Permission registerPermission(@NotNull Permission perm, @NotNull Permission parent) { +- parent.getChildren().put(perm.getName(), true); ++ parent.getChildren().put(perm.getName(), null); // Purpur - Fix default permission system + return registerPermission(perm); + } + +@@ -53,7 +_,7 @@ + @NotNull + public static Permission registerPermission(@NotNull String name, @Nullable String desc, @NotNull Permission parent) { + Permission perm = registerPermission(name, desc); +- parent.getChildren().put(perm.getName(), true); ++ parent.getChildren().put(perm.getName(), null); // Purpur - Fix default permission system + return perm; + } + +@@ -66,7 +_,7 @@ + @NotNull + public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @NotNull Permission parent) { + Permission perm = registerPermission(name, desc, def); +- parent.getChildren().put(perm.getName(), true); ++ parent.getChildren().put(perm.getName(), null); // Purpur - Fix default permission system + return perm; + } + +@@ -79,7 +_,7 @@ + @NotNull + public static Permission registerPermission(@NotNull String name, @Nullable String desc, @Nullable PermissionDefault def, @Nullable Map children, @NotNull Permission parent) { + Permission perm = registerPermission(name, desc, def, children); +- parent.getChildren().put(perm.getName(), true); ++ parent.getChildren().put(perm.getName(), null); // Purpur - Fix default permission system + return perm; + } + +@@ -88,6 +_,8 @@ + + CommandPermissions.registerPermissions(parent); + BroadcastPermissions.registerPermissions(parent); ++ ++ org.purpurmc.purpur.util.permissions.PurpurPermissions.registerPermissions(); // Purpur - Default permissions + + parent.recalculatePermissibles(); + } diff --git a/purpur-api/paper-patches/files/src/main/java/org/spigotmc/CustomTimingsHandler.java.patch b/purpur-api/paper-patches/files/src/main/java/org/spigotmc/CustomTimingsHandler.java.patch new file mode 100644 index 000000000..6d4395fbf --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/spigotmc/CustomTimingsHandler.java.patch @@ -0,0 +1,12 @@ +--- a/src/main/java/org/spigotmc/CustomTimingsHandler.java ++++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java +@@ -61,7 +_,7 @@ + handler = timing; + } + +- public void startTiming() { handler.startTiming(); } +- public void stopTiming() { handler.stopTiming(); } ++ public void startTiming() { /*handler.startTiming();*/ } // Purpur - Remove Timings ++ public void stopTiming() { /*handler.stopTiming();*/ } // Purpur - Remove Timings + + } diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java b/purpur-api/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java new file mode 100644 index 000000000..29540d555 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/entity/StoredEntity.java @@ -0,0 +1,52 @@ +package org.purpurmc.purpur.entity; + +import org.bukkit.Nameable; +import org.bukkit.block.EntityBlockStorage; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.persistence.PersistentDataHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents an entity stored in a block + * + * @see org.bukkit.block.EntityBlockStorage + */ +public interface StoredEntity extends PersistentDataHolder, Nameable { + /** + * Checks if this entity has been released yet + * + * @return if this entity has been released + */ + boolean hasBeenReleased(); + + /** + * Releases the entity from its stored block + * + * @return the released entity, or null if unsuccessful (including if this entity has already been released) + */ + @Nullable + T release(); + + /** + * Returns the block in which this entity is stored + * + * @return the EntityBlockStorage in which this entity is stored, or null if it has been released + */ + @Nullable + EntityBlockStorage getBlockStorage(); + + /** + * Gets the entity type of this stored entity + * + * @return the type of entity this stored entity represents + */ + @NotNull + EntityType getType(); + + /** + * Writes data to the block entity snapshot. {@link EntityBlockStorage#update()} must be run in order to update the block in game. + */ + void update(); +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java new file mode 100644 index 000000000..55feef232 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/ExecuteCommandEvent.java @@ -0,0 +1,127 @@ +package org.purpurmc.purpur.event; + +import com.google.common.base.Preconditions; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * This event is called whenever someone runs a command + */ +@NullMarked +public class ExecuteCommandEvent extends Event implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private CommandSender sender; + private Command command; + private String label; + private @Nullable String[] args; + + @ApiStatus.Internal + public ExecuteCommandEvent(CommandSender sender, Command command, String label, @Nullable String[] args) { + this.sender = sender; + this.command = command; + this.label = label; + this.args = args; + } + + /** + * Gets the command that the player is attempting to execute. + * + * @return Command the player is attempting to execute + */ + public Command getCommand() { + return command; + } + + /** + * Sets the command that the player will execute. + * + * @param command New command that the player will execute + * @throws IllegalArgumentException if command is null or empty + */ + public void setCommand(Command command) throws IllegalArgumentException { + Preconditions.checkArgument(command != null, "Command cannot be null"); + this.command = command; + } + + /** + * Gets the sender that this command will be executed as. + * + * @return Sender this command will be executed as + */ + public CommandSender getSender() { + return sender; + } + + /** + * Sets the sender that this command will be executed as. + * + * @param sender New sender which this event will execute as + * @throws IllegalArgumentException if the sender provided is null + */ + public void setSender(final CommandSender sender) throws IllegalArgumentException { + Preconditions.checkArgument(sender != null, "Sender cannot be null"); + this.sender = sender; + } + + /** + * Get the label used to execute this command + * + * @return Label used to execute this command + */ + public String getLabel() { + return label; + } + + /** + * Set the label used to execute this command + * + * @param label Label used + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * Get the args passed to the command + * + * @return Args passed to the command + */ + public String[] getArgs() { + return args; + } + + /** + * Set the args passed to the command + * + * @param args Args passed to the command + */ + public void setArgs(String[] args) { + this.args = args; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java new file mode 100644 index 000000000..e9637b820 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java @@ -0,0 +1,71 @@ +package org.purpurmc.purpur.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class PlayerAFKEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final boolean setAfk; + private boolean shouldKick; + private @Nullable String broadcast; + private boolean cancel; + + @ApiStatus.Internal + public PlayerAFKEvent(Player player, boolean setAfk, boolean shouldKick, @Nullable String broadcast, boolean async) { + super(player, async); + this.setAfk = setAfk; + this.shouldKick = shouldKick; + this.broadcast = broadcast; + } + + /** + * Whether player is going afk or coming back + * + * @return True if going afk. False is coming back + */ + public boolean isGoingAfk() { + return setAfk; + } + + public boolean shouldKick() { + return shouldKick; + } + + public void setShouldKick(boolean shouldKick) { + this.shouldKick = shouldKick; + } + + @Nullable + public String getBroadcastMsg() { + return broadcast; + } + + public void setBroadcastMsg(@Nullable String broadcast) { + this.broadcast = broadcast; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java new file mode 100644 index 000000000..795c558b4 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetSpawnerTypeWithEggEvent.java @@ -0,0 +1,83 @@ +package org.purpurmc.purpur.event; + +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PlayerSetSpawnerTypeWithEggEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block block; + private final CreatureSpawner spawner; + private EntityType type; + private boolean cancel; + + @ApiStatus.Internal + public PlayerSetSpawnerTypeWithEggEvent(Player player, Block block, CreatureSpawner spawner, EntityType type) { + super(player); + this.block = block; + this.spawner = spawner; + this.type = type; + } + + /** + * Get the spawner Block in the world + * + * @return Spawner Block + */ + public Block getBlock() { + return block; + } + + /** + * Get the spawner state + * + * @return Spawner state + */ + public CreatureSpawner getSpawner() { + return spawner; + } + + /** + * Gets the EntityType being set on the spawner + * + * @return EntityType being set + */ + public EntityType getEntityType() { + return type; + } + + /** + * Sets the EntityType being set on the spawner + * + * @param type EntityType to set + */ + public void setEntityType(EntityType type) { + this.type = type; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetTrialSpawnerTypeWithEggEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetTrialSpawnerTypeWithEggEvent.java new file mode 100644 index 000000000..1d4dbf60a --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/PlayerSetTrialSpawnerTypeWithEggEvent.java @@ -0,0 +1,83 @@ +package org.purpurmc.purpur.event; + +import org.bukkit.block.Block; +import org.bukkit.block.TrialSpawner; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PlayerSetTrialSpawnerTypeWithEggEvent extends PlayerEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final Block block; + private final TrialSpawner spawner; + private EntityType type; + private boolean cancel; + + @ApiStatus.Internal + public PlayerSetTrialSpawnerTypeWithEggEvent(Player player, Block block, TrialSpawner spawner, EntityType type) { + super(player); + this.block = block; + this.spawner = spawner; + this.type = type; + } + + /** + * Get the spawner Block in the world + * + * @return Spawner Block + */ + public Block getBlock() { + return block; + } + + /** + * Get the spawner state + * + * @return Spawner state + */ + public TrialSpawner getSpawner() { + return spawner; + } + + /** + * Gets the EntityType being set on the spawner + * + * @return EntityType being set + */ + public EntityType getEntityType() { + return type; + } + + /** + * Sets the EntityType being set on the spawner + * + * @param type EntityType to set + */ + public void setEntityType(EntityType type) { + this.type = type; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancel = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java new file mode 100644 index 000000000..4b4d32c58 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/PreBlockExplodeEvent.java @@ -0,0 +1,56 @@ +package org.purpurmc.purpur.event; + +import org.bukkit.ExplosionResult; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockExplodeEvent; +import org.jetbrains.annotations.ApiStatus; +import java.util.Collections; +import org.jspecify.annotations.NullMarked; + +/** + * Called before a block's explosion is processed + */ +@NullMarked +public class PreBlockExplodeEvent extends BlockExplodeEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final float yield; + + @ApiStatus.Internal + public PreBlockExplodeEvent(final Block what, final float yield, BlockState explodedBlockState, ExplosionResult result) { + super(what, explodedBlockState, Collections.emptyList(), yield, result); + this.yield = yield; + this.cancelled = false; + } + + /** + * Returns the percentage of blocks to drop from this explosion + * + * @return The yield. + */ + public float getYield() { + return yield; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java new file mode 100644 index 000000000..7f631a41a --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeFoundFlowerEvent.java @@ -0,0 +1,48 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Bee; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Called when a bee targets a flower + */ +@NullMarked +public class BeeFoundFlowerEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location location; + + @ApiStatus.Internal + public BeeFoundFlowerEvent(Bee bee, @Nullable Location location) { + super(bee); + this.location = location; + } + + @Override + public Bee getEntity() { + return (Bee) super.getEntity(); + } + + /** + * Returns the location of the flower that the bee targets + * + * @return The location of the flower + */ + @Nullable + public Location getLocation() { + return location; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java new file mode 100644 index 000000000..e260145d6 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStartedPollinatingEvent.java @@ -0,0 +1,46 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Bee; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a bee starts pollinating + */ +@NullMarked +public class BeeStartedPollinatingEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location location; + + @ApiStatus.Internal + public BeeStartedPollinatingEvent(Bee bee, Location location) { + super(bee); + this.location = location; + } + + @Override + public Bee getEntity() { + return (Bee) super.getEntity(); + } + + /** + * Returns the location of the flower that the bee pollinates + * + * @return The location of the flower + */ + public Location getLocation() { + return this.location; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java new file mode 100644 index 000000000..8b2b351d6 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/BeeStopPollinatingEvent.java @@ -0,0 +1,60 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.Location; +import org.bukkit.entity.Bee; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Called when a bee stops pollinating + */ +@NullMarked +public class BeeStopPollinatingEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + private final Location location; + private final boolean success; + + @ApiStatus.Internal + public BeeStopPollinatingEvent(Bee bee, @Nullable Location location, boolean success) { + super(bee); + this.location = location; + this.success = success; + } + + @Override + public Bee getEntity() { + return (Bee) super.getEntity(); + } + + /** + * Returns the location of the flower that the bee stopped pollinating + * + * @return The location of the flower + */ + @Nullable + public Location getLocation() { + return location; + } + + /** + * Returns whether the bee successfully pollinated the flower + * + * @return True if the pollination was successful + */ + public boolean wasSuccessful() { + return success; + } + + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java new file mode 100644 index 000000000..f0a7fe694 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/GoatRamEntityEvent.java @@ -0,0 +1,58 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.entity.Goat; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a goat rams an entity + */ +@NullMarked +public class GoatRamEntityEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private final LivingEntity rammedEntity; + private boolean cancelled; + + @ApiStatus.Internal + public GoatRamEntityEvent(Goat goat, LivingEntity rammedEntity) { + super(goat); + this.rammedEntity = rammedEntity; + } + + /** + * Returns the entity that was rammed by the goat + * + * @return The rammed entity + */ + public LivingEntity getRammedEntity() { + return this.rammedEntity; + } + + @Override + public Goat getEntity() { + return (Goat) super.getEntity(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java new file mode 100644 index 000000000..e34c37579 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaJoinCaravanEvent.java @@ -0,0 +1,60 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.entity.Llama; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a Llama tries to join a caravan. + *

+ * Cancelling the event will not let the Llama join. To prevent future attempts + * at joining a caravan use {@link Llama#setShouldJoinCaravan(boolean)}. + */ +@NullMarked +public class LlamaJoinCaravanEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final Llama head; + + @ApiStatus.Internal + public LlamaJoinCaravanEvent(Llama llama, Llama head) { + super(llama); + this.head = head; + } + + @Override + public Llama getEntity() { + return (Llama) entity; + } + + /** + * Get the Llama that this Llama is about to follow + * + * @return Llama about to be followed + */ + public Llama getHead() { + return head; + } + + @Override + public boolean isCancelled() { + return canceled; + } + + @Override + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java new file mode 100644 index 000000000..23ea41ff5 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/LlamaLeaveCaravanEvent.java @@ -0,0 +1,34 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.entity.Llama; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a Llama leaves a caravan + */ +@NullMarked +public class LlamaLeaveCaravanEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + + @ApiStatus.Internal + public LlamaLeaveCaravanEvent(Llama llama) { + super(llama); + } + + @Override + public Llama getEntity() { + return (Llama) entity; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java new file mode 100644 index 000000000..d56fb0664 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/PreEntityExplodeEvent.java @@ -0,0 +1,66 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.ExplosionResult; +import org.bukkit.Location; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.jetbrains.annotations.ApiStatus; +import java.util.Collections; +import org.jspecify.annotations.NullMarked; + +/** + * Called before an entity's explosion is processed + */ +@NullMarked +public class PreEntityExplodeEvent extends EntityExplodeEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final float yield; + private final Location location; + + @ApiStatus.Internal + public PreEntityExplodeEvent(org.bukkit.entity.Entity what, final Location location, final float yield, ExplosionResult result) { + super(what, location, Collections.emptyList(), yield, result); + this.cancelled = false; + this.yield = yield; + this.location = location; + } + + /** + * Returns the percentage of blocks to drop from this explosion + * + * @return The yield. + */ + public float getYield() { + return yield; + } + + /** + * Returns the location where the explosion happened. + * + * @return The location of the explosion + */ + public Location getLocation() { + return location; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java new file mode 100644 index 000000000..c31a656da --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableMoveEvent.java @@ -0,0 +1,100 @@ +package org.purpurmc.purpur.event.entity; + +import com.google.common.base.Preconditions; +import org.bukkit.Location; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Triggered when a ridable mob moves with a rider + */ +@NullMarked +public class RidableMoveEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean canceled; + private final Player rider; + private Location from; + private Location to; + + @ApiStatus.Internal + public RidableMoveEvent(Mob entity, Player rider, Location from, Location to) { + super(entity); + this.rider = rider; + this.from = from; + this.to = to; + } + + @Override + public Mob getEntity() { + return (Mob) entity; + } + + public Player getRider() { + return rider; + } + + public boolean isCancelled() { + return canceled; + } + + public void setCancelled(boolean cancel) { + canceled = cancel; + } + + /** + * Gets the location this entity moved from + * + * @return Location the entity moved from + */ + public Location getFrom() { + return from; + } + + /** + * Sets the location to mark as where the entity moved from + * + * @param from New location to mark as the entity's previous location + */ + public void setFrom(Location from) { + validateLocation(from); + this.from = from; + } + + /** + * Gets the location this entity moved to + * + * @return Location the entity moved to + */ + public Location getTo() { + return to; + } + + /** + * Sets the location that this entity will move to + * + * @param to New Location this entity will move to + */ + public void setTo(Location to) { + validateLocation(to); + this.to = to; + } + + private void validateLocation(Location loc) { + Preconditions.checkArgument(loc != null, "Cannot use null location!"); + Preconditions.checkArgument(loc.getWorld() != null, "Cannot use null location with null world!"); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java new file mode 100644 index 000000000..02de629f0 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/RidableSpacebarEvent.java @@ -0,0 +1,38 @@ +package org.purpurmc.purpur.event.entity; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class RidableSpacebarEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + @ApiStatus.Internal + public RidableSpacebarEvent(Entity entity) { + super(entity); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java new file mode 100644 index 000000000..b2199854b --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilTakeResultEvent.java @@ -0,0 +1,50 @@ +package org.purpurmc.purpur.event.inventory; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a player takes the result item out of an anvil + */ +@NullMarked +public class AnvilTakeResultEvent extends InventoryEvent { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final ItemStack result; + + @ApiStatus.Internal + public AnvilTakeResultEvent(HumanEntity player, InventoryView view, ItemStack result) { + super(view); + this.player = (Player) player; + this.result = result; + } + + public Player getPlayer() { + return player; + } + + public ItemStack getResult() { + return result; + } + + @Override + public AnvilInventory getInventory() { + return (AnvilInventory) super.getInventory(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java new file mode 100644 index 000000000..4293c4a57 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/AnvilUpdateResultEvent.java @@ -0,0 +1,35 @@ +package org.purpurmc.purpur.event.inventory; + +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.InventoryView; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when anvil slots change, triggering the result slot to be updated + */ +@NullMarked +public class AnvilUpdateResultEvent extends InventoryEvent { + private static final HandlerList handlers = new HandlerList(); + + @ApiStatus.Internal + public AnvilUpdateResultEvent(InventoryView view) { + super(view); + } + + @Override + public AnvilInventory getInventory() { + return (AnvilInventory) super.getInventory(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java new file mode 100644 index 000000000..d6db2d355 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/inventory/GrindstoneTakeResultEvent.java @@ -0,0 +1,72 @@ +package org.purpurmc.purpur.event.inventory; + +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.GrindstoneInventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a player takes the result item out of a Grindstone + */ +@NullMarked +public class GrindstoneTakeResultEvent extends InventoryEvent { + private static final HandlerList handlers = new HandlerList(); + private final Player player; + private final ItemStack result; + private int experienceAmount; + + @ApiStatus.Internal + public GrindstoneTakeResultEvent(HumanEntity player, InventoryView view, ItemStack result, int experienceAmount) { + super(view); + this.player = (Player) player; + this.result = result; + this.experienceAmount = experienceAmount; + } + + public Player getPlayer() { + return player; + } + + public ItemStack getResult() { + return result; + } + + @Override + public GrindstoneInventory getInventory() { + return (GrindstoneInventory) super.getInventory(); + } + + /** + * Get the amount of experience this transaction will give + * (takes priority over and uses result from {@link org.bukkit.event.block.BlockExpEvent}) + * + * @return Amount of experience to give + */ + public int getExperienceAmount() { + return this.experienceAmount; + } + + /** + * Set the amount of experience this transaction will give + * (takes priority over {@link org.bukkit.event.block.BlockExpEvent}) + * + * @param experienceAmount Amount of experience to give + */ + public void setExperienceAmount(int experienceAmount) { + this.experienceAmount = experienceAmount; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java new file mode 100644 index 000000000..31cce9f4e --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/player/PlayerBookTooLargeEvent.java @@ -0,0 +1,65 @@ +package org.purpurmc.purpur.event.player; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a player tries to bypass book limitations + */ +@NullMarked +public class PlayerBookTooLargeEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + private final ItemStack book; + private boolean kickPlayer = true; + + /** + * @param player The player + * @param book The book + */ + @ApiStatus.Internal + public PlayerBookTooLargeEvent(Player player, ItemStack book) { + super(player, !Bukkit.isPrimaryThread()); + this.book = book; + } + + /** + * Get the book containing the wanted edits + * + * @return The book + */ + public ItemStack getBook() { + return book; + } + + /** + * Whether server should kick the player or not + * + * @return True to kick player + */ + public boolean shouldKickPlayer() { + return kickPlayer; + } + + /** + * Whether server should kick the player or not + * + * @param kickPlayer True to kick player + */ + public void setShouldKickPlayer(boolean kickPlayer) { + this.kickPlayer = kickPlayer; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/language/Language.java b/purpur-api/src/main/java/org/purpurmc/purpur/language/Language.java new file mode 100644 index 000000000..cbdad4cf0 --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/language/Language.java @@ -0,0 +1,60 @@ +package org.purpurmc.purpur.language; + +import net.kyori.adventure.translation.Translatable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Represents a language that can translate translation keys + */ +@NullMarked +public abstract class Language { + private static @Nullable Language language; + + /** + * Returns the default language of the server + */ + @Nullable + public static Language getLanguage() { + return language; + } + + public static void setLanguage(Language language) { + if (Language.language != null) { + throw new UnsupportedOperationException("Cannot redefine singleton Language"); + } + Language.language = language; + } + + /** + * Checks if a certain translation key is translatable with this language + * @param key The translation key + * @return Whether this language can translate the key + */ + abstract public boolean has(String key); + + /** + * Checks if a certain translation key is translatable with this language + * @param key The translation key + * @return Whether this language can translate the key + */ + public boolean has(Translatable key) { + return has(key.translationKey()); + } + + /** + * Translates a translation key to this language + * @param key The translation key + * @return The translated key, or the translation key if it couldn't be translated + */ + abstract public String getOrDefault(String key); + + /** + * Translates a translation key to this language + * @param key The translation key + * @return The translated key, or the translation key if it couldn't be translated + */ + public String getOrDefault(Translatable key) { + return getOrDefault(key.translationKey()); + } +} diff --git a/purpur-api/src/main/java/org/purpurmc/purpur/util/permissions/PurpurPermissions.java b/purpur-api/src/main/java/org/purpurmc/purpur/util/permissions/PurpurPermissions.java new file mode 100644 index 000000000..50647252e --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/util/permissions/PurpurPermissions.java @@ -0,0 +1,87 @@ +package org.purpurmc.purpur.util.permissions; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Mob; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.util.permissions.DefaultPermissions; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public final class PurpurPermissions { + private static final String ROOT = "purpur"; + private static final String PREFIX = ROOT + "."; + private static final Set mobs = new HashSet<>(); + + static { + for (EntityType mob : EntityType.values()) { + Class clazz = mob.getEntityClass(); + if (clazz != null && Mob.class.isAssignableFrom(clazz)) { + mobs.add(mob.getName()); + } + } + } + + @NotNull + public static Permission registerPermissions() { + Permission purpur = DefaultPermissions.registerPermission(ROOT, "Gives the user the ability to use all Purpur utilities and commands", PermissionDefault.FALSE); + + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.six", "Gives the user six rows of enderchest space", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.five", "Gives the user five rows of enderchest space", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.four", "Gives the user four rows of enderchest space", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.three", "Gives the user three rows of enderchest space", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.two", "Gives the user two rows of enderchest space", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "enderchest.rows.one", "Gives the user one row of enderchest space", PermissionDefault.FALSE, purpur); + + DefaultPermissions.registerPermission(PREFIX + "debug.f3n", "Allows the user to use F3+N keybind to swap gamemodes", PermissionDefault.FALSE, purpur); + + DefaultPermissions.registerPermission(PREFIX + "joinfullserver", "Allows the user to join a full server", PermissionDefault.OP, purpur); + + DefaultPermissions.registerPermission(PREFIX + "bypassIdleKick", "Allows the user to bypass being kicked while idle", PermissionDefault.FALSE, purpur); + + DefaultPermissions.registerPermission(PREFIX + "inventory_totem", "Allows the user to use totem of undying anywhere in their inventory", PermissionDefault.FALSE, purpur); + + Permission anvil = DefaultPermissions.registerPermission(PREFIX + "anvil", "Allows the user to use all anvil color and format abilities", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "anvil.color", "Allows the user to use color codes in an anvil", PermissionDefault.FALSE, anvil); + DefaultPermissions.registerPermission(PREFIX + "anvil.minimessage", "Allows the user to use minimessage tags in an anvil", PermissionDefault.FALSE, anvil); + DefaultPermissions.registerPermission(PREFIX + "anvil.remove_italics", "Allows the user to remove italics in an anvil", PermissionDefault.FALSE, anvil); + DefaultPermissions.registerPermission(PREFIX + "anvil.format", "Allows the user to use format codes in an anvil", PermissionDefault.FALSE, anvil); + anvil.recalculatePermissibles(); + + Permission book = DefaultPermissions.registerPermission(PREFIX + "book", "Allows the user to use color codes on books", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "book.color.edit", "Allows the user to use color codes on books when editing", PermissionDefault.FALSE, book); + DefaultPermissions.registerPermission(PREFIX + "book.color.sign", "Allows the user to use color codes on books when signing", PermissionDefault.FALSE, book); + book.recalculatePermissibles(); + + Permission sign = DefaultPermissions.registerPermission(PREFIX + "sign", "Allows the user to use all sign abilities", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission(PREFIX + "sign.edit", "Allows the user to click signs to open sign editor", PermissionDefault.TRUE, sign); + DefaultPermissions.registerPermission(PREFIX + "sign.color", "Allows the user to use color codes on signs", PermissionDefault.FALSE, sign); + DefaultPermissions.registerPermission(PREFIX + "sign.style", "Allows the user to use style codes on signs", PermissionDefault.FALSE, sign); + DefaultPermissions.registerPermission(PREFIX + "sign.magic", "Allows the user to use magic/obfuscate code on signs", PermissionDefault.FALSE, sign); + sign.recalculatePermissibles(); + + Permission ride = DefaultPermissions.registerPermission("allow.ride", "Allows the user to ride all mobs", PermissionDefault.FALSE, purpur); + for (String mob : mobs) { + DefaultPermissions.registerPermission("allow.ride." + mob, "Allows the user to ride " + mob, PermissionDefault.FALSE, ride); + } + ride.recalculatePermissibles(); + + Permission special = DefaultPermissions.registerPermission("allow.special", "Allows the user to use all mobs special abilities", PermissionDefault.FALSE, purpur); + for (String mob : mobs) { + DefaultPermissions.registerPermission("allow.special." + mob, "Allows the user to use " + mob + " special ability", PermissionDefault.FALSE, special); + } + special.recalculatePermissibles(); + + Permission powered = DefaultPermissions.registerPermission("allow.powered", "Allows the user to toggle all mobs powered state", PermissionDefault.FALSE, purpur); + DefaultPermissions.registerPermission("allow.powered.creeper", "Allows the user to toggle creeper powered state", PermissionDefault.FALSE, powered); + powered.recalculatePermissibles(); + + DefaultPermissions.registerPermission(PREFIX + "portal.instant", "Allows the user to bypass portal wait time", PermissionDefault.FALSE, purpur); + + purpur.recalculatePermissibles(); + return purpur; + } +} diff --git a/purpur-server/build.gradle.kts.patch b/purpur-server/build.gradle.kts.patch new file mode 100644 index 000000000..a90912979 --- /dev/null +++ b/purpur-server/build.gradle.kts.patch @@ -0,0 +1,156 @@ +--- a/paper-server/build.gradle.kts ++++ b/paper-server/build.gradle.kts +@@ -2,6 +_,7 @@ + import io.papermc.paperweight.attribute.DevBundleOutput + import io.papermc.paperweight.util.* + import java.time.Instant ++import kotlin.io.path.writeText + + plugins { + `java-library` +@@ -22,6 +_,18 @@ + minecraftVersion = providers.gradleProperty("mcVersion") + gitFilePatches = false + ++ // Purpur start - Rebrand ++ val purpur = forks.register("purpur") { ++ upstream.patchDir("paperServer") { ++ upstreamPath = "paper-server" ++ excludes = setOf("src/minecraft", "patches", "build.gradle.kts") ++ patchesDir = rootDirectory.dir("purpur-server/paper-patches") ++ outputDir = rootDirectory.dir("paper-server") ++ } ++ } ++ activeFork = purpur ++ // Purpur end - Rebrand ++ + spigot { + enabled = false + buildDataRef = "42d18d4c4653ffc549778dbe223f6994a031d69e" +@@ -104,7 +_,21 @@ + } + } + +-val log4jPlugins = sourceSets.create("log4jPlugins") ++// Purpur start - Rebrand ++sourceSets { ++ main { ++ java { srcDir("../paper-server/src/main/java") } ++ resources { srcDir("../paper-server/src/main/resources") } ++ } ++ test { ++ java { srcDir("../paper-server/src/test/java") } ++ resources { srcDir("../paper-server/src/test/resources") } ++ } ++} ++val log4jPlugins = sourceSets.create("log4jPlugins") { ++ java { srcDir("../paper-server/src/log4jPlugins/java") } ++} ++// Purpur end - Rebrand + configurations.named(log4jPlugins.compileClasspathConfigurationName) { + extendsFrom(configurations.compileClasspath.get()) + } +@@ -127,7 +_,7 @@ + } + + dependencies { +- implementation(project(":paper-api")) ++ implementation(project(":purpur-api")) // Purpur + implementation("ca.spottedleaf:concurrentutil:0.0.7") + implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ + implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 +@@ -153,6 +_,10 @@ + implementation("org.ow2.asm:asm-commons:9.8") + implementation("org.spongepowered:configurate-yaml:4.2.0") + ++ implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur ++ implementation("org.mozilla:rhino-engine:1.7.14") // Purpur ++ implementation("dev.omega24:upnp4j:1.0") // Purpur ++ + // Deps that were previously in the API but have now been moved here for backwards compat, eventually to be removed + runtimeOnly("commons-lang:commons-lang:2.6") + runtimeOnly("org.xerial:sqlite-jdbc:3.49.1.0") +@@ -198,14 +_,14 @@ + val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", +- "Implementation-Title" to "Paper", ++ "Implementation-Title" to "Purpur", // Purpur + "Implementation-Version" to implementationVersion, + "Implementation-Vendor" to date, +- "Specification-Title" to "Paper", ++ "Specification-Title" to "Purpur", // Purpur + "Specification-Version" to project.version, +- "Specification-Vendor" to "Paper Team", +- "Brand-Id" to "papermc:paper", +- "Brand-Name" to "Paper", ++ "Specification-Vendor" to "Purpur Team", // Purpur ++ "Brand-Id" to "purpurmc:purpur", // Purpur ++ "Brand-Name" to "Purpur", // Purpur + "Build-Number" to (build ?: ""), + "Build-Time" to buildTime.toString(), + "Git-Branch" to gitBranch, +@@ -264,7 +_,7 @@ + jvmArgumentProviders.add(provider) + } + +-val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() ++val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("../paper-server/src/generated/java").asFile.toPath() // Purpur + idea { + module { + generatedSourceDirs.add(generatedDir.toFile()) +@@ -356,7 +_,7 @@ + mainClass.set(null as String?) + } + +-fill { ++/* fill { // Purpur - we don't use fill + project("paper") + versionFamily(paperweight.minecraftVersion.map { it.split(".", "-").takeWhile { part -> part.toIntOrNull() != null }.take(2).joinToString(".") }) + version(paperweight.minecraftVersion) +@@ -371,4 +_,44 @@ + } + } + } +-} ++} */ // Purpur - we don't use fill ++ ++// tasks.register("rebuildMinecraftSourcesWithGit") { ++// group = "temp" ++// ++// val patchDir = project.rootDir.resolve("purpur-server/minecraft-patches/sources").convertToPath().cleanDir() ++// val inputDir = this.project.rootDir.resolve("purpur-server/src/minecraft/java").convertToPath() ++// ++// val git = Git(inputDir) ++// git("stash", "push").executeSilently(silenceErr = true) ++// git("checkout", "file").executeSilently(silenceErr = true) ++// ++// rebuildWithGit(git, patchDir) ++// } ++// ++// private fun rebuildWithGit( ++// git: Git, ++// patchDir: java.nio.file.Path ++// ): Int { ++// val files = git("diff-tree", "--name-only", "--no-commit-id", "-r", "HEAD").getText().split("\n") ++// files.parallelStream().forEach { filename -> ++// if (filename.isBlank()) return@forEach ++// val patch = git( ++// "format-patch", ++// "--diff-algorithm=myers", ++// "--full-index", ++// "--no-signature", ++// "--no-stat", ++// "--no-numbered", ++// "-1", ++// "HEAD", ++// "--stdout", ++// filename ++// ).getText() ++// val patchFile = patchDir.resolve("$filename.patch") ++// patchFile.createParentDirectories() ++// patchFile.writeText(patch) ++// } ++// ++// return files.size ++// } diff --git a/purpur-server/minecraft-patches/features/0001-Ridables.patch b/purpur-server/minecraft-patches/features/0001-Ridables.patch new file mode 100644 index 000000000..be3daf702 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0001-Ridables.patch @@ -0,0 +1,5240 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 5 Jul 2020 22:19:49 -0500 +Subject: [PATCH] Ridables + + +diff --git a/net/minecraft/gametest/framework/GameTestHelper.java b/net/minecraft/gametest/framework/GameTestHelper.java +index 05eb0c3273ffa1b5a1ebd8f8ae42c11830d755c7..49d3154afe2ca5789e63bdf972c791969405c16d 100644 +--- a/net/minecraft/gametest/framework/GameTestHelper.java ++++ b/net/minecraft/gametest/framework/GameTestHelper.java +@@ -324,6 +324,8 @@ public class GameTestHelper { + + public void setAfk(final boolean afk) {} // Purpur - AFK API + ++ public void resetLastActionTime() {} // Purpur - Ridables ++ + @Override + public boolean isClientAuthoritative() { + return false; +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index c714782f047c211443723738527f4bd019aaa322..cfd9596246713030f7c0f28a65abeed6dcc8d81b 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1833,6 +1833,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent + serverLevel.updateLagCompensationTick(); // Paper - lag compensation + net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers ++ serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables + profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().identifier()); + /* Drop global time updates + if (this.tickCount % 20 == 0) { +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index c18844ca7c9840f28fe5167d67387ebaf758a9da..c99ba8c3eba14efcf7906fe2b42e4db1c73c90fe 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -230,6 +230,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent + public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent + private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) ++ public boolean hasRidableMoveEvent = false; // Purpur - Ridables + + @Override + public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 9ec896ad8e95d7822095c42054e76e7a5db91481..2eba1b0ac8b4a0bb34d04b81c4c279db6e716b3b 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -772,6 +772,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + this.trackEnteredOrExitedLavaOnVehicle(); + this.updatePlayerAttributes(); + this.advancements.flushDirty(this, true); ++ ++ // Purpur start - Ridables ++ if (this.level().purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && this.level().getGameTime() % 100 == 0) { // 5 seconds ++ MobEffectInstance nightVision = this.getEffect(MobEffects.NIGHT_VISION); ++ if (nightVision == null || nightVision.getDuration() <= 300) { // 15 seconds ++ this.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 400, 0)); // 20 seconds ++ } ++ } ++ // Purpur end - Ridables + } + + private void updatePlayerAttributes() { +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index cd7ecd971b5a0ffdf6cabeaa874f2a221431052e..e587414dd1250b14a0fecf8d1b25dd70b20816eb 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2895,6 +2895,8 @@ public class ServerGamePacketListenerImpl + ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); + final boolean resendData = event.isCancelled() || !ServerGamePacketListenerImpl.this.player.getItemInHand(hand).is(itemType); + ++ player.processClick(hand); // Purpur - Ridables ++ + // Entity in bucket - SPIGOT-4048 and SPIGOT-6859 + if (itemType == Items.WATER_BUCKET && target instanceof net.minecraft.world.entity.animal.Bucketable && target instanceof LivingEntity && resendData) { + target.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index c133b6796c0251500801b2e41df9ae4b38d111a1..468df93a0302f200c2bd5e9bc65feccdd8649bf3 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -3371,6 +3371,13 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + + this.passengers = ImmutableList.copyOf(list); + } ++ ++ // Purpur start - Ridables ++ if (isRidable() && this.passengers.get(0) == passenger && passenger instanceof Player player) { ++ onMount(player); ++ this.rider = player; ++ } ++ // Purpur end - Ridables + } + } + +@@ -3411,6 +3418,14 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + return false; + } + // CraftBukkit end ++ ++ // Purpur start - Ridables ++ if (this.rider != null && this.passengers.get(0) == this.rider) { ++ onDismount(this.rider); ++ this.rider = null; ++ } ++ // Purpur end - Ridables ++ + if (this.passengers.size() == 1 && this.passengers.get(0) == passenger) { + this.passengers = ImmutableList.of(); + } else { +@@ -5462,4 +5477,44 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + return ((ServerLevel) this.level()).isPositionEntityTicking(this.blockPosition()); + } + // Paper end - Expose entity id counter ++ // Purpur start - Ridables ++ @Nullable ++ private Player rider = null; ++ ++ @Nullable ++ public Player getRider() { ++ return rider; ++ } ++ ++ public boolean isRidable() { ++ return false; ++ } ++ ++ public boolean isControllable() { ++ return true; ++ } ++ ++ public void onMount(Player rider) { ++ if (this instanceof Mob) { ++ ((Mob) this).setTarget(null, null); ++ ((Mob) this).getNavigation().stop(); ++ } ++ rider.setJumping(false); // fixes jump on mount ++ } ++ ++ public void onDismount(Player player) { ++ } ++ ++ public boolean onSpacebar() { ++ return false; ++ } ++ ++ public boolean onClick(InteractionHand hand) { ++ return false; ++ } ++ ++ public boolean processClick(InteractionHand hand) { ++ return false; ++ } ++ // Purpur end - Ridables + } +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index aeadca3aa7baf337aff8c8e24bf557f6a9db322a..6e0a2741db06f93f31349515d1d13181b70e08ed 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -244,9 +244,9 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + protected int noActionTime; + public float lastHurt; + public boolean jumping; +- public float xxa; +- public float yya; +- public float zza; ++ public float xxa; public float getStrafeMot() { return xxa; } public void setStrafeMot(float strafe) { xxa = strafe; } // Purpur - OBFHELPER ++ public float yya; public float getVerticalMot() { return yya; } public void setVerticalMot(float vertical) { yya = vertical; } // Purpur - OBFHELPER ++ public float zza; public float getForwardMot() { return zza; } public void setForwardMot(float forward) { zza = forward; } // Purpur - OBFHELPER + protected InterpolationHandler interpolation = new InterpolationHandler(this); + protected double lerpYHeadRot; + protected int lerpHeadSteps; +@@ -294,7 +294,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + + protected LivingEntity(EntityType type, Level level) { + super(type, level); +- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type)); ++ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - Ridables + this.craftAttributes = new org.bukkit.craftbukkit.attribute.CraftAttributeMap(this.attributes); // CraftBukkit + // CraftBukkit - this.setHealth(this.getMaxHealth()) inlined and simplified to skip the instanceof check for Player, as getBukkitEntity() is not initialized in constructor + this.entityData.set(LivingEntity.DATA_HEALTH_ID, this.getMaxHealth()); +@@ -371,6 +371,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + .add(Attributes.CAMERA_DISTANCE) + .add(Attributes.WAYPOINT_TRANSMIT_RANGE); + } ++ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur - Ridables + + @Override + protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) { +@@ -3111,6 +3112,20 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + this.move(MoverType.SELF, this.getDeltaMovement()); + this.setDeltaMovement(this.getDeltaMovement().scale(0.5)); + } else { ++ // Purpur start - Ridables ++ if (this.getRider() != null && this.isControllable()) { ++ float friction = 0.91F; ++ if (this.onGround()) { ++ friction = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.91F; ++ } ++ ++ float frictionCompensation = 0.16277137F / (friction * friction * friction); ++ this.moveRelative(this.onGround() ? 0.1F * frictionCompensation : 0.02F, relative); ++ this.move(MoverType.SELF, this.getDeltaMovement()); ++ this.setDeltaMovement(this.getDeltaMovement().scale(friction)); ++ return; ++ } ++ // Purpur end - Ridables + this.moveRelative(amount, relative); + this.move(MoverType.SELF, this.getDeltaMovement()); + this.setDeltaMovement(this.getDeltaMovement().scale(0.91F)); +@@ -3799,8 +3814,10 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + this.pushEntities(); + profilerFiller.pop(); + // Paper start - Add EntityMoveEvent +- if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof Player)) { +- if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { ++ // Purpur start - Ridables ++ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { ++ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof Player)) { ++ // Purpur end - Ridables + Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO); + Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); + io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone()); +@@ -3810,6 +3827,21 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + this.absSnapTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); + } + } ++ // Purpur start - Ridables ++ if (getRider() != null) { ++ getRider().resetLastActionTime(); ++ if (((ServerLevel) level()).hasRidableMoveEvent && this instanceof Mob) { ++ Location from = new Location(level().getWorld(), xo, yo, zo, this.yRotO, this.xRotO); ++ Location to = new Location(level().getWorld(), getX(), getY(), getZ(), this.getYRot(), this.getXRot()); ++ org.purpurmc.purpur.event.entity.RidableMoveEvent event = new org.purpurmc.purpur.event.entity.RidableMoveEvent((org.bukkit.entity.Mob) getBukkitLivingEntity(), (org.bukkit.entity.Player) getRider().getBukkitEntity(), from, to.clone()); ++ if (!event.callEvent()) { ++ this.absSnapTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch()); ++ } else if (!to.equals(event.getTo())) { ++ this.absSnapTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); ++ } ++ } ++ } ++ // Purpur end - Ridables + } + // Paper end - Add EntityMoveEvent + if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterOrRain()) { +diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java +index dd0f83b9355271f7aab15eb49833f518e499595e..c519f3f501963bde3f1cadf24f88edd2a043215d 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -156,8 +156,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + super(type, level); + this.goalSelector = new GoalSelector(); + this.targetSelector = new GoalSelector(); +- this.lookControl = new LookControl(this); +- this.moveControl = new MoveControl(this); ++ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this); // Purpur - Ridables ++ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this); // Purpur - Ridables + this.jumpControl = new JumpControl(this); + this.bodyRotationControl = this.createBodyControl(); + this.navigation = this.createNavigation(level); +@@ -594,6 +594,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + } + } else { ++ if (getRider() == null || !this.isControllable()) // Purpur - Ridables + this.igniteForSeconds(8.0F); + } + } +@@ -1357,7 +1358,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + + protected InteractionResult mobInteract(Player player, InteractionHand hand) { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + + protected void usePlayerItem(Player player, InteractionHand hand, ItemStack stack) { +@@ -1697,4 +1698,58 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + public float chargeSpeedModifier() { + return 1.0F; + } ++ ++ // Purpur start - Ridables ++ public double getMaxY() { ++ return level().getHeight(); ++ } ++ ++ public InteractionResult tryRide(Player player, InteractionHand hand) { ++ return tryRide(player, hand, InteractionResult.PASS); ++ } ++ ++ public InteractionResult tryRide(Player player, InteractionHand hand, InteractionResult result) { ++ if (!isRidable()) { ++ return result; ++ } ++ if (hand != InteractionHand.MAIN_HAND) { ++ return InteractionResult.PASS; ++ } ++ if (player.isShiftKeyDown()) { ++ return InteractionResult.PASS; ++ } ++ if (!player.getItemInHand(hand).isEmpty()) { ++ return InteractionResult.PASS; ++ } ++ if (!passengers.isEmpty() || player.isPassenger()) { ++ return InteractionResult.PASS; ++ } ++ if (this instanceof TamableAnimal tamable) { ++ if (tamable.isTame() && !tamable.isOwnedBy(player)) { ++ return InteractionResult.PASS; ++ } ++ if (!tamable.isTame() && !level().purpurConfig.untamedTamablesAreRidable) { ++ return InteractionResult.PASS; ++ } ++ } ++ if (this instanceof AgeableMob ageable) { ++ if (ageable.isBaby() && !level().purpurConfig.babiesAreRidable) { ++ return InteractionResult.PASS; ++ } ++ } ++ if (!player.getBukkitEntity().hasPermission("allow.ride." + net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(getType()).getPath())) { ++ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ serverPlayer.sendMiniMessage(org.purpurmc.purpur.PurpurConfig.cannotRideMob); ++ } ++ return InteractionResult.PASS; ++ } ++ player.setYRot(this.getYRot()); ++ player.setXRot(this.getXRot()); ++ if (player.startRiding(this)) { ++ return InteractionResult.SUCCESS; ++ } else { ++ return InteractionResult.PASS; ++ } ++ } ++ // Purpur end - Ridables + } +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index 92b7f4cbd276dad49cba78514db3552af8cdd80c..fd26471047cc5bef28ee9ddba4a8542deef889cc 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -18,14 +18,21 @@ public class AttributeMap { + private final Set attributesToSync = new ObjectOpenHashSet<>(); + private final Set attributesToUpdate = new ObjectOpenHashSet<>(); + private final AttributeSupplier supplier; ++ private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables + + public AttributeMap(AttributeSupplier supplier) { +- this.supplier = supplier; ++ // Purpur start - Ridables ++ this(supplier, null); ++ } ++ public AttributeMap(AttributeSupplier defaultAttributes, net.minecraft.world.entity.LivingEntity entity) { ++ this.entity = entity; ++ // Purpur end - Ridables ++ this.supplier = defaultAttributes; + } + + private void onAttributeModified(AttributeInstance instance) { + this.attributesToUpdate.add(instance); +- if (instance.getAttribute().value().isClientSyncable()) { ++ if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables + this.attributesToSync.add(instance); + } + } +@@ -39,7 +46,7 @@ public class AttributeMap { + } + + public Collection getSyncableAttributes() { +- return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable()).collect(Collectors.toList()); ++ return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables + } + + public @Nullable AttributeInstance getInstance(Holder attribute) { +diff --git a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +index 6cf01fe1aac41af171b444ac737816a06bd80d6f..686776bb00560f9da8838bd5f8dd64aaddfa7a2b 100644 +--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java ++++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +@@ -142,7 +142,7 @@ public class DefaultAttributes { + .put(EntityType.PANDA, Panda.createAttributes().build()) + .put(EntityType.PARCHED, Parched.createAttributes().build()) + .put(EntityType.PARROT, Parrot.createAttributes().build()) +- .put(EntityType.PHANTOM, Monster.createMonsterAttributes().build()) ++ .put(EntityType.PHANTOM, net.minecraft.world.entity.monster.Phantom.createAttributes().build()) // Purpur - Ridables + .put(EntityType.PIG, Pig.createAttributes().build()) + .put(EntityType.PIGLIN, Piglin.createAttributes().build()) + .put(EntityType.PIGLIN_BRUTE, PiglinBrute.createAttributes().build()) +diff --git a/net/minecraft/world/entity/ai/control/MoveControl.java b/net/minecraft/world/entity/ai/control/MoveControl.java +index b7a4b5ad718e8f4ea108f606669d4fcaf5219e6f..0604fca0687e02e75c097ff864b13e2763bfeb76 100644 +--- a/net/minecraft/world/entity/ai/control/MoveControl.java ++++ b/net/minecraft/world/entity/ai/control/MoveControl.java +@@ -29,6 +29,20 @@ public class MoveControl implements Control { + this.mob = mob; + } + ++ // Purpur start - Ridables ++ public void setSpeedModifier(double speed) { ++ this.speedModifier = speed; ++ } ++ ++ public void setForward(float forward) { ++ this.strafeForwards = forward; ++ } ++ ++ public void setStrafe(float strafe) { ++ this.strafeRight = strafe; ++ } ++ // Purpur end - Ridables ++ + public boolean hasWanted() { + return this.operation == MoveControl.Operation.MOVE_TO; + } +diff --git a/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java +index d7f9b3b2b1077ea10e8f64b87c8f4c4354e90858..713f62b34a91fa76f40e49a5e390145f70755e58 100644 +--- a/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java ++++ b/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java +@@ -3,7 +3,7 @@ package net.minecraft.world.entity.ai.control; + import net.minecraft.util.Mth; + import net.minecraft.world.entity.Mob; + +-public class SmoothSwimmingLookControl extends LookControl { ++public class SmoothSwimmingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + private final int maxYRotFromCenter; + private static final int HEAD_TILT_X = 10; + private static final int HEAD_TILT_Y = 20; +@@ -14,7 +14,7 @@ public class SmoothSwimmingLookControl extends LookControl { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.lookAtCooldown > 0) { + this.lookAtCooldown--; + this.getYRotD().ifPresent(rotationWanted -> this.mob.yHeadRot = this.rotateTowards(this.mob.yHeadRot, rotationWanted + 20.0F, this.yMaxRotSpeed)); +diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index ca68d356e892e0ac8a651ced92aab7dbfd4699b4..2cfcf1f7473d8612777ca0752f6d1521c231ef42 100644 +--- a/net/minecraft/world/entity/ambient/Bat.java ++++ b/net/minecraft/world/entity/ambient/Bat.java +@@ -42,11 +42,58 @@ public class Bat extends AmbientCreature { + + public Bat(EntityType type, Level level) { + super(type, level); ++ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.075F); // Purpur - Ridables + if (!level.isClientSide()) { + this.setResting(true); + } + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean shouldSendAttribute(net.minecraft.world.entity.ai.attributes.Attribute attribute) { return attribute != Attributes.FLYING_SPEED.value(); } // Fixes log spam on clients ++ ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.batRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.batRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.batControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.batMaxY; ++ } ++ ++ @Override ++ public void onMount(net.minecraft.world.entity.player.Player rider) { ++ super.onMount(rider); ++ if (isResting()) { ++ setResting(false); ++ level().levelEvent(null, 1025, new BlockPos(this).above(), 0); ++ } ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean isFlapping() { + return !this.isResting() && this.tickCount % 10.0F == 0.0F; +@@ -97,7 +144,7 @@ public class Bat extends AmbientCreature { + } + + public static AttributeSupplier.Builder createAttributes() { +- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0); ++ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - Ridables + } + + public boolean isResting() { +@@ -128,6 +175,14 @@ public class Bat extends AmbientCreature { + + @Override + protected void customServerAiStep(ServerLevel level) { ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable()) { ++ Vec3 mot = getDeltaMovement(); ++ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z()); ++ return; ++ } ++ // Purpur end - Ridables ++ + super.customServerAiStep(level); + BlockPos blockPos = this.blockPosition(); + BlockPos blockPos1 = blockPos.above(); +diff --git a/net/minecraft/world/entity/animal/allay/Allay.java b/net/minecraft/world/entity/animal/allay/Allay.java +index 0591dc9c39f94768c5ebf74cad7bae98921ff66c..6ab6305f0b25f7c860673c70d9ce911688ecf1e6 100644 +--- a/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/net/minecraft/world/entity/animal/allay/Allay.java +@@ -113,10 +113,23 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + private float spinningAnimationTicks; + private float spinningAnimationTicks0; + public boolean forceDancing = false; // CraftBukkit ++ private org.purpurmc.purpur.controller.FlyingMoveControllerWASD purpurController; // Purpur - Ridables + + public Allay(EntityType type, Level level) { + super(type, level); +- this.moveControl = new FlyingMoveControl(this, 20, true); ++ // Purpur start - Ridables ++ this.purpurController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.1F, 0.5F); ++ this.moveControl = new FlyingMoveControl(this, 20, true) { ++ @Override ++ public void tick() { ++ if (mob.getRider() != null && mob.isControllable()) { ++ purpurController.purpurTick(mob.getRider()); ++ } else { ++ super.tick(); ++ } ++ } ++ }; ++ // Purpur end - Ridables + this.setCanPickUpLoot(this.canPickUpLoot()); + this.vibrationUser = new Allay.VibrationUser(); + this.vibrationData = new VibrationSystem.Data(); +@@ -132,6 +145,28 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + } + // CraftBukkit end + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.allayRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.allayRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.allayControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ // Purpur end - Ridables ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +@@ -227,6 +262,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("allayBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("allayActivityUpdate"); +diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +index 901e4e9897e76cd4158d7f8bb8ec829df8ff8196..767a730baa8a7694ed7d5f05b70118da1f4288cc 100644 +--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -82,6 +82,23 @@ public class Armadillo extends Animal { + return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 12.0).add(Attributes.MOVEMENT_SPEED, 0.14); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.armadilloRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.armadilloRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.armadilloControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 2e5291af79a04ff7ebfc533596a008b404571214..3837397563bf3d568c120ae4e4e38d1a6dc7a8b2 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -121,6 +121,23 @@ public class Axolotl extends Animal implements Bucketable { + this.lookControl = new Axolotl.AxolotlLookControl(this, 20); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.axolotlRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.axolotlControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ // Purpur end - Ridables ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return 0.0F; +@@ -328,6 +345,7 @@ public class Axolotl extends Animal implements Bucketable { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("axolotlBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("axolotlActivityUpdate"); +@@ -564,23 +582,31 @@ public class Axolotl extends Animal implements Bucketable { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (!Axolotl.this.isPlayingDead()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } + + static class AxolotlMoveControl extends SmoothSwimmingMoveControl { + private final Axolotl axolotl; ++ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur - Ridables + + public AxolotlMoveControl(Axolotl axolotl) { + super(axolotl, 85, 10, 0.1F, 0.5F, false); + this.axolotl = axolotl; ++ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(axolotl, 0.5D); // Purpur - Ridables + } + + @Override + public void tick() { ++ // Purpur start - Ridables ++ if (axolotl.getRider() != null && axolotl.isControllable()) { ++ waterController.purpurTick(axolotl.getRider()); ++ return; ++ } ++ // Purpur end - Ridables + if (!this.axolotl.isPlayingDead()) { + super.tick(); + } +diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java +index 7b9280526af353c3ab1f32e5195499e773731352..bd3dc923058b884afcfad08062230182810c65a2 100644 +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -153,6 +153,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + + public Bee(EntityType type, Level level) { + super(type, level); ++ final org.purpurmc.purpur.controller.FlyingMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this, 0.25F, 1.0F, false); // Purpur - Ridables + // Paper start - Fix MC-167279 + class BeeFlyingMoveControl extends FlyingMoveControl { + public BeeFlyingMoveControl(final Mob mob, final int maxTurn, final boolean hoversInPlace) { +@@ -161,11 +162,24 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + + @Override + public void tick() { ++ // Purpur start - Ridables ++ if (mob.getRider() != null && mob.isControllable()) { ++ flyingController.purpurTick(mob.getRider()); ++ return; ++ } ++ // Purpur end - Ridables + if (this.mob.getY() <= Bee.this.level().getMinY()) { + this.mob.setNoGravity(false); + } + super.tick(); + } ++ ++ // Purpur start - Ridables ++ @Override ++ public boolean hasWanted() { ++ return mob.getRider() != null || !mob.isControllable() || super.hasWanted(); ++ } ++ // Purpur end - Ridables + } + this.moveControl = new BeeFlyingMoveControl(this, 20, true); + // Paper end - Fix MC-167279 +@@ -177,6 +191,40 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + this.setPathfindingMalus(PathType.FENCE, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.beeRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.beeRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.beeControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.beeMaxY; ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +@@ -191,6 +239,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(0, new Bee.BeeAttackGoal(this, 1.4F, true)); + this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal()); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); +@@ -208,6 +257,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + this.goalSelector.addGoal(7, new Bee.BeeGrowCropGoal()); + this.goalSelector.addGoal(8, new Bee.BeeWanderGoal()); + this.goalSelector.addGoal(9, new FloatGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new Bee.BeeHurtByOtherGoal(this).setAlertOthers()); + this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this)); + this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); +@@ -1085,15 +1135,15 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + } + +- class BeeLookControl extends LookControl { ++ class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + BeeLookControl(final Mob mob) { + super(mob); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (!Bee.this.isAngry()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + +diff --git a/net/minecraft/world/entity/animal/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 98721e4d406385108b3d63e9e811f730454090e7..a6a66084323435697b3185d7b86acaf5c7453719 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -90,6 +90,13 @@ public class Camel extends AbstractHorse { + groundPathNavigation.setCanWalkOverFences(true); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index b44367e42793cec9af35f50dffff479b4be7b728..4961bb85b91f68075cf0d22440d6377d9fcb7721 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -73,9 +73,27 @@ public class Chicken extends Animal { + this.setPathfindingMalus(PathType.WATER, 0.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.chickenRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.chickenRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.chickenControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new PanicGoal(this, 1.4)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); + this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, itemStack -> itemStack.is(ItemTags.CHICKEN_FOOD), false)); +diff --git a/net/minecraft/world/entity/animal/cow/AbstractCow.java b/net/minecraft/world/entity/animal/cow/AbstractCow.java +index f6f251227db315b58bee45f8011624a347eb8fea..a25ef3bc802759f61947ef8242c19e9320693d35 100644 +--- a/net/minecraft/world/entity/animal/cow/AbstractCow.java ++++ b/net/minecraft/world/entity/animal/cow/AbstractCow.java +@@ -38,6 +38,7 @@ public abstract class AbstractCow extends Animal { + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new PanicGoal(this, 2.0)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); + this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, itemStack -> level().purpurConfig.cowFeedMushrooms > 0 && (itemStack.is(net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) || itemStack.is(net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem())) || itemStack.is(ItemTags.COW_FOOD), false)); // Purpur - Cows eat mushrooms +@@ -83,13 +84,14 @@ public abstract class AbstractCow extends Animal { + + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { ++ if (getRider() != null) return InteractionResult.PASS; // Purpur - Ridables + ItemStack itemInHand = player.getItemInHand(hand); + if (itemInHand.is(Items.BUCKET) && !this.isBaby()) { + // CraftBukkit start - Got milk? + org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, itemInHand, Items.MILK_BUCKET, hand); + if (event.isCancelled()) { + player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + // CraftBukkit end + player.playSound(SoundEvents.COW_MILK, 1.0F, 1.0F); +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index f37322e0d90ea055a643b144dd87578df6a0bcc9..0268063bb0db8c30c594a7d75d0d11f7236c3a68 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -29,6 +29,23 @@ public class Cow extends AbstractCow { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.cowRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.cowRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.cowControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java +index ea6299e728d7c7e7a52d7c65d759407ba27c8eac..1a9f5f17e46af831bc6621c83c57e5436397dbc2 100644 +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -61,6 +61,23 @@ public class MushroomCow extends AbstractCow implements Shearable { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.mooshroomRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.mooshroomRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.mooshroomControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); +@@ -121,7 +138,7 @@ public class MushroomCow extends AbstractCow implements Shearable { + java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); + org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); + if (event != null) { +- if (event.isCancelled()) return InteractionResult.PASS; ++ if (event.isCancelled()) return tryRide(player, hand); // Purpur - Ridables + drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); + // Paper end - custom shear drops + } +diff --git a/net/minecraft/world/entity/animal/dolphin/Dolphin.java b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +index 45b6707ce9944a6a69f51196c3323821648d500b..656335a3bd021efc538ab407673c09a83178ed7c 100644 +--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java ++++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +@@ -76,14 +76,82 @@ public class Dolphin extends AgeableWaterCreature { + private static final boolean DEFAULT_GOT_FISH = false; + @Nullable public BlockPos treasurePos; + private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance ++ private int spitCooldown; // Purpur - Ridables + + public Dolphin(EntityType type, Level level) { + super(type, level); +- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); ++ // Purpur start - Ridables ++ class DolphinMoveControl extends SmoothSwimmingMoveControl { ++ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterMoveControllerWASD; ++ private final Dolphin dolphin; ++ ++ public DolphinMoveControl(Dolphin dolphin, int pitchChange, int yawChange, float speedInWater, float speedInAir, boolean buoyant) { ++ super(dolphin, pitchChange, yawChange, speedInWater, speedInAir, buoyant); ++ this.dolphin = dolphin; ++ this.waterMoveControllerWASD = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(dolphin); ++ } ++ ++ @Override ++ public void tick() { ++ if (dolphin.getRider() != null && dolphin.isControllable()) { ++ purpurTick(dolphin.getRider()); ++ } else { ++ super.tick(); ++ } ++ } ++ ++ public void purpurTick(Player rider) { ++ if (dolphin.getAirSupply() < 150) { ++ // if drowning override player WASD controls to find air ++ super.tick(); ++ } else { ++ waterMoveControllerWASD.purpurTick(rider); ++ dolphin.setDeltaMovement(dolphin.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); ++ } ++ } ++ }; ++ this.moveControl = new DolphinMoveControl(this, 85, 10, 0.02F, 0.1F, true); ++ // Purpur end - Ridables + this.lookControl = new SmoothSwimmingLookControl(this, 10); + this.setCanPickUpLoot(true); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.dolphinRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.dolphinControllable; ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (spitCooldown == 0 && getRider() != null) { ++ spitCooldown = level().purpurConfig.dolphinSpitCooldown; ++ ++ org.bukkit.craftbukkit.entity.CraftPlayer player = (org.bukkit.craftbukkit.entity.CraftPlayer) getRider().getBukkitEntity(); ++ if (!player.hasPermission("allow.special.dolphin")) { ++ return false; ++ } ++ ++ org.bukkit.Location loc = player.getEyeLocation(); ++ loc.setPitch(loc.getPitch() - 10); ++ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(10).add(loc.toVector()); ++ ++ org.purpurmc.purpur.entity.projectile.DolphinSpit spit = new org.purpurmc.purpur.entity.projectile.DolphinSpit(level(), this); ++ spit.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), level().purpurConfig.dolphinSpitSpeed, 5.0F); ++ ++ level().addFreshEntity(spit); ++ playSound(SoundEvents.DOLPHIN_ATTACK, 1.0F, 1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F); ++ return true; ++ } ++ return false; ++ } ++ // Purpur end - Ridables ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +@@ -158,6 +226,7 @@ public class Dolphin extends AgeableWaterCreature { + this.goalSelector.addGoal(0, new BreathAirGoal(this)); + this.goalSelector.addGoal(0, new TryFindWaterGoal(this)); + this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - Dolphins naturally aggressive to players chance ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this)); + this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0)); + this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0, 10)); +@@ -168,6 +237,7 @@ public class Dolphin extends AgeableWaterCreature { + this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal()); + this.goalSelector.addGoal(8, new FollowBoatGoal(this)); + this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0, 1.0)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Guardian.class).setAlertOthers()); + this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Dolphins naturally aggressive to players chance + } +@@ -213,7 +283,7 @@ public class Dolphin extends AgeableWaterCreature { + + @Override + protected boolean canRide(Entity entity) { +- return true; ++ return boardingCooldown <= 0; // Purpur - make dolphin honor ride cooldown like all other non-boss mobs; + } + + @Override +@@ -242,6 +312,11 @@ public class Dolphin extends AgeableWaterCreature { + @Override + public void tick() { + super.tick(); ++ // Purpur start - Ridables ++ if (spitCooldown > 0) { ++ spitCooldown--; ++ } ++ // Purpur end - Ridables + if (this.isNoAi()) { + this.setAirSupply(this.getMaxAirSupply()); + } else { +diff --git a/net/minecraft/world/entity/animal/equine/AbstractHorse.java b/net/minecraft/world/entity/animal/equine/AbstractHorse.java +index e837c63631c637238b9fe0ba05984af5e0d2b833..a1dce0a5ce1fcd0a2ff7104b3592ffd5c948db34 100644 +--- a/net/minecraft/world/entity/animal/equine/AbstractHorse.java ++++ b/net/minecraft/world/entity/animal/equine/AbstractHorse.java +@@ -128,11 +128,21 @@ public abstract class AbstractHorse extends Animal implements HasCustomInventory + + protected AbstractHorse(EntityType type, Level level) { + super(type, level); ++ this.moveControl = new net.minecraft.world.entity.ai.control.MoveControl(this); // Purpur - use vanilla controller ++ this.lookControl = new net.minecraft.world.entity.ai.control.LookControl(this); // Purpur - use vanilla controller + this.createInventory(); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return false; // vanilla handles ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new AbstractHorse.MountPanicGoal(1.2)); + this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0, AbstractHorse.class)); +@@ -143,6 +153,7 @@ public abstract class AbstractHorse extends Animal implements HasCustomInventory + if (this.canPerformRearing()) { + this.goalSelector.addGoal(9, new RandomStandGoal(this)); + } ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables + + this.addBehaviourGoals(); + } +diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java +index 55844fe13c403a55d135ec4ff4731d88601b205e..8aec9f254c82993632e68368d37b8c9bee7869cc 100644 +--- a/net/minecraft/world/entity/animal/equine/Donkey.java ++++ b/net/minecraft/world/entity/animal/equine/Donkey.java +@@ -16,6 +16,13 @@ public class Donkey extends AbstractChestedHorse { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java +index 1763543942a25a788c0f90241db75ddad70a7da8..cc50151ce6e6daffc1ecd41eb89a0d2f159f651e 100644 +--- a/net/minecraft/world/entity/animal/equine/Horse.java ++++ b/net/minecraft/world/entity/animal/equine/Horse.java +@@ -50,6 +50,13 @@ public class Horse extends AbstractHorse { + this.setPathfindingMalus(PathType.DAMAGE_OTHER, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.horseRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void randomizeAttributes(RandomSource random) { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt)); +diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java +index cceb66525a4d017b2db21bd301e63670c639223f..bba6493eee2f605faac0d49d665117d2f2c41213 100644 +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -82,7 +82,58 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + super(type, level); + this.getNavigation().setRequiredPathLength(40.0F); + this.maxDomestication = 30; // Paper - Missing entity API; configure max temper instead of a hardcoded value ++ // Purpur start - Ridables ++ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this) { ++ @Override ++ public void tick() { ++ if (entity.getRider() != null && entity.isControllable() && isSaddled()) { ++ purpurTick(entity.getRider()); ++ } else { ++ vanillaTick(); ++ } ++ } ++ }; ++ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { ++ @Override ++ public void tick() { ++ if (entity.getRider() != null && entity.isControllable() && isSaddled()) { ++ purpurTick(entity.getRider()); ++ } else { ++ vanillaTick(); ++ } ++ } ++ }; ++ // Purpur end - Ridables ++ } ++ ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.llamaRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.llamaRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.llamaControllable; ++ } ++ ++ @Override ++ public boolean isSaddled() { ++ return super.isWearingBodyArmor() || this.isTamed(); ++ } ++ ++ @Nullable ++ @Override ++ public LivingEntity getControllingPassenger() { ++ Entity firstPassenger = this.getFirstPassenger(); ++ return !this.isNoAi() && firstPassenger instanceof net.minecraft.world.entity.Mob mob && firstPassenger.canControlVehicle() ? mob : null; + } ++ // Purpur end - Ridables + + public boolean isTraderLlama() { + return false; +@@ -120,6 +171,7 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2)); + this.goalSelector.addGoal(2, new LlamaFollowCaravanGoal(this, 2.1F)); + this.goalSelector.addGoal(3, new RangedAttackGoal(this, 1.25, 40, 20.0F)); +@@ -130,6 +182,7 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.7)); + this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(9, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.LlamaHasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new Llama.LlamaHurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new Llama.LlamaAttackWolfGoal(this)); + } +diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java +index b5444b88d30e7d375495bdbbe1469beb72beb87a..60c151af9e51ba1dd1063344a3f5c021b6a48440 100644 +--- a/net/minecraft/world/entity/animal/equine/Mule.java ++++ b/net/minecraft/world/entity/animal/equine/Mule.java +@@ -15,6 +15,13 @@ public class Mule extends AbstractChestedHorse { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.muleRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.MULE_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +index 0afb4c836e697f820696bd760cca761520bc8250..3d98259439c3bdb97ab2c66734daf90a56b290ef 100644 +--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +@@ -43,6 +43,13 @@ public class SkeletonHorse extends AbstractHorse { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isTamed() { ++ return super.isTamed() || this.level().purpurConfig.skeletonHorseRidable; ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F); + } +@@ -62,6 +69,7 @@ public class SkeletonHorse extends AbstractHorse { + + @Override + protected void addBehaviourGoals() { ++ if (level().purpurConfig.skeletonHorseCanSwim) goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables + } + + @Override +diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java +index 44f85dc4075e9fc000dc89ba01e2039c01989dde..68b72c18a3880dead3b32b646a2f6a09d4b98c44 100644 +--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java ++++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java +@@ -31,6 +31,28 @@ public class TraderLlama extends Llama { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.traderLlamaRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.traderLlamaRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.traderLlamaControllable; ++ } ++ ++ @Override ++ public boolean isSaddled() { ++ return super.isSaddled() || isTamed(); ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean isTraderLlama() { + return true; +diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +index 4cfae7e877c6545780a8b9fdb3e326edb8cc7943..c1a13047c673cafc5de873215dd368eae6ac8b5e 100644 +--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java ++++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +@@ -52,6 +52,18 @@ public class ZombieHorse extends AbstractHorse { + this.setPathfindingMalus(PathType.DAMAGE_OTHER, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieHorseRidableInWater; ++ } ++ ++ @Override ++ public boolean isTamed() { ++ return super.isTamed() || this.level().purpurConfig.zombieHorseRidable; ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0); + } +diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java +index 6acd39413ab4fed1eaf251d3b6d9b9e184060e5c..2fe8b8382b7cb4056f4d430cf632ecf413e8e25b 100644 +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -95,10 +95,36 @@ public class Cat extends TamableAnimal { + this.reassessTameGoals(); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.catRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.catRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.catControllable; ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ setInSittingPose(false); ++ setLying(false); ++ setRelaxStateOne(false); ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true); + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5)); + this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this)); +@@ -111,6 +137,7 @@ public class Cat extends TamableAnimal { + this.goalSelector.addGoal(10, new BreedGoal(this, 0.8)); + this.goalSelector.addGoal(11, new WaterAvoidingRandomStrollGoal(this, 0.8, 1.0000001E-5F)); + this.goalSelector.addGoal(12, new LookAtPlayerGoal(this, Player.class, 10.0F)); ++ this.targetSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Rabbit.class, false, null)); + this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); + } +@@ -373,6 +400,7 @@ public class Cat extends TamableAnimal { + + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { ++ if (getRider() != null) return InteractionResult.PASS; // Purpur - Ridables + ItemStack itemInHand = player.getItemInHand(hand); + Item item = itemInHand.getItem(); + if (this.isTame()) { +diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java +index 94fdbae92dabf7505a7c7b518d4c07b4f68c8e9c..22fa29aa785eda8fb4a895d36413626da8a49a0e 100644 +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -66,6 +66,23 @@ public class Ocelot extends Animal { + this.reassessTrustingGoals(); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.ocelotRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ocelotRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.ocelotControllable; ++ } ++ // Purpur end - Ridables ++ + public boolean isTrusting() { + return this.entityData.get(DATA_TRUSTING); + } +@@ -97,12 +114,14 @@ public class Ocelot extends Animal { + protected void registerGoals() { + this.temptGoal = new Ocelot.OcelotTemptGoal(this, 0.6, itemStack -> itemStack.is(ItemTags.OCELOT_FOOD), true); + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(3, this.temptGoal); + this.goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3F)); + this.goalSelector.addGoal(8, new OcelotAttackGoal(this)); + this.goalSelector.addGoal(9, new BreedGoal(this, 0.8)); + this.goalSelector.addGoal(10, new WaterAvoidingRandomStrollGoal(this, 0.8, 1.0000001E-5F)); + this.goalSelector.addGoal(11, new LookAtPlayerGoal(this, Player.class, 10.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Chicken.class, false)); + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, false, false, Turtle.BABY_ON_LAND_SELECTOR)); + } +diff --git a/net/minecraft/world/entity/animal/fish/AbstractFish.java b/net/minecraft/world/entity/animal/fish/AbstractFish.java +index 7b4215058325812e8dc785277d1ece0fd5dd6ea3..970904ce07ebc6aabc97166284cbb855d2976f09 100644 +--- a/net/minecraft/world/entity/animal/fish/AbstractFish.java ++++ b/net/minecraft/world/entity/animal/fish/AbstractFish.java +@@ -91,6 +91,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + @Override + protected void registerGoals() { + super.registerGoals(); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(0, new PanicGoal(this, 1.25)); + this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 1.6, 1.4, EntitySelector.NO_SPECTATORS)); + this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this)); +@@ -103,7 +104,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + + @Override + protected void travelInWater(Vec3 travelVector, double gravity, boolean isFalling, double previousY) { +- this.moveRelative(0.01F, travelVector); ++ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, travelVector); // Purpur - Ridables + this.move(MoverType.SELF, this.getDeltaMovement()); + this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); + if (this.getTarget() == null) { +@@ -160,7 +161,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + protected void playStepSound(BlockPos pos, BlockState block) { + } + +- static class FishMoveControl extends MoveControl { ++ static class FishMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur - Ridables + private final AbstractFish fish; + + FishMoveControl(AbstractFish mob) { +@@ -168,14 +169,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + this.fish = mob; + } + ++ // Purpur start - Ridables + @Override +- public void tick() { ++ public void purpurTick(Player rider) { ++ super.purpurTick(rider); ++ fish.setDeltaMovement(fish.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); ++ } ++ // Purpur end - Ridables ++ ++ @Override ++ public void vanillaTick() { // Purpur - Ridables + if (this.fish.isEyeInFluid(FluidTags.WATER)) { + this.fish.setDeltaMovement(this.fish.getDeltaMovement().add(0.0, 0.005, 0.0)); + } + + if (this.operation == MoveControl.Operation.MOVE_TO && !this.fish.getNavigation().isDone()) { +- float f = (float)(this.speedModifier * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); ++ float f = (float)(this.getSpeedModifier() * this.fish.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables + this.fish.setSpeed(Mth.lerp(0.125F, this.fish.getSpeed(), f)); + double d = this.wantedX - this.fish.getX(); + double d1 = this.wantedY - this.fish.getY(); +diff --git a/net/minecraft/world/entity/animal/fish/Cod.java b/net/minecraft/world/entity/animal/fish/Cod.java +index 9a473f29931059e36a6fe8fa541c4706ababf59d..f73b3f5ae65f7793ff25145c72fb35e3daec8494 100644 +--- a/net/minecraft/world/entity/animal/fish/Cod.java ++++ b/net/minecraft/world/entity/animal/fish/Cod.java +@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.codRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.codControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + public ItemStack getBucketItemStack() { + return new ItemStack(Items.COD_BUCKET); +diff --git a/net/minecraft/world/entity/animal/fish/Pufferfish.java b/net/minecraft/world/entity/animal/fish/Pufferfish.java +index d8280671bb36d57b86a5d212d8deae9acc745bd4..1eaa5e5fb65b18e5042d69b5dbfea15a7271c0aa 100644 +--- a/net/minecraft/world/entity/animal/fish/Pufferfish.java ++++ b/net/minecraft/world/entity/animal/fish/Pufferfish.java +@@ -47,6 +47,18 @@ public class Pufferfish extends AbstractFish { + this.refreshDimensions(); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.pufferfishRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.pufferfishControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/fish/Salmon.java b/net/minecraft/world/entity/animal/fish/Salmon.java +index c691912a8b109e7430ffb39bb832983cbf33fc8a..b362ce156765ad45b8c29b5dc2c6d9d99d2e1474 100644 +--- a/net/minecraft/world/entity/animal/fish/Salmon.java ++++ b/net/minecraft/world/entity/animal/fish/Salmon.java +@@ -40,6 +40,18 @@ public class Salmon extends AbstractSchoolingFish { + this.refreshDimensions(); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.salmonRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.salmonControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + public int getMaxSchoolSize() { + return 5; +diff --git a/net/minecraft/world/entity/animal/fish/TropicalFish.java b/net/minecraft/world/entity/animal/fish/TropicalFish.java +index e3961892ffc05bef4947bda75445ca5e255dce11..3281e75c48fb6cafdba088254009e5b4285b3819 100644 +--- a/net/minecraft/world/entity/animal/fish/TropicalFish.java ++++ b/net/minecraft/world/entity/animal/fish/TropicalFish.java +@@ -77,6 +77,18 @@ public class TropicalFish extends AbstractSchoolingFish { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.tropicalFishRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.tropicalFishControllable; ++ } ++ // Purpur end - Ridables ++ + public static String getPredefinedName(int variantId) { + return "entity.minecraft.tropical_fish.predefined." + variantId; + } +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index 6d0eb7e0310b3fdd234dee5b8bf993d1c5ef27f1..bf00cf5e6fd8b9143e1b327ef7b90ae055d264ff 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -151,6 +151,44 @@ public class Fox extends Animal { + this.getNavigation().setRequiredPathLength(32.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.foxRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.foxRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.foxControllable; ++ } ++ ++ @Override ++ public float getJumpPower() { ++ return getRider() != null && this.isControllable() ? 0.5F : super.getJumpPower(); ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ setCanPickUpLoot(false); ++ clearStates(); ++ setIsPouncing(false); ++ spitOutItem(getItemBySlot(EquipmentSlot.MAINHAND)); ++ setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); ++ } ++ ++ @Override ++ public void onDismount(Player rider) { ++ super.onDismount(rider); ++ setCanPickUpLoot(true); ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +@@ -170,6 +208,7 @@ public class Fox extends Animal { + this, AbstractFish.class, 20, false, false, (entity, level) -> entity instanceof AbstractSchoolingFish + ); + this.goalSelector.addGoal(0, new Fox.FoxFloatGoal()); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(0, new ClimbOnTopOfPowderSnowGoal(this, this.level())); + this.goalSelector.addGoal(1, new Fox.FaceplantGoal()); + this.goalSelector.addGoal(2, new Fox.FoxPanicGoal(2.2)); +@@ -195,6 +234,7 @@ public class Fox extends Animal { + this.goalSelector.addGoal(11, new Fox.FoxSearchForItemsGoal()); + this.goalSelector.addGoal(12, new Fox.FoxLookAtPlayerGoal(this, Player.class, 24.0F)); + this.goalSelector.addGoal(13, new Fox.PerchAndSearchGoal()); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector + .addGoal( + 3, +@@ -1119,15 +1159,15 @@ public class Fox extends Animal { + } + } + +- public class FoxLookControl extends LookControl { ++ public class FoxLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + public FoxLookControl() { + super(Fox.this); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (!Fox.this.isSleeping()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + +@@ -1163,15 +1203,15 @@ public class Fox extends Animal { + } + } + +- class FoxMoveControl extends MoveControl { ++ class FoxMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + public FoxMoveControl() { + super(Fox.this); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (Fox.this.canMove()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index 09d817aa34d4a6d8a49d614087f9942d75e673e8..5b4bb6fd100abc569c5da167735a220014bf5d8c 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -105,6 +105,8 @@ public class Frog extends Animal { + public final AnimationState croakAnimationState = new AnimationState(); + public final AnimationState tongueAnimationState = new AnimationState(); + public final AnimationState swimIdleAnimationState = new AnimationState(); ++ private org.purpurmc.purpur.controller.MoveControllerWASD purpurLandController; // Purpur - Ridables ++ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - Ridables + + public Frog(EntityType type, Level level) { + super(type, level); +@@ -112,7 +114,55 @@ public class Frog extends Animal { + this.setPathfindingMalus(PathType.WATER, 4.0F); + this.setPathfindingMalus(PathType.TRAPDOOR, -1.0F); + this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); ++ // Purpur start - Ridables ++ this.purpurLandController = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.2F); ++ this.purpurWaterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F); ++ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) { ++ @Override ++ public void tick() { ++ net.minecraft.world.entity.player.Player rider = mob.getRider(); ++ if (rider != null && mob.isControllable()) { ++ if (mob.isInWater()) { ++ purpurWaterController.purpurTick(rider); ++ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, -0.005D, 0.0D)); ++ } else { ++ purpurLandController.purpurTick(rider); ++ } ++ } else { ++ super.tick(); ++ } ++ } ++ }; ++ // Purpur end - Ridables ++ } ++ ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.frogRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.frogRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.frogControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ ++ @Override ++ public float getJumpPower() { ++ return (getRider() != null && isControllable()) ? level().purpurConfig.frogRidableJumpHeight * this.getBlockJumpFactor() : super.getJumpPower(); + } ++ // Purpur end - Ridables + + @Override + protected Brain.Provider brainProvider() { +@@ -204,6 +254,7 @@ public class Frog extends Animal { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("frogBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("frogActivityUpdate"); +@@ -373,7 +424,7 @@ public class Frog extends Animal { + return level.getBlockState(pos.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(level, pos); + } + +- class FrogLookControl extends LookControl { ++ class FrogLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + FrogLookControl(final Mob mob) { + super(mob); + } +diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java +index f65cfb908aac16e6df4d209c6b5c3ee5065289d3..9b5205d7f481d4cd8fe454ae588dc2a89bc613bf 100644 +--- a/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -65,13 +65,50 @@ public class Tadpole extends AbstractFish { + MemoryModuleType.IS_PANICKING + ); + public boolean ageLocked; // Paper ++ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurController; // Purpur - Ridables + + public Tadpole(EntityType type, Level level) { + super(type, level); +- this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); ++ // Purpur start - Ridables ++ this.purpurController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(this, 0.5F); ++ this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true) { ++ @Override ++ public void tick() { ++ Player rider = mob.getRider(); ++ if (rider != null && mob.isControllable()) { ++ purpurController.purpurTick(rider); ++ mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.002D, 0.0D)); ++ } else { ++ super.tick(); ++ } ++ } ++ }; ++ // Purpur end - Ridables + this.lookControl = new SmoothSwimmingLookControl(this, 10); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.tadpoleRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.tadpoleRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.tadpoleControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ // Purpur end - Ridables ++ + @Override + protected PathNavigation createNavigation(Level level) { + return new WaterBoundPathNavigation(this, level); +@@ -101,6 +138,7 @@ public class Tadpole extends AbstractFish { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("tadpoleBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("tadpoleActivityUpdate"); +diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index 566390c9258c1132c9ffa2df8ecc3e713963787f..6e4d611c959960e8d13f79704c176b41ea5c12c4 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -115,6 +115,23 @@ public class Goat extends Animal { + .orElseGet(() -> new ItemStack(Items.GOAT_HORN)); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.goatRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.goatRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.goatControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +@@ -191,6 +208,7 @@ public class Goat extends Animal { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("goatBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("goatActivityUpdate"); +diff --git a/net/minecraft/world/entity/animal/golem/CopperGolem.java b/net/minecraft/world/entity/animal/golem/CopperGolem.java +index ad88672368b66ac99f437f8af48b9fd1dcc32c8a..4dc1ce5e07e23134cf3ed68e78a055c3b75fdc83 100644 +--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java +@@ -108,6 +108,28 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + } + // Purpur end - Summoner API + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.copperGolemRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.copperGolemRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.copperGolemControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0); + } +@@ -198,6 +220,7 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("copperGolemBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("copperGolemActivityUpdate"); +@@ -237,7 +260,7 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); + org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); + if (event != null) { +- if (event.isCancelled()) return InteractionResult.PASS; ++ if (event.isCancelled()) return tryRide(player, hand); // Purpur - Ridables + drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); + } + this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); +@@ -273,6 +296,8 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + } + } + ++ if (level().purpurConfig.villagerRidable && itemInHand.isEmpty()) return tryRide(player, hand); // Purpur - Ridables ++ + return super.mobInteract(player, hand); + } + } +diff --git a/net/minecraft/world/entity/animal/golem/IronGolem.java b/net/minecraft/world/entity/animal/golem/IronGolem.java +index 32425f0aaa748c7f80f2e5cf95ef27238fe50489..363bf7781c8785bc6fdbc65941794a5a02f8a57a 100644 +--- a/net/minecraft/world/entity/animal/golem/IronGolem.java ++++ b/net/minecraft/world/entity/animal/golem/IronGolem.java +@@ -74,9 +74,28 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + } + // Purpur end - Summoner API + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.ironGolemRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ironGolemRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.ironGolemControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options ++ if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true)); + this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9, 32.0F)); + this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6, false)); +@@ -84,6 +103,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + this.goalSelector.addGoal(5, new OfferFlowerGoal(this)); + this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this)); + this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); +@@ -271,12 +291,12 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + protected InteractionResult mobInteract(Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + if (!itemInHand.is(Items.IRON_INGOT)) { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } else { + float health = this.getHealth(); + this.heal(25.0F); + if (this.getHealth() == health) { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } else { + float f = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F; + this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0F, f); +diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java +index c474b69dd4e2c7893d51627403425e9fd38042ef..d8a3b7d329757bb84ee4d53671c89f211e8581ab 100644 +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -62,12 +62,31 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + } + // Purpur end - Summoner API + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.snowGolemRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snowGolemRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.snowGolemControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur - Snow Golem rate of fire config + this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F)); + this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entity, level) -> entity instanceof Enemy)); + } + +@@ -112,6 +131,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + return; + } + ++ if (getRider() != null && this.isControllable() && !level().purpurConfig.snowGolemLeaveTrailWhenRidden) return; // Purpur - don't leave snow trail when being ridden + BlockState blockState = Blocks.SNOW.defaultBlockState(); + + for (int i = 0; i < 4; i++) { +@@ -154,7 +174,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); + if (event != null) { + if (event.isCancelled()) { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); + // Paper end - custom shear drops +@@ -175,7 +195,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + return InteractionResult.SUCCESS; + // Purpur end - Snowman drop and put back pumpkin + } else { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + } + +diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +index 5a4a96895c1d2f7538bf644dd133645bc831b582..0a75d3790a4bf4812e4f3b27ec50d67fdccf812d 100644 +--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java ++++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +@@ -126,6 +126,13 @@ public class HappyGhast extends Animal { + this.removeAllGoals(goal -> true); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.happyGhastRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void ageBoundaryReached() { + if (this.isBaby()) { +diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java +index c5425b36e96e4f41f0ed7d468f53ea3de6b9ef17..93503f6b4eea2cb2ae6c01279e847c307920c35d 100644 +--- a/net/minecraft/world/entity/animal/panda/Panda.java ++++ b/net/minecraft/world/entity/animal/panda/Panda.java +@@ -108,6 +108,32 @@ public class Panda extends Animal { + } + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.pandaRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pandaRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.pandaControllable; ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ setForwardMot(0.0F); ++ sit(false); ++ eat(false); ++ setOnBack(false); ++ } ++ // Purpur end - Ridables ++ + @Override + protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) { + return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot(); +@@ -260,6 +286,7 @@ public class Panda extends Animal { + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(2, new Panda.PandaPanicGoal(this, 2.0)); + this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0)); + this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2F, true)); +@@ -275,6 +302,7 @@ public class Panda extends Animal { + this.goalSelector.addGoal(12, new Panda.PandaRollGoal(this)); + this.goalSelector.addGoal(13, new FollowParentGoal(this, 1.25)); + this.goalSelector.addGoal(14, new WaterAvoidingRandomStrollGoal(this, 1.0)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new Panda.PandaHurtByTargetGoal(this).setAlertOthers()); + } + +@@ -617,7 +645,7 @@ public class Panda extends Animal { + public InteractionResult mobInteract(Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + if (this.isScared()) { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } else if (this.isOnBack()) { + this.setOnBack(false); + return InteractionResult.SUCCESS; +@@ -653,7 +681,7 @@ public class Panda extends Animal { + + return InteractionResult.SUCCESS_SERVER; + } else { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + } + +@@ -958,7 +986,7 @@ public class Panda extends Animal { + } + } + +- static class PandaMoveControl extends MoveControl { ++ static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private final Panda panda; + + public PandaMoveControl(Panda mob) { +@@ -967,9 +995,9 @@ public class Panda extends Animal { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.panda.canPerformAction()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java +index 0337ddcd664cd0329d98286214d1aa4daf83e1ea..6a4d170cd1e4dde2af30895f9fbac9dd599d0602 100644 +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -137,12 +137,68 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + + public Parrot(EntityType type, Level level) { + super(type, level); +- this.moveControl = new FlyingMoveControl(this, 10, false); ++ // Purpur start - Ridables ++ final org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD flyingController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); ++ class ParrotMoveControl extends FlyingMoveControl { ++ public ParrotMoveControl(Mob entity, int maxPitchChange, boolean noGravity) { ++ super(entity, maxPitchChange, noGravity); ++ } ++ ++ @Override ++ public void tick() { ++ if (mob.getRider() != null && mob.isControllable()) { ++ flyingController.purpurTick(mob.getRider()); ++ } else { ++ super.tick(); ++ } ++ } ++ ++ @Override ++ public boolean hasWanted() { ++ return mob.getRider() != null && mob.isControllable() ? getForwardMot() != 0 || getStrafeMot() != 0 : super.hasWanted(); ++ } ++ } ++ this.moveControl = new ParrotMoveControl(this, 10, false); ++ // Purpur end - Ridables + this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F); + this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0F); + this.setPathfindingMalus(PathType.COCOA, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.parrotRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.parrotRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.parrotControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.parrotMaxY; ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 2; ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.25, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ // Purpur end - Ridables ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +@@ -162,9 +218,11 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + + @Override + protected void registerGoals() { +- this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25)); ++ //this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25)); // Purpur - move down + this.goalSelector.addGoal(0, new FloatGoal(this)); + if (this.level().purpurConfig.parrotBreedable) this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); // Purpur - Breedable parrots ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.25D)); // Purpur - Ridables + this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(2, new FollowOwnerGoal(this, 1.0, 5.0F, 1.0F)); +diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java +index 943bd459554e9f3374978e595ba369a479d44941..2a3fc6ad3a12a3cc9815b66b659e3c9ce2afa7a8 100644 +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -63,9 +63,27 @@ public class Pig extends Animal implements ItemSteerable { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.pigRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pigRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.pigControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new PanicGoal(this, 1.25)); + this.goalSelector.addGoal(3, new BreedGoal(this, 1.0)); + this.goalSelector.addGoal(4, new TemptGoal(this, 1.2, itemStack -> itemStack.is(Items.CARROT_ON_A_STICK), false)); +diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +index df4b4a4d32019ef3a667841a0ce4485a8325e897..718531a324c36ac65a93af5c12e0c0a3948606cb 100644 +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -61,6 +61,7 @@ public class PolarBear extends Animal implements NeutralMob { + private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39); + private long persistentAngerEndTime; + private @Nullable EntityReference persistentAngerTarget; ++ private int standTimer = 0; // Purpur - Ridables + + public PolarBear(EntityType type, Level level) { + super(type, level); +@@ -89,6 +90,34 @@ public class PolarBear extends Animal implements NeutralMob { + } + // Purpur end - Breedable Polar Bears + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.polarBearRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.polarBearRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.polarBearControllable; ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (!isStanding()) { ++ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0) { ++ setStanding(true); ++ playSound(SoundEvents.POLAR_BEAR_WARNING, 1.0F, 1.0F); ++ } ++ } ++ return false; ++ } ++ // Purpur end - Ridables ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +@@ -103,6 +132,7 @@ public class PolarBear extends Animal implements NeutralMob { + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal()); + this.goalSelector.addGoal(1, new PanicGoal(this, 2.0, mob -> mob.isBaby() ? DamageTypeTags.PANIC_CAUSES : DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES)); + // Purpur start - Breedable Polar Bears +@@ -115,6 +145,7 @@ public class PolarBear extends Animal implements NeutralMob { + this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new PolarBear.PolarBearHurtByTargetGoal()); + this.targetSelector.addGoal(2, new PolarBear.PolarBearAttackPlayersGoal()); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); +@@ -232,6 +263,12 @@ public class PolarBear extends Animal implements NeutralMob { + if (!this.level().isClientSide()) { + this.updatePersistentAnger((ServerLevel)this.level(), true); + } ++ ++ // Purpur start - Ridables ++ if (isStanding() && --standTimer <= 0) { ++ setStanding(false); ++ } ++ // Purpur end - Ridables + } + + @Override +@@ -251,6 +288,7 @@ public class PolarBear extends Animal implements NeutralMob { + + public void setStanding(boolean standing) { + this.entityData.set(DATA_STANDING_ID, standing); ++ standTimer = standing ? 20 : -1; // Purpur - Ridables + } + + public float getStandingAnimationScale(float partialTick) { +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index d5e7599c23405eb5a4519e2dfd93ddbe2853fa3b..16fbee742f28cf4571effb66265daeee64bae9b7 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -94,6 +94,7 @@ public class Rabbit extends Animal { + private boolean wasOnGround; + private int jumpDelayTicks; + public int moreCarrotTicks = 0; ++ private boolean actualJump; // Purpur - Ridables + + public Rabbit(EntityType type, Level level) { + super(type, level); +@@ -102,9 +103,55 @@ public class Rabbit extends Animal { + // this.setSpeedModifier(0.0); // CraftBukkit + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.rabbitRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.rabbitRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.rabbitControllable; ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (onGround) { ++ actualJump = true; ++ jumpFromGround(); ++ actualJump = false; ++ } ++ return true; ++ } ++ ++ private void handleJumping() { ++ if (onGround) { ++ RabbitJumpControl jumpController = (RabbitJumpControl) jumpControl; ++ if (!wasOnGround) { ++ setJumping(false); ++ jumpController.setCanJump(false); ++ } ++ if (!jumpController.wantJump()) { ++ if (moveControl.hasWanted()) { ++ startJumping(); ++ } ++ } else if (!jumpController.canJump()) { ++ jumpController.setCanJump(true); ++ } ++ } ++ wasOnGround = onGround; ++ } ++ // Purpur end - Ridables ++ + @Override + public void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); + this.goalSelector.addGoal(1, new Rabbit.RabbitPanicGoal(this, 2.2)); + this.goalSelector.addGoal(2, new BreedGoal(this, 0.8)); +@@ -119,6 +166,14 @@ public class Rabbit extends Animal { + + @Override + protected float getJumpPower() { ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable()) { ++ if (getForwardMot() < 0) { ++ setSpeed(getForwardMot() * 2F); ++ } ++ return actualJump ? 0.5F : 0.3F; ++ } ++ // Purpur end - Ridables + float f = 0.3F; + if (this.moveControl.getSpeedModifier() <= 0.6) { + f = 0.2F; +@@ -186,6 +241,12 @@ public class Rabbit extends Animal { + + @Override + public void customServerAiStep(ServerLevel level) { ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable()) { ++ handleJumping(); ++ return; ++ } ++ // Purpur end - Ridables + if (this.jumpDelayTicks > 0) { + this.jumpDelayTicks--; + } +@@ -511,7 +572,7 @@ public class Rabbit extends Animal { + } + } + +- static class RabbitMoveControl extends MoveControl { ++ static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private final Rabbit rabbit; + private double nextJumpSpeed; + +@@ -521,14 +582,14 @@ public class Rabbit extends Animal { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.rabbit.onGround() && !this.rabbit.jumping && !((Rabbit.RabbitJumpControl)this.rabbit.jumpControl).wantJump()) { + this.rabbit.setSpeedModifier(0.0); + } else if (this.hasWanted() || this.operation == MoveControl.Operation.JUMPING) { + this.rabbit.setSpeedModifier(this.nextJumpSpeed); + } + +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + + @Override +diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java +index 8f4e68b1a910cd56d4c57540c0a09a8724d13738..a33800517d5fb5ab008e22b77be2f079901c1a99 100644 +--- a/net/minecraft/world/entity/animal/sheep/Sheep.java ++++ b/net/minecraft/world/entity/animal/sheep/Sheep.java +@@ -63,10 +63,28 @@ public class Sheep extends Animal implements Shearable { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.sheepRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.sheepRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.sheepControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.eatBlockGoal = new EatBlockGoal(this); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new PanicGoal(this, 1.25)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); + this.goalSelector.addGoal(3, new TemptGoal(this, 1.1, stack -> stack.is(ItemTags.SHEEP_FOOD), false)); +diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +index a4d60a0dddabd7981d2db28af6b1d88d64d6e806..3d5cf62bd9bd99a978b7dc535675178deb695af0 100644 +--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java ++++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +@@ -89,6 +89,23 @@ public class Sniffer extends Animal { + this.setPathfindingMalus(PathType.DAMAGE_CAUTIOUS, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.snifferRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.snifferRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.snifferControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +@@ -467,6 +484,7 @@ public class Sniffer extends Animal { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("snifferBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.popPush("snifferActivityUpdate"); + SnifferAi.updateActivity(this); +diff --git a/net/minecraft/world/entity/animal/squid/GlowSquid.java b/net/minecraft/world/entity/animal/squid/GlowSquid.java +index 2b1ece8ff62376a0851c18c39f43882abf2abaca..c5a8080aa0fa1014602ba76fb4d8ad69b926dbfa 100644 +--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java ++++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java +@@ -37,6 +37,19 @@ public class GlowSquid extends Squid { + } + // Purpur end - Flying squids! Oh my! + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.glowSquidRidable; ++ } ++ ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.glowSquidControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected ParticleOptions getInkParticle() { + return ParticleTypes.GLOW_SQUID_INK; +diff --git a/net/minecraft/world/entity/animal/squid/Squid.java b/net/minecraft/world/entity/animal/squid/Squid.java +index 504573bf4cbdf1debfa9f9bd071fa40d443b860d..69d87bd27a95bb9e92cd24fb7973a9b06714567e 100644 +--- a/net/minecraft/world/entity/animal/squid/Squid.java ++++ b/net/minecraft/world/entity/animal/squid/Squid.java +@@ -71,9 +71,32 @@ public class Squid extends AgeableWaterCreature { + } + // Purpur end - Flying squids! Oh my! + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.squidRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.squidControllable; ++ } ++ ++ protected static void rotateVectorAroundY(org.bukkit.util.Vector vector, double degrees) { ++ double rad = Math.toRadians(degrees); ++ double cos = Math.cos(rad); ++ double sine = Math.sin(rad); ++ double x = vector.getX(); ++ double z = vector.getZ(); ++ vector.setX(cos * x - sine * z); ++ vector.setZ(sine * x + cos * z); ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new Squid.SquidFleeGoal()); + } + +@@ -325,6 +348,37 @@ public class Squid extends AgeableWaterCreature { + + @Override + public void tick() { ++ // Purpur start - Ridables ++ net.minecraft.world.entity.player.Player rider = squid.getRider(); ++ if (rider != null && squid.isControllable()) { ++ if (rider.jumping) { ++ squid.onSpacebar(); ++ } ++ float forward = rider.getForwardMot(); ++ float strafe = rider.getStrafeMot(); ++ float speed = (float) squid.getAttributeValue(Attributes.MOVEMENT_SPEED) * 10F; ++ if (forward < 0.0F) { ++ speed *= -0.5; ++ } ++ org.bukkit.util.Vector dir = rider.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(speed / 20.0F); ++ if (strafe != 0.0F) { ++ if (forward == 0.0F) { ++ dir.setY(0); ++ rotateVectorAroundY(dir, strafe > 0.0F ? -90 : 90); ++ } else if (forward < 0.0F) { ++ rotateVectorAroundY(dir, strafe > 0.0F ? 45 : -45); ++ } else { ++ rotateVectorAroundY(dir, strafe > 0.0F ? -45 : 45); ++ } ++ } ++ if (forward != 0.0F || strafe != 0.0F) { ++ squid.movementVector = new Vec3((float) dir.getX(), (float) dir.getY(), (float) dir.getZ()); ++ } else { ++ squid.movementVector = Vec3.ZERO; ++ } ++ return; ++ } ++ // Purpur end - Ridables + int noActionTime = this.squid.getNoActionTime(); + if (noActionTime > 100) { + this.squid.movementVector = Vec3.ZERO; +diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java +index fdbc70322eb653d2cc576090c27d1cb67bfce1f6..7699967db327cf80940bf30d8e0c734b43fd1e8f 100644 +--- a/net/minecraft/world/entity/animal/turtle/Turtle.java ++++ b/net/minecraft/world/entity/animal/turtle/Turtle.java +@@ -87,6 +87,23 @@ public class Turtle extends Animal { + this.moveControl = new Turtle.TurtleMoveControl(this); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.turtleRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.turtleRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.turtleControllable; ++ } ++ // Purpur end - Ridables ++ + public void setHomePos(BlockPos homePos) { + this.homePos = homePos; + } +@@ -145,6 +162,7 @@ public class Turtle extends Animal { + + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(0, new Turtle.TurtlePanicGoal(this, 1.2)); + this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0)); + this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0)); +@@ -485,12 +503,14 @@ public class Turtle extends Animal { + } + } + +- static class TurtleMoveControl extends MoveControl { ++ static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private final Turtle turtle; ++ private final org.purpurmc.purpur.controller.WaterMoveControllerWASD waterController; // Purpur - Ridables + + TurtleMoveControl(Turtle mob) { + super(mob); + this.turtle = mob; ++ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur - Ridables + } + + private void updateSpeed() { +@@ -509,7 +529,7 @@ public class Turtle extends Animal { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + this.updateSpeed(); + if (this.operation == MoveControl.Operation.MOVE_TO && !this.turtle.getNavigation().isDone()) { + double d = this.wantedX - this.turtle.getX(); +@@ -523,7 +543,7 @@ public class Turtle extends Animal { + float f = (float)(Mth.atan2(d2, d) * 180.0F / (float)Math.PI) - 90.0F; + this.turtle.setYRot(this.rotlerp(this.turtle.getYRot(), f, 90.0F)); + this.turtle.yBodyRot = this.turtle.getYRot(); +- float f1 = (float)(this.speedModifier * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED)); ++ float f1 = (float)(this.getSpeedModifier() * this.turtle.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables + this.turtle.setSpeed(Mth.lerp(0.125F, this.turtle.getSpeed(), f1)); + this.turtle.setDeltaMovement(this.turtle.getDeltaMovement().add(0.0, this.turtle.getSpeed() * d1 * 0.1, 0.0)); + } +diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java +index 725624f63a2fbf0bd489a92e2c5862c82d72556c..44b650de646ea605e41f39f3d99d8f550c9a8e19 100644 +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -186,9 +186,32 @@ public class Wolf extends TamableAnimal implements NeutralMob { + } + // Purpur end - Configurable default collar color + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.wolfRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wolfRidableInWater; ++ } ++ ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ setInSittingPose(false); ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.wolfControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5, DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES)); + this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5, 1.5)); +@@ -201,6 +224,7 @@ public class Wolf extends TamableAnimal implements NeutralMob { + this.goalSelector.addGoal(9, new BegGoal(this, 8.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(10, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); + this.targetSelector.addGoal(3, new HurtByTargetGoal(this).setAlertOthers()); +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index b7b76dd193820c9a3f22349572ba443619114d58..b8ddcc460ff1ae1cbfde70d3d8c16486bb2d3f8c 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -90,6 +90,7 @@ public class EnderDragon extends Mob implements Enemy { + private final net.minecraft.world.level.Explosion explosionSource; // Paper - reusable source for CraftTNTPrimed.getSource() + @Nullable private BlockPos podium; + // Paper end ++ private boolean hadRider; // Purpur - Ridables + + public EnderDragon(EntityType type, Level level) { + super(EntityType.ENDER_DRAGON, level); +@@ -106,6 +107,37 @@ public class EnderDragon extends Mob implements Enemy { + this.noPhysics = true; + this.phaseManager = new EnderDragonPhaseManager(this); + this.explosionSource = new net.minecraft.world.level.ServerExplosion(level.getMinecraftWorld(), this, null, null, new Vec3(Double.NaN, Double.NaN, Double.NaN), Float.NaN, true, net.minecraft.world.level.Explosion.BlockInteraction.DESTROY); // Paper ++ ++ // Purpur start - Ridables ++ this.moveControl = new org.purpurmc.purpur.controller.FlyingMoveControllerWASD(this) { ++ @Override ++ public void vanillaTick() { ++ // dragon doesn't use the controller. do nothing ++ } ++ }; ++ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { ++ @Override ++ public void vanillaTick() { ++ // dragon doesn't use the controller. do nothing ++ } ++ ++ @Override ++ public void purpurTick(Player rider) { ++ setYawPitch(rider.getYRot() - 180F, rider.xRotO * 0.5F); ++ } ++ }; ++ // Purpur end - Ridables ++ } ++ ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.enderDragonRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.enderDragonRidableInWater; + } + + public void setDragonFight(EndDragonFight dragonFight) { +@@ -120,6 +152,17 @@ public class EnderDragon extends Mob implements Enemy { + return this.fightOrigin; + } + ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.enderDragonControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.enderDragonMaxY; ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0).add(Attributes.CAMERA_DISTANCE, 16.0); + } +@@ -169,6 +212,37 @@ public class EnderDragon extends Mob implements Enemy { + + @Override + public void aiStep() { ++ // Purpur start - Ridables ++ boolean hasRider = getRider() != null && this.isControllable(); ++ if (hasRider) { ++ if (!hadRider) { ++ hadRider = true; ++ noPhysics = false; ++ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(4.0F, 2.0F); ++ } ++ ++ // dragon doesn't use controllers, so must tick manually ++ moveControl.tick(); ++ lookControl.tick(); ++ ++ moveRelative((float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F, new Vec3(-getStrafeMot(), getVerticalMot(), -getForwardMot())); ++ Vec3 mot = getDeltaMovement(); ++ setDeltaMovement(mot); ++ move(MoverType.PLAYER, mot); ++ ++ mot = mot.multiply(0.9F, 0.9F, 0.9F); ++ setDeltaMovement(mot); ++ ++ // control wing flap speed on client ++ phaseManager.setPhase(mot.x() * mot.x() + mot.z() * mot.z() < 0.005F ? EnderDragonPhase.HOVERING : EnderDragonPhase.HOLDING_PATTERN); ++ } else if (hadRider) { ++ hadRider = false; ++ noPhysics = true; ++ this.dimensions = net.minecraft.world.entity.EntityDimensions.scalable(16.0F, 8.0F); ++ phaseManager.setPhase(EnderDragonPhase.HOLDING_PATTERN); // HoldingPattern ++ } ++ // Purpur end - Ridables ++ + this.processFlappingMovement(); + if (this.level().isClientSide()) { + this.setHealth(this.getHealth()); +@@ -197,6 +271,8 @@ public class EnderDragon extends Mob implements Enemy { + + this.oFlapTime = this.flapTime; + if (this.isDeadOrDying()) { ++ if (hasRider) ejectPassengers(); // Purpur - Ridables ++ + float f = (this.random.nextFloat() - 0.5F) * 8.0F; + float f1 = (this.random.nextFloat() - 0.5F) * 4.0F; + float f2 = (this.random.nextFloat() - 0.5F) * 8.0F; +@@ -206,9 +282,9 @@ public class EnderDragon extends Mob implements Enemy { + Vec3 deltaMovement = this.getDeltaMovement(); + float f1 = 0.2F / ((float)deltaMovement.horizontalDistance() * 10.0F + 1.0F); + f1 *= (float)Math.pow(2.0, deltaMovement.y); +- if (this.phaseManager.getCurrentPhase().isSitting()) { ++ if (!hasRider && this.phaseManager.getCurrentPhase().isSitting()) { // Purpur - Ridables + this.flapTime += 0.1F; +- } else if (this.inWall) { ++ } else if (!hasRider && this.inWall) { // Purpur - Ridables + this.flapTime += f1 * 0.5F; + } else { + this.flapTime += f1; +@@ -219,7 +295,7 @@ public class EnderDragon extends Mob implements Enemy { + this.flapTime = 0.5F; + } else { + this.flightHistory.record(this.getY(), this.getYRot()); +- if (this.level() instanceof ServerLevel serverLevel1) { ++ if (this.level() instanceof ServerLevel serverLevel1 && !hasRider) { // Purpur - Ridables + DragonPhaseInstance currentPhase = this.phaseManager.getCurrentPhase(); + currentPhase.doServerTick(serverLevel1); + if (this.phaseManager.getCurrentPhase() != currentPhase) { +@@ -294,7 +370,7 @@ public class EnderDragon extends Mob implements Enemy { + this.tickPart(this.body, sin1 * 0.5F, 0.0, -cos1 * 0.5F); + this.tickPart(this.wing1, cos1 * 4.5F, 2.0, sin1 * 4.5F); + this.tickPart(this.wing2, cos1 * -4.5F, 2.0, sin1 * -4.5F); +- if (this.level() instanceof ServerLevel serverLevel2 && this.hurtTime == 0) { ++ if (this.level() instanceof ServerLevel serverLevel2 && this.hurtTime == 0 && !hasRider) { // Purpur - Ridables + this.knockBack( + serverLevel2, + serverLevel2.getEntities( +@@ -344,9 +420,9 @@ public class EnderDragon extends Mob implements Enemy { + } + + if (this.level() instanceof ServerLevel serverLevel3) { +- this.inWall = this.checkWalls(serverLevel3, this.head.getBoundingBox()) ++ this.inWall = !hasRider && this.checkWalls(serverLevel3, this.head.getBoundingBox()) + | this.checkWalls(serverLevel3, this.neck.getBoundingBox()) +- | this.checkWalls(serverLevel3, this.body.getBoundingBox()); ++ | this.checkWalls(serverLevel3, this.body.getBoundingBox()); // Purpur - Ridables + if (this.dragonFight != null) { + this.dragonFight.updateDragon(this); + } +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java +index 41200efbaa29ee487c0383b261122e0701413865..871adbd10f49a4d59820c089da149a47de424656 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragonPart.java +@@ -27,6 +27,13 @@ public class EnderDragonPart extends Entity { + this.name = name; + } + ++ // Purpur start - Ridables ++ @Override ++ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { ++ return parentMob.isAlive() ? parentMob.tryRide(player, hand) : net.minecraft.world.InteractionResult.PASS; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + } +diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index f170db52921583050786050cf7fdaee439117bb8..dbf3c7049fae08dc5c4ac491a5fc8141ab177a22 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -72,6 +72,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + private final int[] nextHeadUpdate = new int[2]; + private final int[] idleHeadUpdates = new int[2]; + private int destroyBlocksTick; ++ private int shootCooldown = 0; // Purpur - Ridables + private boolean canPortal = false; // Paper + public final ServerBossEvent bossEvent = (ServerBossEvent)new ServerBossEvent( + this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS +@@ -81,9 +82,23 @@ public class WitherBoss extends Monster implements RangedAttackMob { + && entity.attackable(); + private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0).selector(LIVING_ENTITY_SELECTOR); + private java.util.@Nullable UUID summoner; // Purpur - Summoner API ++ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur - Ridables + + public WitherBoss(EntityType type, Level level) { + super(type, level); ++ // Purpur start - Ridables ++ this.purpurController = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.1F); ++ this.moveControl = new FlyingMoveControl(this, 10, false) { ++ @Override ++ public void tick() { ++ if (mob.getRider() != null && mob.isControllable()) { ++ purpurController.purpurTick(mob.getRider()); ++ } else { ++ super.tick(); ++ } ++ } ++ }; ++ // Purpur end - Ridables + this.moveControl = new FlyingMoveControl(this, 10, false); + this.setHealth(this.getMaxHealth()); + this.xpReward = 50; +@@ -99,6 +114,105 @@ public class WitherBoss extends Monster implements RangedAttackMob { + } + // Purpur end - Summoner API + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.witherRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.witherControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.witherMaxY; ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED) * 5F; ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 0.5, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ this.entityData.set(DATA_TARGETS.get(0), 0); ++ this.entityData.set(DATA_TARGETS.get(1), 0); ++ this.entityData.set(DATA_TARGETS.get(2), 0); ++ getNavigation().stop(); ++ shootCooldown = 20; ++ } ++ ++ @Override ++ public boolean onClick(net.minecraft.world.InteractionHand hand) { ++ return shoot(getRider(), hand == net.minecraft.world.InteractionHand.MAIN_HAND ? new int[]{1} : new int[]{2}); ++ } ++ ++ public boolean shoot(@Nullable Player rider, int[] heads) { ++ if (shootCooldown > 0) { ++ return false; ++ } ++ ++ shootCooldown = 20; ++ if (rider == null) { ++ return false; ++ } ++ ++ org.bukkit.craftbukkit.entity.CraftHumanEntity player = rider.getBukkitEntity(); ++ if (!player.hasPermission("allow.special.wither")) { ++ return false; ++ } ++ ++ net.minecraft.world.phys.HitResult rayTrace = getRayTrace(120, net.minecraft.world.level.ClipContext.Fluid.NONE); ++ if (rayTrace == null) { ++ return false; ++ } ++ ++ Vec3 loc; ++ if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) { ++ BlockPos pos = ((net.minecraft.world.phys.BlockHitResult) rayTrace).getBlockPos(); ++ loc = new Vec3(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D); ++ } else if (rayTrace.getType() == net.minecraft.world.phys.HitResult.Type.ENTITY) { ++ Entity target = ((net.minecraft.world.phys.EntityHitResult) rayTrace).getEntity(); ++ loc = new Vec3(target.getX(), target.getY() + (target.getEyeHeight() / 2), target.getZ()); ++ } else { ++ org.bukkit.block.Block block = player.getTargetBlock(null, 120); ++ loc = new Vec3(block.getX() + 0.5D, block.getY() + 0.5D, block.getZ() + 0.5D); ++ } ++ ++ for (int head : heads) { ++ shoot(head, loc.x(), loc.y(), loc.z(), rider); ++ } ++ ++ return true; // handled ++ } ++ ++ public void shoot(int head, double x, double y, double z, Player rider) { ++ level().levelEvent(null, 1024, blockPosition(), 0); ++ double headX = getHeadX(head); ++ double headY = getHeadY(head); ++ double headZ = getHeadZ(head); ++ Vec3 vec3d = new Vec3(x - headX, y - headY, z - headZ); ++ WitherSkull skull = new WitherSkull(level(), this, vec3d.normalize()); ++ skull.setPosRaw(headX, headY, headZ); ++ level().addFreshEntity(skull); ++ } ++ // Purpur end - Ridables ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +@@ -109,11 +223,13 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(0, new WitherBoss.WitherDoNothingGoal()); + this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0, 40, 20.0F)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomFlyingGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 0, false, false, LIVING_ENTITY_SELECTOR)); + } +@@ -273,6 +389,15 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + @Override + protected void customServerAiStep(ServerLevel level) { ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable()) { ++ Vec3 mot = getDeltaMovement(); ++ setDeltaMovement(mot.x(), mot.y() + (getVerticalMot() > 0 ? 0.07D : 0.0D), mot.z()); ++ } ++ if (shootCooldown > 0) { ++ shootCooldown--; ++ } ++ // Purpur end - Ridables + if (this.getInvulnerableTicks() > 0) { + int i = this.getInvulnerableTicks() - 1; + this.bossEvent.setProgress(1.0F - i / 220.0F); +@@ -579,11 +704,11 @@ public class WitherBoss extends Monster implements RangedAttackMob { + } + + public int getAlternativeTarget(int head) { +- return this.entityData.get(DATA_TARGETS.get(head)); ++ return getRider() != null && this.isControllable() ? 0 : this.entityData.get(DATA_TARGETS.get(head)); // Purpur - Ridables + } + + public void setAlternativeTarget(int targetOffset, int newId) { +- this.entityData.set(DATA_TARGETS.get(targetOffset), newId); ++ if (getRider() == null || !this.isControllable()) this.entityData.set(DATA_TARGETS.get(targetOffset), newId); // Purpur - Ridables + } + + public boolean isPowered() { +diff --git a/net/minecraft/world/entity/monster/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java +index f5539b27261755380312b392fee836cbaaff2e56..1febee23188d91e2a27c182b97502e8c7ab696a2 100644 +--- a/net/minecraft/world/entity/monster/Blaze.java ++++ b/net/minecraft/world/entity/monster/Blaze.java +@@ -34,6 +34,7 @@ public class Blaze extends Monster { + + public Blaze(EntityType type, Level level) { + super(type, level); ++ this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur - Ridables + this.setPathfindingMalus(PathType.WATER, -1.0F); + this.setPathfindingMalus(PathType.LAVA, 8.0F); + this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F); +@@ -41,19 +42,55 @@ public class Blaze extends Monster { + this.xpReward = 10; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.blazeRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.blazeRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.blazeControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.blazeMaxY; ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED); ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(4, new Blaze.BlazeAttackGoal(this)); + this.goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 1.0)); + this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 0.0F)); + this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + } + + public static AttributeSupplier.Builder createAttributes() { +- return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0); ++ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0).add(Attributes.MOVEMENT_SPEED, 0.23F).add(Attributes.FOLLOW_RANGE, 48.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - Ridables + } + + @Override +@@ -118,6 +155,13 @@ public class Blaze extends Monster { + + @Override + protected void customServerAiStep(ServerLevel level) { ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable()) { ++ Vec3 mot = getDeltaMovement(); ++ setDeltaMovement(mot.x(), getVerticalMot() > 0 ? 0.07D : -0.07D, mot.z()); ++ return; ++ } ++ // Purpur end - Ridables + this.nextHeightOffsetChangeTick--; + if (this.nextHeightOffsetChangeTick <= 0) { + this.nextHeightOffsetChangeTick = 100; +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index 168003b7159b5e9ea3e3ca19d0b939ed1cfd2311..cfd09a5ff326cf4ecc248901696ce1f623a8e7c0 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -57,21 +57,98 @@ public class Creeper extends Monster { + public boolean droppedSkulls; + public @Nullable Entity entityIgniter; // CraftBukkit + private boolean exploding = false; // Purpur - Config to make Creepers explode on death ++ // Purpur start - Ridables ++ private int spacebarCharge = 0; ++ private int prevSpacebarCharge = 0; ++ private int powerToggleDelay = 0; ++ // Purpur end - Ridables + + public Creeper(EntityType type, Level level) { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.creeperRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creeperRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.creeperControllable; ++ } ++ ++ @Override ++ protected void customServerAiStep(ServerLevel world) { ++ if (powerToggleDelay > 0) { ++ powerToggleDelay--; ++ } ++ if (getRider() != null && this.isControllable()) { ++ if (getRider().getForwardMot() != 0 || getRider().getStrafeMot() != 0) { ++ spacebarCharge = 0; ++ setIgnited(false); ++ setSwellDir(-1); ++ } ++ if (spacebarCharge == prevSpacebarCharge) { ++ spacebarCharge = 0; ++ } ++ prevSpacebarCharge = spacebarCharge; ++ } ++ super.customServerAiStep(world); ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ setIgnited(false); ++ setSwellDir(-1); ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (powerToggleDelay > 0) { ++ return true; // just toggled power, do not jump or ignite ++ } ++ spacebarCharge++; ++ if (spacebarCharge > maxSwell - 2) { ++ spacebarCharge = 0; ++ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.powered.creeper")) { ++ powerToggleDelay = 20; ++ setPowered(!isPowered()); ++ setIgnited(false); ++ setSwellDir(-1); ++ return true; ++ } ++ } ++ if (!isIgnited()) { ++ if (getRider() != null && getRider().getForwardMot() == 0 && getRider().getStrafeMot() == 0 && ++ getRider().getBukkitEntity().hasPermission("allow.special.creeper")) { ++ setIgnited(true); ++ setSwellDir(1); ++ return true; ++ } ++ } ++ return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); + this.goalSelector.addGoal(2, new SwellGoal(this)); ++ this.goalSelector.addGoal(3, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0, 1.2)); + this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0, 1.2)); + this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true)); + this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); + } +@@ -314,6 +391,7 @@ public class Creeper extends Monster { + com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited); + if (event.callEvent()) { + this.entityData.set(DATA_IS_IGNITED, event.isIgnited()); ++ if (!event.isIgnited()) setSwellDir(-1); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/monster/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index 3a16eb269b733fcf1db5dbc2dfae13009e2278b5..e5d16ca14a40d520dce43dd3d9b6347aefbd25d7 100644 +--- a/net/minecraft/world/entity/monster/ElderGuardian.java ++++ b/net/minecraft/world/entity/monster/ElderGuardian.java +@@ -31,6 +31,18 @@ public class ElderGuardian extends Guardian { + } + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.elderGuardianRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.elderGuardianControllable; ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0); + } +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index 2d903c77017ae356eb309df3c2c43b91c4e52f2f..bd78d8c3e8c1d9b581c751d336322fbfe20dacef 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -89,9 +89,27 @@ public class EnderMan extends Monster implements NeutralMob { + this.setPathfindingMalus(PathType.WATER, -1.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.endermanRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermanRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.endermanControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this)); + this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false)); + this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 0.0F)); +@@ -99,6 +117,7 @@ public class EnderMan extends Monster implements NeutralMob { + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); + this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this)); + this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt)); + this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving, ignored) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur +@@ -263,7 +282,7 @@ public class EnderMan extends Monster implements NeutralMob { + + @Override + protected void customServerAiStep(ServerLevel level) { +- if (level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600) { ++ if ((getRider() == null || !this.isControllable()) && level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - Ridables - no random teleporting + float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); + if (lightLevelDependentMagicValue > 0.5F + && level.canSeeSky(this.blockPosition()) +@@ -375,6 +394,7 @@ public class EnderMan extends Monster implements NeutralMob { + public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + if (this.isInvulnerableTo(level, damageSource)) { + return false; ++ } else if (getRider() != null && this.isControllable()) { return super.hurtServer(level, damageSource, amount); // Purpur - no teleporting on damage + } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && damageSource.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height - Short enderman height + } else { + AbstractThrownPotion abstractThrownPotion1 = damageSource.getDirectEntity() instanceof AbstractThrownPotion abstractThrownPotion +diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java +index 980ec0e45581812c027bb2ce8cd25d74e5f10663..844ba943a2a752f9b25506b644a4b4abc981b4cb 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -47,14 +47,33 @@ public class Endermite extends Monster { + } + // Purpur end - Add back player spawned endermite API + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.endermiteRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.endermiteRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.endermiteControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); + this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false)); + this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + } +diff --git a/net/minecraft/world/entity/monster/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index b8774eb91d21e9f154cc70c5d3bd26de25be41c4..451627153610f2c8db2403f73545c9af6dfd2e4b 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -53,11 +53,35 @@ public class Ghast extends Mob implements Enemy { + this.moveControl = new Ghast.GhastMoveControl(this, false, () -> false); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.ghastRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ghastRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.ghastControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.ghastMaxY; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this)); + this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this)); + this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector + .addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entity, level) -> Math.abs(entity.getY() - this.getY()) <= 4.0)); + } +@@ -102,6 +126,15 @@ public class Ghast extends Mob implements Enemy { + @Override + public void travel(Vec3 travelVector) { + this.travelFlying(travelVector, 0.02F); ++ // Purpur start - Ridables ++ if (getRider() != null && this.isControllable() && !onGround) { ++ float speed = (float) getAttributeValue(Attributes.FLYING_SPEED); ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ // Purpur end - Ridables + } + + @Override +@@ -237,7 +270,7 @@ public class Ghast extends Mob implements Enemy { + } + } + +- public static class GhastMoveControl extends MoveControl { ++ public static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables + private final Mob ghast; + private int floatDuration; + private final boolean careful; +@@ -251,7 +284,7 @@ public class Ghast extends Mob implements Enemy { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.shouldBeStopped.getAsBoolean()) { + this.operation = MoveControl.Operation.WAIT; + this.ghast.stopInPlace(); +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index 0de6a20a3fed53bd11a0152de6953bfaecc85289..dcbe6bbce0baa4fad7fe180944beeb6ca4026f7d 100644 +--- a/net/minecraft/world/entity/monster/Giant.java ++++ b/net/minecraft/world/entity/monster/Giant.java +@@ -12,6 +12,29 @@ public class Giant extends Monster { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.giantRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.giantRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.giantControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 100.0) +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index 9c91fbfd39af513668bd94578810cef7e283abd9..4cf54e55de1ecc57061ad0c09e7966d6104f1cc2 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -65,14 +65,35 @@ public class Guardian extends Monster { + this.xpReward = 10; + this.setPathfindingMalus(PathType.WATER, 0.0F); + this.moveControl = new Guardian.GuardianMoveControl(this); ++ // Purpur start - Ridables ++ this.lookControl = new org.purpurmc.purpur.controller.LookControllerWASD(this) { ++ @Override ++ public void setYawPitch(float yaw, float pitch) { ++ super.setYawPitch(yaw, pitch * 0.35F); ++ } ++ }; ++ // Purpur end - Ridables + this.clientSideTailAnimation = this.random.nextFloat(); + this.clientSideTailAnimationO = this.clientSideTailAnimation; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.guardianRidable; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.guardianControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0); + this.randomStrollGoal = new RandomStrollGoal(this, 1.0, 80); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field + this.goalSelector.addGoal(5, moveTowardsRestrictionGoal); + this.goalSelector.addGoal(7, this.randomStrollGoal); +@@ -81,6 +102,7 @@ public class Guardian extends Monster { + this.goalSelector.addGoal(9, new RandomLookAroundGoal(this)); + this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); + moveTowardsRestrictionGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this))); + } + +@@ -346,7 +368,7 @@ public class Guardian extends Monster { + + @Override + protected void travelInWater(Vec3 travelVector, double gravity, boolean isFalling, double previousY) { +- this.moveRelative(0.1F, travelVector); ++ this.moveRelative(getRider() != null && this.isControllable() ? getSpeed() : 0.1F, travelVector); // Purpur - Ridables + this.move(MoverType.SELF, this.getDeltaMovement()); + this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); + if (!this.isMoving() && this.getTarget() == null) { +@@ -451,7 +473,7 @@ public class Guardian extends Monster { + } + } + +- static class GuardianMoveControl extends MoveControl { ++ static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur - Ridables + private final Guardian guardian; + + public GuardianMoveControl(Guardian mob) { +@@ -459,8 +481,17 @@ public class Guardian extends Monster { + this.guardian = mob; + } + ++ // Purpur start - Ridables + @Override +- public void tick() { ++ public void purpurTick(Player rider) { ++ super.purpurTick(rider); ++ guardian.setDeltaMovement(guardian.getDeltaMovement().add(0.0D, 0.005D, 0.0D)); ++ guardian.setMoving(guardian.getForwardMot() > 0.0F); // control tail speed ++ } ++ // Purpur end - Ridables ++ ++ @Override ++ public void vanillaTick() { // Purpur - Ridables + if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) { + Vec3 vec3 = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ()); + double len = vec3.length(); +@@ -470,7 +501,7 @@ public class Guardian extends Monster { + float f = (float)(Mth.atan2(vec3.z, vec3.x) * 180.0F / (float)Math.PI) - 90.0F; + this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F)); + this.guardian.yBodyRot = this.guardian.getYRot(); +- float f1 = (float)(this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); ++ float f1 = (float)(this.getSpeedModifier() * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables + float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1); + this.guardian.setSpeed(f2); + double d3 = Math.sin((this.guardian.tickCount + this.guardian.getId()) * 0.5) * 0.05; +diff --git a/net/minecraft/world/entity/monster/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index 969736a46c85b2fcaa641c2863e6000b51611df5..a3cd6d4b999fc49893794838c73370c19c6a66b6 100644 +--- a/net/minecraft/world/entity/monster/MagmaCube.java ++++ b/net/minecraft/world/entity/monster/MagmaCube.java +@@ -24,6 +24,28 @@ public class MagmaCube extends Slime { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.magmaCubeRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.magmaCubeRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.magmaCubeControllable; ++ } ++ ++ @Override ++ public float getJumpPower() { ++ return 0.42F * this.getBlockJumpFactor(); // from EntityLiving ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F); + } +@@ -76,6 +98,7 @@ public class MagmaCube extends Slime { + float f = this.getSize() * 0.1F; + this.setDeltaMovement(deltaMovement.x, this.getJumpPower() + f, deltaMovement.z); + this.needsSync = true; ++ this.actualJump = false; // Purpur - Ridables + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index d26b7970490a2a108affee11c3fb74e38509d92b..64e55d48bc81d6237970e86e6e1cc719831902fd 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -61,6 +61,52 @@ public class Phantom extends Mob implements Enemy { + this.lookControl = new Phantom.PhantomLookControl(this); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.phantomRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.phantomRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.phantomControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.phantomMaxY; ++ } ++ ++ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() { ++ return Monster.createMonsterAttributes().add(Attributes.FLYING_SPEED, 3.0D); ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (getRider() != null && getRider().getBukkitEntity().hasPermission("allow.special.phantom")) { ++ shoot(); ++ } ++ return false; ++ } ++ ++ public boolean shoot() { ++ org.bukkit.Location loc = ((org.bukkit.entity.LivingEntity) getBukkitEntity()).getEyeLocation(); ++ loc.setPitch(-loc.getPitch()); ++ org.bukkit.util.Vector target = loc.getDirection().normalize().multiply(100).add(loc.toVector()); ++ ++ org.purpurmc.purpur.entity.projectile.PhantomFlames flames = new org.purpurmc.purpur.entity.projectile.PhantomFlames(level(), this); ++ flames.canGrief = level().purpurConfig.phantomAllowGriefing; ++ flames.shoot(target.getX() - getX(), target.getY() - getY(), target.getZ() - getZ(), 1.0F, 5.0F); ++ level().addFreshEntity(flames); ++ return true; ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean isFlapping() { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +@@ -73,9 +119,11 @@ public class Phantom extends Mob implements Enemy { + + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal()); + this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal()); + this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal()); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal()); + } + +@@ -91,6 +139,7 @@ public class Phantom extends Mob implements Enemy { + + private void updatePhantomSizeInfo() { + this.refreshDimensions(); ++ if (level().purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur - Ridables - Phantom flames on swoop + this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(6 + this.getPhantomSize()); + } + +@@ -159,6 +208,15 @@ public class Phantom extends Mob implements Enemy { + @Override + public void travel(Vec3 travelVector) { + this.travelFlying(travelVector, 0.2F); ++ // Purpur start - Ridables ++ if (this.getRider() != null && this.isControllable() && !this.onGround) { ++ float speed = (float) this.getAttributeValue(Attributes.FLYING_SPEED); ++ this.setSpeed(speed); ++ Vec3 mot = this.getDeltaMovement(); ++ this.move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, speed, speed)); ++ this.setDeltaMovement(mot.scale(0.9D)); ++ } ++ // Purpur end - Ridables + } + + @Override +@@ -412,25 +470,42 @@ public class Phantom extends Mob implements Enemy { + } + } + +- static class PhantomLookControl extends LookControl { ++ static class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + public PhantomLookControl(Mob mob) { + super(mob); + } + ++ // Purpur start - Ridables ++ public void purpurTick(Player rider) { ++ setYawPitch(rider.getYRot(), -rider.xRotO * 0.75F); ++ } ++ // Purpur end - Ridables ++ + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + } + } + +- class PhantomMoveControl extends MoveControl { ++ class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables + private float speed = 0.1F; + + public PhantomMoveControl(final Mob mob) { + super(mob); + } + ++ // Purpur start - Ridables ++ public void purpurTick(Player rider) { ++ if (!Phantom.this.onGround) { ++ // phantom is always in motion when flying ++ // TODO - FIX THIS ++ // rider.setForward(1.0F); ++ } ++ super.purpurTick(rider); ++ } ++ // Purpur end - Ridables ++ + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (Phantom.this.horizontalCollision) { + Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F); + this.speed = 0.1F; +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index a10c693a8948d1b7c4bd06b957a08a9cea9170ad..f7807aac5a8f9c92ba77ac38c469ef14948197ac 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -72,15 +72,40 @@ public class Ravager extends Raider { + this.setPathfindingMalus(PathType.LEAVES, 0.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.ravagerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.ravagerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.ravagerControllable; ++ } ++ ++ @Override ++ public void onMount(Player rider) { ++ super.onMount(rider); ++ getNavigation().stop(); ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); + if (level().purpurConfig.ravagerAvoidRabbits) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.AvoidEntityGoal<>(this, net.minecraft.world.entity.animal.rabbit.Rabbit.class, 6.0F, 1.0D, 1.2D)); // Purpur - option to make ravagers afraid of rabbits ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(2, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)); + this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entity, level) -> !entity.isBaby())); +@@ -137,7 +162,7 @@ public class Ravager extends Raider { + @Override + public void aiStep() { + super.aiStep(); +- if (this.isAlive()) { ++ if (this.isAlive() && (getRider() == null || !this.isControllable())) { // Purpur - Ridables + if (this.isImmobile()) { + this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0); + } else { +diff --git a/net/minecraft/world/entity/monster/Shulker.java b/net/minecraft/world/entity/monster/Shulker.java +index 6e8400c4d523da8799d586c5c9019ae78710fbcb..9647741b67e1b4b7bbff0df22fc1bf3fb26afa73 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -108,12 +108,31 @@ public class Shulker extends AbstractGolem implements Enemy { + } + // Purpur end - Shulker change color with dye + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.shulkerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.shulkerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.shulkerControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true)); + this.goalSelector.addGoal(4, new Shulker.ShulkerAttackGoal()); + this.goalSelector.addGoal(7, new Shulker.ShulkerPeekGoal()); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, this.getClass()).setAlertOthers()); + this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this)); + this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this)); +@@ -708,7 +727,7 @@ public class Shulker extends AbstractGolem implements Enemy { + } + } + +- class ShulkerLookControl extends LookControl { ++ class ShulkerLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables + public ShulkerLookControl(final Mob mob) { + super(mob); + } +diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java +index 93b46d52d8019aef91798b796ab2f490f886a3e9..cf305d4a0a71f91caa31757dcd8ca5be84fc3f03 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -38,14 +38,33 @@ public class Silverfish extends Monster { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.silverfishRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.silverfishRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.silverfishControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this); + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); + this.goalSelector.addGoal(3, this.friendsGoal); + this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false)); + this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + } +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index e433eacd865f620a9c336a1976fd017b8053cf6a..0c56f9ae47b9263ac65fd3593911e2c8479fc157 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -60,6 +60,7 @@ public class Slime extends Mob implements Enemy { + public float oSquish; + private boolean wasOnGround = false; + private boolean canWander = true; // Paper - Slime pathfinder events ++ protected boolean actualJump; // Purpur - Ridables + + public Slime(EntityType type, Level level) { + super(type, level); +@@ -67,12 +68,48 @@ public class Slime extends Mob implements Enemy { + this.moveControl = new Slime.SlimeMoveControl(this); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.slimeRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.slimeRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.slimeControllable; ++ } ++ ++ @Override ++ public float getJumpPower() { ++ float height = super.getJumpPower(); ++ return getRider() != null && this.isControllable() && actualJump ? height * 1.5F : height; ++ } ++ ++ @Override ++ public boolean onSpacebar() { ++ if (onGround && getRider() != null && this.isControllable()) { ++ actualJump = true; ++ if (getRider().getForwardMot() == 0 || getRider().getStrafeMot() == 0) { ++ jumpFromGround(); // jump() here if not moving ++ } ++ } ++ return true; // do not jump() in wasd controller, let vanilla controller handle ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new Slime.SlimeFloatGoal(this)); + this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this)); + this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this)); + this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector + .addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entity, level) -> Math.abs(entity.getY() - this.getY()) <= 4.0)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); +@@ -359,6 +396,7 @@ public class Slime extends Mob implements Enemy { + Vec3 deltaMovement = this.getDeltaMovement(); + this.setDeltaMovement(deltaMovement.x, this.getJumpPower(), deltaMovement.z); + this.needsSync = true; ++ this.actualJump = false; // Purpur - Ridables + } + + @Override +@@ -522,7 +560,7 @@ public class Slime extends Mob implements Enemy { + } + } + +- static class SlimeMoveControl extends MoveControl { ++ static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private float yRot; + private int jumpDelay; + private final Slime slime; +@@ -540,21 +578,33 @@ public class Slime extends Mob implements Enemy { + } + + public void setWantedMovement(double speedModifier) { +- this.speedModifier = speedModifier; ++ this.setSpeedModifier(speedModifier); // Purpur - Ridables + this.operation = MoveControl.Operation.MOVE_TO; + } + + @Override + public void tick() { ++ // Purpur start - Ridables ++ if (slime.getRider() != null && slime.isControllable()) { ++ purpurTick(slime.getRider()); ++ if (slime.getForwardMot() != 0 || slime.getStrafeMot() != 0) { ++ if (jumpDelay > 10) { ++ jumpDelay = 6; ++ } ++ } else { ++ jumpDelay = 20; ++ } ++ } else { ++ // Purpur end - Ridables + this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F)); + this.mob.yHeadRot = this.mob.getYRot(); + this.mob.yBodyRot = this.mob.getYRot(); +- if (this.operation != MoveControl.Operation.MOVE_TO) { ++ } if ((slime.getRider() == null || !slime.isControllable()) && this.operation != MoveControl.Operation.MOVE_TO) { // Purpur - Ridables + this.mob.setZza(0.0F); + } else { + this.operation = MoveControl.Operation.WAIT; + if (this.mob.onGround()) { +- this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); ++ this.mob.setSpeed((float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - Ridables + if (this.jumpDelay-- <= 0) { + this.jumpDelay = this.slime.getJumpDelay(); + if (this.isAggressive) { +@@ -571,7 +621,7 @@ public class Slime extends Mob implements Enemy { + this.mob.setSpeed(0.0F); + } + } else { +- this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED))); ++ this.mob.setSpeed((float)(this.getSpeedModifier() * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED) * (slime.getRider() != null && slime.isControllable() && (slime.getRider().getForwardMot() != 0 || slime.getRider().getStrafeMot() != 0) ? 2.0D : 1.0D))); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index beaffb771e6c8d4b992437997ab27be218425c5a..46121b018c5dd87d888a3724df12a9af0d007772 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -92,6 +92,23 @@ public class Strider extends Animal implements ItemSteerable { + this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.striderRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.striderRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.striderControllable; ++ } ++ // Purpur end - Ridables ++ + public static boolean checkStriderSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +@@ -138,6 +155,7 @@ public class Strider extends Animal implements ItemSteerable { + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new PanicGoal(this, 1.65)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); + this.temptGoal = new TemptGoal(this, 1.4, itemStack -> itemStack.is(ItemTags.STRIDER_TEMPT_ITEMS), false); + this.goalSelector.addGoal(3, this.temptGoal); +@@ -414,7 +432,7 @@ public class Strider extends Animal implements ItemSteerable { + ItemStack itemInHand = player.getItemInHand(hand); + return (InteractionResult)(this.isEquippableInSlot(itemInHand, EquipmentSlot.SADDLE) + ? itemInHand.interactLivingEntity(player, this, hand) +- : InteractionResult.PASS); ++ : tryRide(player, hand)); // Purpur - Ridables + } else { + if (isFood && !this.isSilent()) { + this.level() +diff --git a/net/minecraft/world/entity/monster/Vex.java b/net/minecraft/world/entity/monster/Vex.java +index a6bfb39305732068c50ae99a28114b58b76d2cf7..d567e2b9843606c36a61c83a3a4e2d181f3eefd9 100644 +--- a/net/minecraft/world/entity/monster/Vex.java ++++ b/net/minecraft/world/entity/monster/Vex.java +@@ -58,6 +58,50 @@ public class Vex extends Monster implements TraceableEntity { + this.xpReward = 3; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.vexRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vexRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.vexControllable; ++ } ++ ++ @Override ++ public double getMaxY() { ++ return level().purpurConfig.vexMaxY; ++ } ++ ++ @Override ++ public void travel(Vec3 vec3) { ++ super.travel(vec3); ++ if (getRider() != null && this.isControllable()) { ++ float speed; ++ if (onGround) { ++ speed = (float) getAttributeValue(Attributes.MOVEMENT_SPEED) * 0.1F; ++ } else { ++ speed = (float) getAttributeValue(Attributes.FLYING_SPEED); ++ } ++ setSpeed(speed); ++ Vec3 mot = getDeltaMovement(); ++ move(net.minecraft.world.entity.MoverType.SELF, mot.multiply(speed, 1.0, speed)); ++ setDeltaMovement(mot.scale(0.9D)); ++ } ++ } ++ ++ @Override ++ public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) { ++ return false; // no fall damage please ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean isFlapping() { + return this.tickCount % TICKS_PER_FLAP == 0; +@@ -70,7 +114,7 @@ public class Vex extends Monster implements TraceableEntity { + + @Override + public void tick() { +- this.noPhysics = true; ++ this.noPhysics = getRider() == null || !this.isControllable(); // Purpur - Ridables + super.tick(); + this.noPhysics = false; + this.setNoGravity(true); +@@ -84,17 +128,19 @@ public class Vex extends Monster implements TraceableEntity { + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(4, new Vex.VexChargeAttackGoal()); + this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal()); + this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true)); + } + + public static AttributeSupplier.Builder createAttributes() { +- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0).add(Attributes.ATTACK_DAMAGE, 4.0); ++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0).add(Attributes.ATTACK_DAMAGE, 4.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur; + } + + @Override +@@ -293,13 +339,13 @@ public class Vex extends Monster implements TraceableEntity { + } + } + +- class VexMoveControl extends MoveControl { ++ class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables + public VexMoveControl(final Vex mob) { + super(mob); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.operation == MoveControl.Operation.MOVE_TO) { + Vec3 vec3 = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ()); + double len = vec3.length(); +@@ -307,7 +353,7 @@ public class Vex extends Monster implements TraceableEntity { + this.operation = MoveControl.Operation.WAIT; + Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5)); + } else { +- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3.scale(this.speedModifier * 0.05 / len))); ++ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3.scale(this.getSpeedModifier() * 0.05 / len))); // Purpur - Ridables + if (Vex.this.getTarget() == null) { + Vec3 deltaMovement = Vex.this.getDeltaMovement(); + Vex.this.setYRot(-((float)Mth.atan2(deltaMovement.x, deltaMovement.z)) * (180.0F / (float)Math.PI)); +diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 38991c0c17554c0b58980759eaeb3e9fee774420..bcb136e71db2272dc107304b52be41f83d6772b7 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -57,6 +57,23 @@ public class Witch extends Raider implements RangedAttackMob { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.witchRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witchRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.witchControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); +@@ -65,10 +82,12 @@ public class Witch extends Raider implements RangedAttackMob { + ); + this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, null); + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0, 60, 10.0F)); + this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(3, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class)); + this.targetSelector.addGoal(2, this.healRaidersGoal); + this.targetSelector.addGoal(3, this.attackPlayersGoal); +diff --git a/net/minecraft/world/entity/monster/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index 201ccf4f20bd9e48ed8c1d59d70f843c74e5287f..c4f009357e416ed4052e058d8bf3b8c3e9dfd4ae 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -87,6 +87,23 @@ public class Zoglin extends Monster implements HoglinBase { + this.xpReward = 5; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.zoglinRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zoglinRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.zoglinControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +@@ -251,6 +268,7 @@ public class Zoglin extends Monster implements HoglinBase { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("zoglinBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + this.updateActivity(); +diff --git a/net/minecraft/world/entity/monster/breeze/Breeze.java b/net/minecraft/world/entity/monster/breeze/Breeze.java +index d0019ebf0469f4070d323d03dbe54375b8d2ce73..6f22312d0a5ab38be4a07b93fdb29b7f36f67c71 100644 +--- a/net/minecraft/world/entity/monster/breeze/Breeze.java ++++ b/net/minecraft/world/entity/monster/breeze/Breeze.java +@@ -236,6 +236,7 @@ public class Breeze extends Monster { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("breezeBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.popPush("breezeActivityUpdate"); + BreezeAi.updateActivity(this); +diff --git a/net/minecraft/world/entity/monster/creaking/Creaking.java b/net/minecraft/world/entity/monster/creaking/Creaking.java +index 6775e62ae5b133bc44d730924b4821e0c0b8497c..01d3d638e194ec5e812eea0ab96b100f4752ea18 100644 +--- a/net/minecraft/world/entity/monster/creaking/Creaking.java ++++ b/net/minecraft/world/entity/monster/creaking/Creaking.java +@@ -103,6 +103,29 @@ public class Creaking extends Monster { + return this.getHomePos() != null; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.creakingRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.creakingRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.creakingControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ // Purpur end - Ridables ++ + @Override + protected BodyRotationControl createBodyControl() { + return new Creaking.CreakingBodyRotationControl(this); +@@ -547,28 +570,28 @@ public class Creaking extends Monster { + } + } + +- class CreakingLookControl extends LookControl { ++ class CreakingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables { + public CreakingLookControl(final Creaking mob) { + super(mob); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (Creaking.this.canMove()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } + +- class CreakingMoveControl extends MoveControl { ++ class CreakingMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + public CreakingMoveControl(final Creaking mob) { + super(mob); + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (Creaking.this.canMove()) { +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index d1749ac82cdbd73422e6229e98eb034978f50fc2..c84f009cb0498e9c1898e310f19cff80ec6989ea 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -97,6 +97,23 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + this.xpReward = 5; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.hoglinRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.hoglinRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.hoglinControllable; ++ } ++ // Purpur end - Ridables ++ + @VisibleForTesting + public void setTimeInOverworld(int timeInOverworld) { + this.timeInOverworld = timeInOverworld; +@@ -165,6 +182,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("hoglinBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + HoglinAi.updateActivity(this); +diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java +index f291887c07fee2ad8d00bc79822e709777dc3fc3..e40434d24c7a9ffe76c3410601942d0c8a963e30 100644 +--- a/net/minecraft/world/entity/monster/illager/Evoker.java ++++ b/net/minecraft/world/entity/monster/illager/Evoker.java +@@ -49,10 +49,28 @@ public class Evoker extends SpellcasterIllager { + this.xpReward = 10; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.evokerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.evokerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.evokerControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal()); + this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6, 1.0)); + this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 0.6, 1.0)); +@@ -62,6 +80,7 @@ public class Evoker extends SpellcasterIllager { + this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6)); + this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true).setUnseenMemoryTicks(300)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false).setUnseenMemoryTicks(300)); +diff --git a/net/minecraft/world/entity/monster/illager/Illusioner.java b/net/minecraft/world/entity/monster/illager/Illusioner.java +index 1f80b1732ae809ce9ea26b0e8ddd507d0a96dbef..8a80c202752f56ba174c00531993795a4e621910 100644 +--- a/net/minecraft/world/entity/monster/illager/Illusioner.java ++++ b/net/minecraft/world/entity/monster/illager/Illusioner.java +@@ -59,10 +59,28 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { + } + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.illusionerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.illusionerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.illusionerControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal()); + this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2)); + this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal()); +@@ -71,6 +89,7 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { + this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6)); + this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true).setUnseenMemoryTicks(300)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false).setUnseenMemoryTicks(300)); +diff --git a/net/minecraft/world/entity/monster/illager/Pillager.java b/net/minecraft/world/entity/monster/illager/Pillager.java +index e9207ed52cdf28bffcc6e6d9fc550285e1ee6294..0b97c4fe89c8549841cbb79ecb9383a9be7a44af 100644 +--- a/net/minecraft/world/entity/monster/illager/Pillager.java ++++ b/net/minecraft/world/entity/monster/illager/Pillager.java +@@ -65,16 +65,35 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.pillagerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.pillagerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.pillagerControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2)); + this.goalSelector.addGoal(2, new Raider.HoldGroundAttackGoal(this, 10.0F)); + this.goalSelector.addGoal(3, new RangedCrossbowAttackGoal<>(this, 1.0, 8.0F)); + this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6)); + this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 15.0F, 1.0F)); + this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 15.0F)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); +diff --git a/net/minecraft/world/entity/monster/illager/Vindicator.java b/net/minecraft/world/entity/monster/illager/Vindicator.java +index 0cdc52cf3b1bb9c00e6ca8c8e564de60e195d6fc..4162e7ee482829f0c5276a31d44ed4c5ee24c3cf 100644 +--- a/net/minecraft/world/entity/monster/illager/Vindicator.java ++++ b/net/minecraft/world/entity/monster/illager/Vindicator.java +@@ -57,15 +57,34 @@ public class Vindicator extends AbstractIllager { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.vindicatorRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.vindicatorRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.vindicatorControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2)); + this.goalSelector.addGoal(2, new Vindicator.VindicatorBreakDoorGoal(this)); + this.goalSelector.addGoal(3, new AbstractIllager.RaiderOpenDoorGoal(this)); + this.goalSelector.addGoal(4, new Raider.HoldGroundAttackGoal(this, 10.0F)); + this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, false)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true)); +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index fb2ff2b9712f95429bde04d35baddc2da3e3fb52..a42b84611836b970d0d7ddb602bd04d9ec850712 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -141,6 +141,23 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + this.xpReward = 5; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.piglinRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.piglinControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +@@ -319,6 +336,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("piglinBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + PiglinAi.updateActivity(this); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index d33c4d32c99b0b4553ce89746b443820f586ce73..e33809e9ccc4a65909f1c12ec4b0fdc4050aeee7 100644 +--- a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +@@ -65,6 +65,23 @@ public class PiglinBrute extends AbstractPiglin { + this.xpReward = 20; + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.piglinBruteRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.piglinBruteRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.piglinBruteControllable; ++ } ++ // Purpur end - Ridables ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 50.0) +@@ -116,6 +133,7 @@ public class PiglinBrute extends AbstractPiglin { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("piglinBruteBrain"); ++ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + PiglinBruteAi.updateActivity(this); +diff --git a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +index 1ba7777164c7fd8516c97e1bd964183df37c029f..1ab860be69bcc1ab5cc07418c2d7e733afdc482b 100644 +--- a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +@@ -75,12 +75,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(2, new RestrictSunGoal(this)); + this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0)); + this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0, 1.2)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); +diff --git a/net/minecraft/world/entity/monster/skeleton/Bogged.java b/net/minecraft/world/entity/monster/skeleton/Bogged.java +index c54ad2d6a271d70cc9eeb284f13e5d143f81a084..10e66643a918184d858a4e6c9de7a6c2664fbcaf 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Bogged.java ++++ b/net/minecraft/world/entity/monster/skeleton/Bogged.java +@@ -41,6 +41,23 @@ public class Bogged extends AbstractSkeleton implements Shearable { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.boggedRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.boggedRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.boggedControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Skeleton.java b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +index d995572427844540a98d76b2926eb15a018230fa..7f8298892565b073373ad68f9c1399a9c565c29a 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +@@ -26,6 +26,23 @@ public class Skeleton extends AbstractSkeleton { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.skeletonRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.skeletonRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.skeletonControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Stray.java b/net/minecraft/world/entity/monster/skeleton/Stray.java +index 24ae7263b2661899e7824ab5877f9b1c9f04d80f..23ceecffe8f1b758433e02b61cae421c518cc841 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Stray.java ++++ b/net/minecraft/world/entity/monster/skeleton/Stray.java +@@ -23,6 +23,23 @@ public class Stray extends AbstractSkeleton { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.strayRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.strayRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.strayControllable; ++ } ++ // Purpur end - Ridables ++ + public static boolean checkStraySpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +index 79326303fc1adf5a35343847a2401d193368b64f..d49200718e7fab29ce5e55278b7584458bfb10e7 100644 +--- a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +@@ -34,6 +34,23 @@ public class WitherSkeleton extends AbstractSkeleton { + this.setPathfindingMalus(PathType.LAVA, 8.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.witherSkeletonRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.witherSkeletonRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.witherSkeletonControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); +diff --git a/net/minecraft/world/entity/monster/spider/CaveSpider.java b/net/minecraft/world/entity/monster/spider/CaveSpider.java +index 6a1714d093fc35e6ca840b769723f7bffaea7ba6..17a9b046ccd8de37aa79501f7556a5a816dfa90f 100644 +--- a/net/minecraft/world/entity/monster/spider/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/spider/CaveSpider.java +@@ -26,6 +26,23 @@ public class CaveSpider extends Spider { + return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.caveSpiderRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.caveSpiderRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.caveSpiderControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean doHurtTarget(ServerLevel level, Entity target) { + if (super.doHurtTarget(level, target)) { +diff --git a/net/minecraft/world/entity/monster/spider/Spider.java b/net/minecraft/world/entity/monster/spider/Spider.java +index 64a84f73d48deb254345a5c25f3fe650591e64a1..7a8b9c83490a05629e4996844a575ba4c7e9f156 100644 +--- a/net/minecraft/world/entity/monster/spider/Spider.java ++++ b/net/minecraft/world/entity/monster/spider/Spider.java +@@ -52,15 +52,34 @@ public class Spider extends Monster { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.spiderRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.spiderRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.spiderControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); ++ this.goalSelector.addGoal(1, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Armadillo.class, 6.0F, 1.0, 1.2, livingEntity -> !((Armadillo)livingEntity).isScared())); + this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F)); + this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.targetSelector.addGoal(1, new HurtByTargetGoal(this)); + this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class)); + this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class)); +diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java +index 3b931c6105ea14add0d2a60dc19cc3601a092a3b..4f4bf16cfa1390f19a7e8745d86e42e24ffb3bbc 100644 +--- a/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/net/minecraft/world/entity/monster/warden/Warden.java +@@ -124,8 +124,32 @@ public class Warden extends Monster implements VibrationSystem { + this.setPathfindingMalus(PathType.LAVA, 8.0F); + this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F); + this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F); ++ this.moveControl = new org.purpurmc.purpur.controller.MoveControllerWASD(this, 0.5F); // Purpur - Ridables + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.wardenRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wardenRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.wardenControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables ++ } ++ // Purpur end - Ridables ++ + @Override + public Packet getAddEntityPacket(ServerEntity entity) { + return new ClientboundAddEntityPacket(this, entity, this.hasPose(Pose.EMERGING) ? 1 : 0); +@@ -280,6 +304,7 @@ public class Warden extends Monster implements VibrationSystem { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("wardenBrain"); ++ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + super.customServerAiStep(level); +@@ -382,6 +407,7 @@ public class Warden extends Monster implements VibrationSystem { + + @Contract("null->false") + public boolean canTargetEntity(@Nullable Entity entity) { ++ if (getRider() != null && isControllable()) return false; // Purpur - Ridables + return entity instanceof LivingEntity livingEntity + && this.level() == entity.level() + && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) +diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java +index 604d5e6a4962de61fb97988a2f3de2965908bada..03c801d5982eed73fd8f56f63ccab570e83b8a53 100644 +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -74,6 +74,23 @@ public class Drowned extends Zombie implements RangedAttackMob { + return Zombie.createAttributes().add(Attributes.STEP_HEIGHT, 1.0); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.drownedRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.drownedRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.drownedControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected PathNavigation createNavigation(Level level) { + return new AmphibiousPathNavigation(this, level); +@@ -433,7 +450,7 @@ public class Drowned extends Zombie implements RangedAttackMob { + } + } + +- static class DrownedMoveControl extends MoveControl { ++ static class DrownedMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private final Drowned drowned; + + public DrownedMoveControl(Drowned mob) { +@@ -442,7 +459,7 @@ public class Drowned extends Zombie implements RangedAttackMob { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + LivingEntity target = this.drowned.getTarget(); + if (this.drowned.wantsToSwim() && this.drowned.isInWater()) { + if (target != null && target.getY() > this.drowned.getY() || this.drowned.searchingForLand) { +@@ -462,7 +479,7 @@ public class Drowned extends Zombie implements RangedAttackMob { + float f = (float)(Mth.atan2(d2, d) * 180.0F / (float)Math.PI) - 90.0F; + this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F)); + this.drowned.yBodyRot = this.drowned.getYRot(); +- float f1 = (float)(this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); ++ float f1 = (float)(this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables + float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1); + this.drowned.setSpeed(f2); + this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(f2 * d * 0.005, f2 * d1 * 0.1, f2 * d2 * 0.005)); +@@ -471,7 +488,7 @@ public class Drowned extends Zombie implements RangedAttackMob { + this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0, -0.008, 0.0)); + } + +- super.tick(); ++ super.vanillaTick(); // Purpur - Ridables + } + } + } +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index f36d92f0f62354be5f4e39e768aabb1369cfe18c..f18ac33d31ae23db02654658840b89ba03736bf3 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -29,6 +29,23 @@ public class Husk extends Zombie { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.huskRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.huskRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.huskControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + public boolean isSunSensitive() { + return false; +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index c86fb77485a038b600877432c828dc7940cf26f4..b2d322bee9343079c0c21cc7b47493df9cfd861e 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -103,11 +103,30 @@ public class Zombie extends Monster { + this(EntityType.ZOMBIE, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.zombieRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.zombieControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + if (this.level().paperConfig().entities.behavior.zombiesTargetTurtleEggs) this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0, 3)); // Paper - Add zombie targets turtle egg config + this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables + this.addBehaviourGoals(); + } + +diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +index c73419613c67685dd78d7aa0a959267b4b9c1ae8..d91a1aec302a93326a357311f1743fa87ec92fa3 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -85,6 +85,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + super(type, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.zombieVillagerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombieVillagerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.zombieVillagerControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +index ef6a4aff03c1a85535b15e653714751e79f61090..e2c5301a465e28cea057f9afec7e9d78090e1c9e 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -64,6 +64,23 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + this.setPathfindingMalus(PathType.LAVA, 8.0F); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.zombifiedPiglinRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.zombifiedPiglinRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.zombifiedPiglinControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index e117ae1b114c7e2ca314a00335473efc41137f7f..6d4ef7b5d64639eb7fe400b2ed612c3b3552aa52 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -250,6 +250,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end - Lobotomize stuck villagers + ++ // Purpur start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.villagerRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.villagerRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.villagerControllable; ++ } ++ ++ @Override ++ protected void registerGoals() { ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ } ++ // Purpur end - Ridables ++ + @Override + public Brain getBrain() { + return (Brain)super.getBrain(); +@@ -357,7 +379,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } else { + this.isLobotomized = false; + } +- if (!inactive) { ++ if (!inactive && (getRider() == null || !this.isControllable())) { // Purpur - Ridables + this.getBrain().tick(level, this); // Paper - EAR 2 + } + else if (this.isLobotomized && shouldRestock(level)) restock(); +@@ -417,7 +439,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return super.mobInteract(player, hand); + } else if (this.isBaby()) { + this.setUnhappy(); +- return InteractionResult.SUCCESS; ++ return tryRide(player, hand, InteractionResult.SUCCESS); // Purpur - Ridables + } else { + if (!this.level().isClientSide()) { + boolean isEmpty = this.getOffers().isEmpty(); +@@ -430,9 +452,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + + if (isEmpty) { +- return InteractionResult.CONSUME; ++ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables + } + ++ if (level().purpurConfig.villagerRidable && itemInHand.isEmpty()) return tryRide(player, hand); // Purpur - Ridables ++ + if (this.level().purpurConfig.villagerAllowTrading) // Purpur - Add config for villager trading + this.startTrading(player); + } +diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +index aceeb9919473f5ff1b84efe950d10aa4dbc10121..5c7da654ef967356173a9d85a8675a7dc61ef395 100644 +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -68,6 +68,23 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + } + // Purpur end - Allow leashing villagers + ++ // Purpur - start - Ridables ++ @Override ++ public boolean isRidable() { ++ return level().purpurConfig.wanderingTraderRidable; ++ } ++ ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.wanderingTraderRidableInWater; ++ } ++ ++ @Override ++ public boolean isControllable() { ++ return level().purpurConfig.wanderingTraderControllable; ++ } ++ // Purpur end - Ridables ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +@@ -128,8 +145,9 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + + if (!this.level().isClientSide()) { + if (this.getOffers().isEmpty()) { +- return InteractionResult.CONSUME; ++ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables + } ++ if (level().purpurConfig.wanderingTraderRidable && itemInHand.isEmpty()) return tryRide(player, hand); // Purpur - Ridables + + if (this.level().purpurConfig.wanderingTraderAllowTrading) { // Purpur - Add config for villager trading + this.setTradingPlayer(player); +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index f5d3aa7c1e008377f9ac06401e88dae8c671ec13..eee303b4e0fa22a3d037e5fb3bf31946f41744cf 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -199,6 +199,19 @@ public abstract class Player extends Avatar implements ContainerUser { + } + // CraftBukkit end + ++ // Purpur start - Ridables ++ public abstract void resetLastActionTime(); ++ ++ @Override ++ public boolean processClick(InteractionHand hand) { ++ Entity vehicle = getRootVehicle(); ++ if (vehicle != null && vehicle.getRider() == this) { ++ return vehicle.onClick(hand); ++ } ++ return false; ++ } ++ // Purpur end - Ridables ++ + public Player(Level level, GameProfile gameProfile) { + super(EntityType.PLAYER, level); + this.setUUID(gameProfile.id()); +diff --git a/net/minecraft/world/entity/projectile/LlamaSpit.java b/net/minecraft/world/entity/projectile/LlamaSpit.java +index b8e3616de5f07e49a8bbab314329157e4268fb1e..56d78249c3fd7da0ff963712fe3a5c722b907c09 100644 +--- a/net/minecraft/world/entity/projectile/LlamaSpit.java ++++ b/net/minecraft/world/entity/projectile/LlamaSpit.java +@@ -33,6 +33,12 @@ public class LlamaSpit extends Projectile { + ); + } + ++ // Purpur start - Ridables ++ public void projectileTick() { ++ super.tick(); ++ } ++ // Purpur end - Ridables ++ + @Override + protected double getDefaultGravity() { + return 0.06; +diff --git a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java +index 1020e835f24bc25e032335f739d526bc0bd4ecc4..fced6e9df3239ffff9f297706c764cb9675c0ebb 100644 +--- a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java ++++ b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java +@@ -110,6 +110,14 @@ public class WitherSkull extends AbstractHurtingProjectile { + } + // Purpur end - Add canSaveToDisk to Entity + ++ // Purpur start - Ridables ++ @Override ++ public boolean canHitEntity(Entity target) { ++ // do not hit rider ++ return target != this.getRider() && super.canHitEntity(target); ++ } ++ // Purpur end - Ridables ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + builder.define(DATA_DANGEROUS, false); diff --git a/purpur-server/minecraft-patches/features/0002-Configurable-entity-base-attributes.patch b/purpur-server/minecraft-patches/features/0002-Configurable-entity-base-attributes.patch new file mode 100644 index 000000000..c0346feeb --- /dev/null +++ b/purpur-server/minecraft-patches/features/0002-Configurable-entity-base-attributes.patch @@ -0,0 +1,1886 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 10 Dec 2020 16:44:54 -0600 +Subject: [PATCH] Configurable entity base attributes + + +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 6e0a2741db06f93f31349515d1d13181b70e08ed..42dcad656dff5ba1e7fa8aced86628ac28f300af 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -295,6 +295,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + protected LivingEntity(EntityType type, Level level) { + super(type, level); + this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - Ridables ++ this.initAttributes(); // Purpur - Configurable entity base attributes + this.craftAttributes = new org.bukkit.craftbukkit.attribute.CraftAttributeMap(this.attributes); // CraftBukkit + // CraftBukkit - this.setHealth(this.getMaxHealth()) inlined and simplified to skip the instanceof check for Player, as getBukkitEntity() is not initialized in constructor + this.entityData.set(LivingEntity.DATA_HEALTH_ID, this.getMaxHealth()); +@@ -316,6 +317,8 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + return new EntityEquipment(); + } + ++ protected void initAttributes() {}// Purpur - Configurable entity base attributes ++ + public Brain getBrain() { + return this.brain; + } +diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index 2cfcf1f7473d8612777ca0752f6d1521c231ef42..a09b5035e91d50e12f613a7a1211c6869fbbf4df 100644 +--- a/net/minecraft/world/entity/ambient/Bat.java ++++ b/net/minecraft/world/entity/ambient/Bat.java +@@ -94,6 +94,21 @@ public class Bat extends AmbientCreature { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.batMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.batScale); ++ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.batFollowRange); ++ this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level().purpurConfig.batKnockbackResistance); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.batMovementSpeed); ++ this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level().purpurConfig.batFlyingSpeed); ++ this.getAttribute(Attributes.ARMOR).setBaseValue(this.level().purpurConfig.batArmor); ++ this.getAttribute(Attributes.ARMOR_TOUGHNESS).setBaseValue(this.level().purpurConfig.batArmorToughness); ++ this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level().purpurConfig.batAttackKnockback); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public boolean isFlapping() { + return !this.isResting() && this.tickCount % 10.0F == 0.0F; +diff --git a/net/minecraft/world/entity/animal/allay/Allay.java b/net/minecraft/world/entity/animal/allay/Allay.java +index 6ab6305f0b25f7c860673c70d9ce911688ecf1e6..ea7666cc10aee49e13dbdd6e3367fabaa0dcbc17 100644 +--- a/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/net/minecraft/world/entity/animal/allay/Allay.java +@@ -167,6 +167,14 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.allayMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.allayScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +index 767a730baa8a7694ed7d5f05b70118da1f4288cc..1f6b28531127ea2e5b291583f6bb6a236868fbf0 100644 +--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -99,6 +99,14 @@ public class Armadillo extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.armadilloMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.armadilloScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 3837397563bf3d568c120ae4e4e38d1a6dc7a8b2..3f9e15685ba52a5b9bd4282ba6de6751296975bf 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -138,6 +138,14 @@ public class Axolotl extends Animal implements Bucketable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.axolotlMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.axolotlScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return 0.0F; +diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java +index bd3dc923058b884afcfad08062230182810c65a2..7d7ab4c5092ac085e8cd6d3a432feb683db5a282 100644 +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -477,6 +477,14 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + return beehiveBlockEntity != null && beehiveBlockEntity.isFireNearby(); + } + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.beeMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.beeScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public long getPersistentAngerEndTime() { + return this.entityData.get(DATA_ANGER_END_TIME); +diff --git a/net/minecraft/world/entity/animal/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index a6a66084323435697b3185d7b86acaf5c7453719..95e86330e1e973c43e50bd4ac310212b8a057430 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -343,6 +343,23 @@ public class Camel extends AbstractHorse { + return this.dashCooldown; + } + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(net.minecraft.util.RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.camelMaxHealthMin, this.level().purpurConfig.camelMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(net.minecraft.util.RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.camelJumpStrengthMin, this.level().purpurConfig.camelJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(net.minecraft.util.RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.camelMovementSpeedMin, this.level().purpurConfig.camelMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.CAMEL_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index 4961bb85b91f68075cf0d22440d6377d9fcb7721..bfc5c6639ef1005d2752d5164f3613ddacc46bf1 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -90,6 +90,14 @@ public class Chicken extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.chickenMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.chickenScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index 0268063bb0db8c30c594a7d75d0d11f7236c3a68..5e5b239ebc774ae66f8c35a725ea917993239ef2 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -46,6 +46,14 @@ public class Cow extends AbstractCow { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java +index 1a9f5f17e46af831bc6621c83c57e5436397dbc2..85ad44eb3e64232cf1ea326257078c46288a613a 100644 +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -78,6 +78,13 @@ public class MushroomCow extends AbstractCow implements Shearable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.mooshroomMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); +diff --git a/net/minecraft/world/entity/animal/dolphin/Dolphin.java b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +index 656335a3bd021efc538ab407673c09a83178ed7c..94f92080de46f2af67e1d28753208691da534ddf 100644 +--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java ++++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +@@ -152,6 +152,14 @@ public class Dolphin extends AgeableWaterCreature { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.dolphinMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.dolphinScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/equine/AbstractHorse.java b/net/minecraft/world/entity/animal/equine/AbstractHorse.java +index a1dce0a5ce1fcd0a2ff7104b3592ffd5c948db34..5bd1e0f20a9553ae20db1d72d0b2a88bd92db723 100644 +--- a/net/minecraft/world/entity/animal/equine/AbstractHorse.java ++++ b/net/minecraft/world/entity/animal/equine/AbstractHorse.java +@@ -140,6 +140,46 @@ public abstract class AbstractHorse extends Animal implements HasCustomInventory + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth(random)); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.generateSpeed(random)); ++ this.getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(this.generateJumpStrength(random)); ++ } ++ ++ protected double generateMaxHealth(double min, double max) { ++ if (min == max) return min; ++ int diff = Mth.floor(max - min); ++ double base = max - diff; ++ int first = Mth.floor((double) diff / 2); ++ int rest = diff - first; ++ return base + random.nextInt(first + 1) + random.nextInt(rest + 1); ++ } ++ ++ protected double generateJumpStrength(double min, double max) { ++ if (min == max) return min; ++ return min + (max - min) * this.random.nextDouble(); ++ } ++ ++ protected double generateSpeed(double min, double max) { ++ if (min == max) return min; ++ return min + (max - min) * this.random.nextDouble(); ++ } ++ ++ protected float generateMaxHealth(RandomSource random) { ++ return 15.0F + (float) random.nextInt(8) + (float) random.nextInt(9); ++ } ++ ++ protected double generateJumpStrength(RandomSource random) { ++ return 0.4F + random.nextDouble() * 0.2 + random.nextDouble() * 0.2 + random.nextDouble() * 0.2; ++ } ++ ++ protected double generateSpeed(RandomSource random) { ++ return (0.45F + random.nextDouble() * 0.3 + random.nextDouble() * 0.3 + random.nextDouble() * 0.3) * 0.25; ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables +@@ -1050,7 +1090,7 @@ public abstract class AbstractHorse extends Animal implements HasCustomInventory + spawnGroupData = new AgeableMob.AgeableMobGroupData(0.2F); + } + +- this.randomizeAttributes(level.getRandom()); ++ //this.randomizeAttributes(level.getRandom()); // Purpur - replaced by initAttributes() + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); + } + +diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java +index 8aec9f254c82993632e68368d37b8c9bee7869cc..b85d967c7c809683e4576be30ed855941c6e68cc 100644 +--- a/net/minecraft/world/entity/animal/equine/Donkey.java ++++ b/net/minecraft/world/entity/animal/equine/Donkey.java +@@ -23,6 +23,23 @@ public class Donkey extends AbstractChestedHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(net.minecraft.util.RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.donkeyMaxHealthMin, this.level().purpurConfig.donkeyMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(net.minecraft.util.RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.donkeyJumpStrengthMin, this.level().purpurConfig.donkeyJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(net.minecraft.util.RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.donkeyMovementSpeedMin, this.level().purpurConfig.donkeyMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java +index cc50151ce6e6daffc1ecd41eb89a0d2f159f651e..9a97cd588fb2dc0f393b2c8768f4e45066a34850 100644 +--- a/net/minecraft/world/entity/animal/equine/Horse.java ++++ b/net/minecraft/world/entity/animal/equine/Horse.java +@@ -57,6 +57,23 @@ public class Horse extends AbstractHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.horseMaxHealthMin, this.level().purpurConfig.horseMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.horseJumpStrengthMin, this.level().purpurConfig.horseJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.horseMovementSpeedMin, this.level().purpurConfig.horseMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void randomizeAttributes(RandomSource random) { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt)); +diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java +index bba6493eee2f605faac0d49d665117d2f2c41213..b54a535add643390a6164ab3f4a2fdd475d60162 100644 +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -135,6 +135,23 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.llamaMaxHealthMin, this.level().purpurConfig.llamaMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.llamaJumpStrengthMin, this.level().purpurConfig.llamaJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.llamaMovementSpeedMin, this.level().purpurConfig.llamaMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public boolean isTraderLlama() { + return false; + } +diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java +index 60c151af9e51ba1dd1063344a3f5c021b6a48440..f49c8a61ac3883f7ff8ad8193ae9ea9136f54c45 100644 +--- a/net/minecraft/world/entity/animal/equine/Mule.java ++++ b/net/minecraft/world/entity/animal/equine/Mule.java +@@ -22,6 +22,23 @@ public class Mule extends AbstractChestedHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(net.minecraft.util.RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.muleMaxHealthMin, this.level().purpurConfig.muleMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(net.minecraft.util.RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.muleJumpStrengthMin, this.level().purpurConfig.muleJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(net.minecraft.util.RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.muleMovementSpeedMin, this.level().purpurConfig.muleMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.MULE_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +index 3d98259439c3bdb97ab2c66734daf90a56b290ef..d8b7b787b98b86d16578abe1cd19321ad2c176c0 100644 +--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +@@ -50,6 +50,23 @@ public class SkeletonHorse extends AbstractHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.skeletonHorseMaxHealthMin, this.level().purpurConfig.skeletonHorseMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.skeletonHorseJumpStrengthMin, this.level().purpurConfig.skeletonHorseJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.skeletonHorseMovementSpeedMin, this.level().purpurConfig.skeletonHorseMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java +index 68b72c18a3880dead3b32b646a2f6a09d4b98c44..deb0a1eb867daec57a644ce698fe50d79fd8960e 100644 +--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java ++++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java +@@ -53,6 +53,23 @@ public class TraderLlama extends Llama { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(net.minecraft.util.RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.traderLlamaMaxHealthMin, this.level().purpurConfig.traderLlamaMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(net.minecraft.util.RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.traderLlamaJumpStrengthMin, this.level().purpurConfig.traderLlamaJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(net.minecraft.util.RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.traderLlamaMovementSpeedMin, this.level().purpurConfig.traderLlamaMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public boolean isTraderLlama() { + return true; +diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +index c1a13047c673cafc5de873215dd368eae6ac8b5e..8b8580f8c082a40f958ec61adf32dc7d1485ea68 100644 +--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java ++++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +@@ -64,6 +64,23 @@ public class ZombieHorse extends AbstractHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public float generateMaxHealth(RandomSource random) { ++ return (float) generateMaxHealth(this.level().purpurConfig.zombieHorseMaxHealthMin, this.level().purpurConfig.zombieHorseMaxHealthMax); ++ } ++ ++ @Override ++ public double generateJumpStrength(RandomSource random) { ++ return generateJumpStrength(this.level().purpurConfig.zombieHorseJumpStrengthMin, this.level().purpurConfig.zombieHorseJumpStrengthMax); ++ } ++ ++ @Override ++ public double generateSpeed(RandomSource random) { ++ return generateSpeed(this.level().purpurConfig.zombieHorseMovementSpeedMin, this.level().purpurConfig.zombieHorseMovementSpeedMax); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0); + } +diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java +index 2fe8b8382b7cb4056f4d430cf632ecf413e8e25b..b7c17aed96dce39cb03780ff251e0e38c3c6adb1 100644 +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -120,6 +120,14 @@ public class Cat extends TamableAnimal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.catMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.catScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java +index 22fa29aa785eda8fb4a895d36413626da8a49a0e..c9a8dcfd1e46f97dee0393db3205049c0db1cefb 100644 +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -83,6 +83,14 @@ public class Ocelot extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ocelotMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ocelotScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public boolean isTrusting() { + return this.entityData.get(DATA_TRUSTING); + } +diff --git a/net/minecraft/world/entity/animal/fish/Cod.java b/net/minecraft/world/entity/animal/fish/Cod.java +index f73b3f5ae65f7793ff25145c72fb35e3daec8494..e00e623d7df9e179d8bb5ee4812538578c5f4c08 100644 +--- a/net/minecraft/world/entity/animal/fish/Cod.java ++++ b/net/minecraft/world/entity/animal/fish/Cod.java +@@ -25,6 +25,13 @@ public class Cod extends AbstractSchoolingFish { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.codMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public ItemStack getBucketItemStack() { + return new ItemStack(Items.COD_BUCKET); +diff --git a/net/minecraft/world/entity/animal/fish/Pufferfish.java b/net/minecraft/world/entity/animal/fish/Pufferfish.java +index 1eaa5e5fb65b18e5042d69b5dbfea15a7271c0aa..b2932aeb6000a4e268db12cb7b05be746788569c 100644 +--- a/net/minecraft/world/entity/animal/fish/Pufferfish.java ++++ b/net/minecraft/world/entity/animal/fish/Pufferfish.java +@@ -59,6 +59,13 @@ public class Pufferfish extends AbstractFish { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pufferfishMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/fish/Salmon.java b/net/minecraft/world/entity/animal/fish/Salmon.java +index b362ce156765ad45b8c29b5dc2c6d9d99d2e1474..97194d5c844bf96ba431c12ccc1a82cb8944c52b 100644 +--- a/net/minecraft/world/entity/animal/fish/Salmon.java ++++ b/net/minecraft/world/entity/animal/fish/Salmon.java +@@ -52,6 +52,13 @@ public class Salmon extends AbstractSchoolingFish { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.salmonMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public int getMaxSchoolSize() { + return 5; +diff --git a/net/minecraft/world/entity/animal/fish/TropicalFish.java b/net/minecraft/world/entity/animal/fish/TropicalFish.java +index 3281e75c48fb6cafdba088254009e5b4285b3819..8ffb253c114d882c9459f74a27bd8a086ae82f01 100644 +--- a/net/minecraft/world/entity/animal/fish/TropicalFish.java ++++ b/net/minecraft/world/entity/animal/fish/TropicalFish.java +@@ -89,6 +89,13 @@ public class TropicalFish extends AbstractSchoolingFish { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.tropicalFishMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static String getPredefinedName(int variantId) { + return "entity.minecraft.tropical_fish.predefined." + variantId; + } +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index bf00cf5e6fd8b9143e1b327ef7b90ae055d264ff..6819bcb15ad6c85f41a098a9fdb73ce5a7987d8f 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -189,6 +189,14 @@ public class Fox extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.foxMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.foxScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/golem/CopperGolem.java b/net/minecraft/world/entity/animal/golem/CopperGolem.java +index 4dc1ce5e07e23134cf3ed68e78a055c3b75fdc83..e3c5b6fe7d6cdb674fbf5ac22001c3d917bf91ec 100644 +--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java +@@ -130,6 +130,16 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.copperGolemMaxHealth); ++ this.getAttribute(Attributes.STEP_HEIGHT).setBaseValue(this.level().purpurConfig.copperGolemStepHeight); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.copperGolemMovementSpeed); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.copperGolemScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0); + } +diff --git a/net/minecraft/world/entity/animal/golem/IronGolem.java b/net/minecraft/world/entity/animal/golem/IronGolem.java +index 363bf7781c8785bc6fdbc65941794a5a02f8a57a..677f584b38aeb6805db0bb867f62d5617e309f5e 100644 +--- a/net/minecraft/world/entity/animal/golem/IronGolem.java ++++ b/net/minecraft/world/entity/animal/golem/IronGolem.java +@@ -91,6 +91,14 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ironGolemMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ironGolemScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options +diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java +index d8a3b7d329757bb84ee4d53671c89f211e8581ab..ab44bc401438d589696d9f25ebaca0fc39648bed 100644 +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -79,6 +79,14 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snowGolemMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.snowGolemScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +index 0a75d3790a4bf4812e4f3b27ec50d67fdccf812d..cd888e070a72ff46d35a4425a0013c617b31e159 100644 +--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java ++++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +@@ -133,6 +133,19 @@ public class HappyGhast extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.happyGhastMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.happyGhastScale); ++ this.getAttribute(Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.happyGhastTemptRange); ++ this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level().purpurConfig.happyGhastFlyingSpeed); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.happyGhastMovementSpeed); ++ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.happyGhastFollowRange); ++ this.getAttribute(Attributes.CAMERA_DISTANCE).setBaseValue(this.level().purpurConfig.happyGhastCameraDistance); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void ageBoundaryReached() { + if (this.isBaby()) { +@@ -156,7 +169,7 @@ public class HappyGhast extends Animal { + + @Override + protected float sanitizeScale(float scale) { +- return Math.min(scale, 1.0F); ++ return Math.min(scale, 1.0F); // Purpur - Configurable entity base attributes + } + + @Override +diff --git a/net/minecraft/world/entity/animal/nautilus/Nautilus.java b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +index a02a4b47cf02cd7eaa76d25af2dccb50ad884504..a32ac3907883f65c2a00173128a4a011fd552932 100644 +--- a/net/minecraft/world/entity/animal/nautilus/Nautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +@@ -21,6 +21,17 @@ public class Nautilus extends AbstractNautilus { + super(type, level); + } + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.nautilusMaxHealth); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.nautilusMovementSpeed); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.nautilusAttackDamage); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level().purpurConfig.nautilusKnockbackResistance); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SCALE).setBaseValue(this.level().purpurConfig.nautilusScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected Brain.Provider brainProvider() { + return NautilusAi.brainProvider(); +diff --git a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +index 83c16a44b230efd010e06cabb428954d157cea47..7ef4a15ba2b315a41480484572a6c6898340466b 100644 +--- a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +@@ -43,6 +43,17 @@ public class ZombieNautilus extends AbstractNautilus { + super(type, level); + } + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieNautilusMaxHealth); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.zombieNautilusMovementSpeed); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.zombieNautilusAttackDamage); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level().purpurConfig.zombieNautilusKnockbackResistance); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombieNautilusScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return AbstractNautilus.createAttributes().add(Attributes.MOVEMENT_SPEED, 1.1F); + } +diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java +index 93503f6b4eea2cb2ae6c01279e847c307920c35d..d5060fb8e9a711e6230f2c4950521d8b4f5c01d2 100644 +--- a/net/minecraft/world/entity/animal/panda/Panda.java ++++ b/net/minecraft/world/entity/animal/panda/Panda.java +@@ -134,6 +134,15 @@ public class Panda extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pandaMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pandaScale); ++ setAttributes(); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) { + return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot(); +@@ -625,7 +634,11 @@ public class Panda extends Animal { + + public void setAttributes() { + if (this.isWeak()) { +- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0); ++ // Purpur start - Configurable entity base attributes ++ net.minecraft.world.entity.ai.attributes.AttributeInstance maxHealth = this.getAttribute(Attributes.MAX_HEALTH); ++ maxHealth.setBaseValue(maxHealth.getValue() / 2); ++ // Purpur end - Configurable entity base attributes ++ + } + + if (this.isLazy()) { +diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java +index 6a4d170cd1e4dde2af30895f9fbac9dd599d0602..7480a491533a47882eaf4b36c320adf45ebfb190 100644 +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -199,6 +199,14 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.parrotMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.parrotScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java +index 2a3fc6ad3a12a3cc9815b66b659e3c9ce2afa7a8..aeb9e57db2233bff20fa5208a679ac9bcb8d5026 100644 +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -80,6 +80,14 @@ public class Pig extends Animal implements ItemSteerable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pigMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pigScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +index 718531a324c36ac65a93af5c12e0c0a3948606cb..904e4c641f1892220f263528c65f6f81708399cd 100644 +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -118,6 +118,14 @@ public class PolarBear extends Animal implements NeutralMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.polarBearMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.polarBearScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index 16fbee742f28cf4571effb66265daeee64bae9b7..cfbab57dd0527c5e2f17718f3974059eb881c2ea 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -148,6 +148,14 @@ public class Rabbit extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.rabbitMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.rabbitScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java +index a33800517d5fb5ab008e22b77be2f079901c1a99..1d60d35c1330418009f7d1d0b60d263559b68b7f 100644 +--- a/net/minecraft/world/entity/animal/sheep/Sheep.java ++++ b/net/minecraft/world/entity/animal/sheep/Sheep.java +@@ -80,6 +80,14 @@ public class Sheep extends Animal implements Shearable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.sheepMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.sheepScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.eatBlockGoal = new EatBlockGoal(this); +diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +index 3d5cf62bd9bd99a978b7dc535675178deb695af0..eb6675394ecc5bba67e0f8bb0220ad92ef2b5e4f 100644 +--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java ++++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +@@ -106,6 +106,14 @@ public class Sniffer extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.snifferMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.snifferScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/squid/GlowSquid.java b/net/minecraft/world/entity/animal/squid/GlowSquid.java +index c5a8080aa0fa1014602ba76fb4d8ad69b926dbfa..7d41aaa0546a9ca02e46ce46e61ecc57120bfca9 100644 +--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java ++++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java +@@ -50,6 +50,13 @@ public class GlowSquid extends Squid { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected ParticleOptions getInkParticle() { + return ParticleTypes.GLOW_SQUID_INK; +diff --git a/net/minecraft/world/entity/animal/squid/Squid.java b/net/minecraft/world/entity/animal/squid/Squid.java +index 69d87bd27a95bb9e92cd24fb7973a9b06714567e..5e4181f95711c6b0299f28de14d95da421dda373 100644 +--- a/net/minecraft/world/entity/animal/squid/Squid.java ++++ b/net/minecraft/world/entity/animal/squid/Squid.java +@@ -93,6 +93,14 @@ public class Squid extends AgeableWaterCreature { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.squidMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.squidScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); +diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java +index 7699967db327cf80940bf30d8e0c734b43fd1e8f..8d9a1fe887c39588e43e4ccfc46151a033c8ccb7 100644 +--- a/net/minecraft/world/entity/animal/turtle/Turtle.java ++++ b/net/minecraft/world/entity/animal/turtle/Turtle.java +@@ -104,6 +104,14 @@ public class Turtle extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.turtleMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.turtleScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public void setHomePos(BlockPos homePos) { + this.homePos = homePos; + } +diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java +index 44b650de646ea605e41f39f3d99d8f550c9a8e19..7bccee8b5dd689bbff18f34d3afac52bfe34aa42 100644 +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -208,6 +208,14 @@ public class Wolf extends TamableAnimal implements NeutralMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wolfMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.wolfScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index b8ddcc460ff1ae1cbfde70d3d8c16486bb2d3f8c..844c989fc4e0d131d823bf8a59951f35f30b7641 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -163,6 +163,13 @@ public class EnderDragon extends Mob implements Enemy { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.enderDragonMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0).add(Attributes.CAMERA_DISTANCE, 16.0); + } +@@ -1059,7 +1066,7 @@ public class EnderDragon extends Mob implements Enemy { + + @Override + protected float sanitizeScale(float scale) { +- return 1.0F; ++ return 1.0F; // Purpur - Configurable entity base attributes + } + + // CraftBukkit start - SPIGOT-2420: Special case, the ender dragon drops 12000 xp for the first kill and 500 xp for every other kill and this over time. +diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index dbf3c7049fae08dc5c4ac491a5fc8141ab177a22..612df8799b80f1793ab9781212442098633e9d65 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -213,6 +213,14 @@ public class WitherBoss extends Monster implements RangedAttackMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witherScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +@@ -435,7 +443,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + this.setInvulnerableTicks(i); + if (this.tickCount % 10 == 0) { +- this.heal(10.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit ++ this.heal(this.getMaxHealth() / 30, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit // Purpur - Configurable entity base attributes + } + } else { + super.customServerAiStep(level); +diff --git a/net/minecraft/world/entity/monster/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java +index 1febee23188d91e2a27c182b97502e8c7ab696a2..4cd7975a8e9e55d6b6ded0b7e13d8da436e49c6c 100644 +--- a/net/minecraft/world/entity/monster/Blaze.java ++++ b/net/minecraft/world/entity/monster/Blaze.java +@@ -76,6 +76,14 @@ public class Blaze extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.blazeMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.blazeScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index cfd09a5ff326cf4ecc248901696ce1f623a8e7c0..ed1ccd712a2fe00740beeee4fe615976258f5c6c 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -137,6 +137,14 @@ public class Creeper extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.creeperMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.creeperScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index e5d16ca14a40d520dce43dd3d9b6347aefbd25d7..57fb334f670d35aa181b13e12fa2c5f36da1dda6 100644 +--- a/net/minecraft/world/entity/monster/ElderGuardian.java ++++ b/net/minecraft/world/entity/monster/ElderGuardian.java +@@ -43,6 +43,14 @@ public class ElderGuardian extends Guardian { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.elderGuardianMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.elderGuardianScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0); + } +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index bd78d8c3e8c1d9b581c751d336322fbfe20dacef..29570047bfc78a8993b0bbd7168cb4fe318a6be6 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -106,6 +106,14 @@ public class EnderMan extends Monster implements NeutralMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.endermanMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.endermanScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java +index 844ba943a2a752f9b25506b644a4b4abc981b4cb..c6ae00f6ab83743b9ce43d9e55f04ba23b0de0f1 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -64,6 +64,14 @@ public class Endermite extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.endermiteMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.endermiteScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index 451627153610f2c8db2403f73545c9af6dfd2e4b..7d97c00ae49629b97104631317aa6174741cdd5e 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -75,6 +75,14 @@ public class Ghast extends Mob implements Enemy { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ghastMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ghastScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index dcbe6bbce0baa4fad7fe180944beeb6ca4026f7d..416bb85fc87b5f9e372f33df8fe27f12e83834b9 100644 +--- a/net/minecraft/world/entity/monster/Giant.java ++++ b/net/minecraft/world/entity/monster/Giant.java +@@ -35,6 +35,16 @@ public class Giant extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ protected void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.giantMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.giantScale); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.giantMovementSpeed); ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.giantAttackDamage); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 100.0) +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index 4cf54e55de1ecc57061ad0c09e7966d6104f1cc2..1eaa4f255b01eddc93ddbc5615ad05e7d8273581 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -89,6 +89,14 @@ public class Guardian extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.guardianMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.guardianScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0); +diff --git a/net/minecraft/world/entity/monster/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index a3cd6d4b999fc49893794838c73370c19c6a66b6..e0fda9a975e00c49ba09db65d7b3fba8fa434757 100644 +--- a/net/minecraft/world/entity/monster/MagmaCube.java ++++ b/net/minecraft/world/entity/monster/MagmaCube.java +@@ -46,6 +46,28 @@ public class MagmaCube extends Slime { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ protected String getMaxHealthEquation() { ++ return level().purpurConfig.magmaCubeMaxHealth; ++ } ++ ++ @Override ++ protected String getAttackDamageEquation() { ++ return level().purpurConfig.magmaCubeAttackDamage; ++ } ++ ++ @Override ++ protected java.util.Map getMaxHealthCache() { ++ return level().purpurConfig.magmaCubeMaxHealthCache; ++ } ++ ++ @Override ++ protected java.util.Map getAttackDamageCache() { ++ return level().purpurConfig.magmaCubeAttackDamageCache; ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 64e55d48bc81d6237970e86e6e1cc719831902fd..046d6d3b665d0dbb5403ebe91d18503ec7613936 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -140,7 +140,10 @@ public class Phantom extends Mob implements Enemy { + private void updatePhantomSizeInfo() { + this.refreshDimensions(); + if (level().purpurConfig.phantomFlamesOnSwoop && attackPhase == AttackPhase.SWOOP) shoot(); // Purpur - Ridables - Phantom flames on swoop +- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(6 + this.getPhantomSize()); ++ // Purpur start - Configurable entity base attributes ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomMaxHealth, () -> this.level().purpurConfig.phantomMaxHealthCache, () -> 20.0D)); ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(() -> this.level().purpurConfig.phantomAttackDamage, () -> this.level().purpurConfig.phantomAttackDamageCache, () -> (double) (6 + this.getPhantomSize()))); ++ // Purpur end - Configurable entity base attributes + } + + public int getPhantomSize() { +@@ -160,6 +163,23 @@ public class Phantom extends Mob implements Enemy { + return this.getId() * 3; + } + ++ // Purpur start - Configurable entity base attributes ++ private double getFromCache(java.util.function.Supplier equation, java.util.function.Supplier> cache, java.util.function.Supplier defaultValue) { ++ int size = getPhantomSize(); ++ Double value = cache.get().get(size); ++ if (value == null) { ++ try { ++ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue(); ++ } catch (javax.script.ScriptException e) { ++ e.printStackTrace(); ++ value = defaultValue.get(); ++ } ++ cache.get().put(size, value); ++ } ++ return value; ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public void tick() { + super.tick(); +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index f7807aac5a8f9c92ba77ac38c469ef14948197ac..86c55955ff6c0a411cde34999c7bd6ad07be5d4e 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -95,6 +95,14 @@ public class Ravager extends Raider { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.ravagerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ravagerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Shulker.java b/net/minecraft/world/entity/monster/Shulker.java +index 9647741b67e1b4b7bbff0df22fc1bf3fb26afa73..30f2503ebd045b05f5594cef28389a694338d13a 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -125,6 +125,14 @@ public class Shulker extends AbstractGolem implements Enemy { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.shulkerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.shulkerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +@@ -601,7 +609,7 @@ public class Shulker extends AbstractGolem implements Enemy { + + @Override + protected float sanitizeScale(float scale) { +- return Math.min(scale, 3.0F); ++ return Math.min(scale, MAX_SCALE); // Purpur - Configurable entity base attributes + } + + private void setVariant(Optional variant) { +diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java +index cf305d4a0a71f91caa31757dcd8ca5be84fc3f03..1df006a0a49038f1e737194e7da8e0b27e6eeb95 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -55,6 +55,16 @@ public class Silverfish extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.silverfishMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.silverfishScale); ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.silverfishMovementSpeed); ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.silverfishAttackDamage); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this); +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index 0c56f9ae47b9263ac65fd3593911e2c8479fc157..f93a2c58b5835e9ce8318d28e944533ef69df8ef 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -102,6 +102,39 @@ public class Slime extends Mob implements Enemy { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ protected String getMaxHealthEquation() { ++ return level().purpurConfig.slimeMaxHealth; ++ } ++ ++ protected String getAttackDamageEquation() { ++ return level().purpurConfig.slimeAttackDamage; ++ } ++ ++ protected java.util.Map getMaxHealthCache() { ++ return level().purpurConfig.slimeMaxHealthCache; ++ } ++ ++ protected java.util.Map getAttackDamageCache() { ++ return level().purpurConfig.slimeAttackDamageCache; ++ } ++ ++ protected double getFromCache(java.util.function.Supplier equation, java.util.function.Supplier> cache, java.util.function.Supplier defaultValue) { ++ int size = getSize(); ++ Double value = cache.get().get(size); ++ if (value == null) { ++ try { ++ value = ((Number) scriptEngine.eval("let size = " + size + "; " + equation.get())).doubleValue(); ++ } catch (javax.script.ScriptException e) { ++ e.printStackTrace(); ++ value = defaultValue.get(); ++ } ++ cache.get().put(size, value); ++ } ++ return value; ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +@@ -132,9 +165,9 @@ public class Slime extends Mob implements Enemy { + this.entityData.set(ID_SIZE, i); + this.reapplyPosition(); + this.refreshDimensions(); +- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(i * i); ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(getFromCache(this::getMaxHealthEquation, this::getMaxHealthCache, () -> (double) (size * size))); // Purpur - Configurable entity base attributes + this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.2F + 0.1F * i); +- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(i); ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(getFromCache(this::getAttackDamageEquation, this::getAttackDamageCache, () -> (double) i)); // Purpur - Configurable entity base attributes + if (resetHealth) { + this.setHealth(this.getMaxHealth()); + } +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index 46121b018c5dd87d888a3724df12a9af0d007772..3308e954c8f6deff89c6df0af01f7774e36b0385 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -109,6 +109,14 @@ public class Strider extends Animal implements ItemSteerable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.striderMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.striderScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static boolean checkStriderSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Vex.java b/net/minecraft/world/entity/monster/Vex.java +index d567e2b9843606c36a61c83a3a4e2d181f3eefd9..32759864a8ff0c4e28ce80ae8906cbcf1927094e 100644 +--- a/net/minecraft/world/entity/monster/Vex.java ++++ b/net/minecraft/world/entity/monster/Vex.java +@@ -102,6 +102,14 @@ public class Vex extends Monster implements TraceableEntity { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vexMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vexScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public boolean isFlapping() { + return this.tickCount % TICKS_PER_FLAP == 0; +diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index bcb136e71db2272dc107304b52be41f83d6772b7..3c3c258d4cc7cf7376d0d3ad0794ec3611ab81ce 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -74,6 +74,14 @@ public class Witch extends Raider implements RangedAttackMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witchMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witchScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index c4f009357e416ed4052e058d8bf3b8c3e9dfd4ae..e0192a13b8d7131024471569bf34329ba0f28287 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -104,6 +104,14 @@ public class Zoglin extends Monster implements HoglinBase { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zoglinMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zoglinScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/monster/creaking/Creaking.java b/net/minecraft/world/entity/monster/creaking/Creaking.java +index 01d3d638e194ec5e812eea0ab96b100f4752ea18..a84edd4953e59daed6816531545a6ec1c914bce6 100644 +--- a/net/minecraft/world/entity/monster/creaking/Creaking.java ++++ b/net/minecraft/world/entity/monster/creaking/Creaking.java +@@ -126,6 +126,14 @@ public class Creaking extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.creakingMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.creakingScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected BodyRotationControl createBodyControl() { + return new Creaking.CreakingBodyRotationControl(this); +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index c84f009cb0498e9c1898e310f19cff80ec6989ea..baf81e1919e64af9d6da0a49b19e5f34cf962a79 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -114,6 +114,14 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.hoglinMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.hoglinScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @VisibleForTesting + public void setTimeInOverworld(int timeInOverworld) { + this.timeInOverworld = timeInOverworld; +diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java +index e40434d24c7a9ffe76c3410601942d0c8a963e30..46f79ed345ec51125364b49b244d6d005a3e64ae 100644 +--- a/net/minecraft/world/entity/monster/illager/Evoker.java ++++ b/net/minecraft/world/entity/monster/illager/Evoker.java +@@ -66,6 +66,14 @@ public class Evoker extends SpellcasterIllager { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.evokerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.evokerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Illusioner.java b/net/minecraft/world/entity/monster/illager/Illusioner.java +index 8a80c202752f56ba174c00531993795a4e621910..8fa53cccb73b2d8c599c898f298681735007d2a0 100644 +--- a/net/minecraft/world/entity/monster/illager/Illusioner.java ++++ b/net/minecraft/world/entity/monster/illager/Illusioner.java +@@ -76,6 +76,16 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ protected void initAttributes() { ++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level().purpurConfig.illusionerMovementSpeed); ++ this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level().purpurConfig.illusionerFollowRange); ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.illusionerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.illusionerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Pillager.java b/net/minecraft/world/entity/monster/illager/Pillager.java +index 0b97c4fe89c8549841cbb79ecb9383a9be7a44af..1e43c69366287c7a191a6f8e3a7d5459743f07a2 100644 +--- a/net/minecraft/world/entity/monster/illager/Pillager.java ++++ b/net/minecraft/world/entity/monster/illager/Pillager.java +@@ -82,6 +82,14 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.pillagerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pillagerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Vindicator.java b/net/minecraft/world/entity/monster/illager/Vindicator.java +index 4162e7ee482829f0c5276a31d44ed4c5ee24c3cf..5f3857186e86e27fe237c62cec4af13ebf58debe 100644 +--- a/net/minecraft/world/entity/monster/illager/Vindicator.java ++++ b/net/minecraft/world/entity/monster/illager/Vindicator.java +@@ -74,6 +74,14 @@ public class Vindicator extends AbstractIllager { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.vindicatorMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vindicatorScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index a42b84611836b970d0d7ddb602bd04d9ec850712..98241af88cc961470e07df47d128d8912338bd44 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -158,6 +158,14 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index e33809e9ccc4a65909f1c12ec4b0fdc4050aeee7..3d34bef7ffe9e66e77e3fc10b2c5869d98a4a5c9 100644 +--- a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +@@ -82,6 +82,14 @@ public class PiglinBrute extends AbstractPiglin { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.piglinBruteMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinBruteScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 50.0) +diff --git a/net/minecraft/world/entity/monster/skeleton/Bogged.java b/net/minecraft/world/entity/monster/skeleton/Bogged.java +index 10e66643a918184d858a4e6c9de7a6c2664fbcaf..f1e9cec85ce911a50bb83eff9228d6b98aa00d44 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Bogged.java ++++ b/net/minecraft/world/entity/monster/skeleton/Bogged.java +@@ -58,6 +58,14 @@ public class Bogged extends AbstractSkeleton implements Shearable { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.boggedMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.boggedScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Skeleton.java b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +index 7f8298892565b073373ad68f9c1399a9c565c29a..96d989cffc6ff72954ed92e2535c72992d489372 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +@@ -43,6 +43,13 @@ public class Skeleton extends AbstractSkeleton { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.skeletonMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Stray.java b/net/minecraft/world/entity/monster/skeleton/Stray.java +index 23ceecffe8f1b758433e02b61cae421c518cc841..575f30cc9a6166a4e0733cc33e8b5814acb92660 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Stray.java ++++ b/net/minecraft/world/entity/monster/skeleton/Stray.java +@@ -40,6 +40,13 @@ public class Stray extends AbstractSkeleton { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.strayMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + public static boolean checkStraySpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +index d49200718e7fab29ce5e55278b7584458bfb10e7..f38bb54c0f5d7797179e5e75eb8092baea7b9dcb 100644 +--- a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +@@ -51,6 +51,14 @@ public class WitherSkeleton extends AbstractSkeleton { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.witherSkeletonMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witherSkeletonScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); +diff --git a/net/minecraft/world/entity/monster/spider/CaveSpider.java b/net/minecraft/world/entity/monster/spider/CaveSpider.java +index 17a9b046ccd8de37aa79501f7556a5a816dfa90f..a1391173cc4997df723c59f176726dde88491978 100644 +--- a/net/minecraft/world/entity/monster/spider/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/spider/CaveSpider.java +@@ -43,6 +43,14 @@ public class CaveSpider extends Spider { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.caveSpiderMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.caveSpiderScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public boolean doHurtTarget(ServerLevel level, Entity target) { + if (super.doHurtTarget(level, target)) { +diff --git a/net/minecraft/world/entity/monster/spider/Spider.java b/net/minecraft/world/entity/monster/spider/Spider.java +index 7a8b9c83490a05629e4996844a575ba4c7e9f156..6a9c807ed50dab4b65787d9f7269385103fa5f26 100644 +--- a/net/minecraft/world/entity/monster/spider/Spider.java ++++ b/net/minecraft/world/entity/monster/spider/Spider.java +@@ -69,6 +69,14 @@ public class Spider extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.spiderMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.spiderScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java +index 03c801d5982eed73fd8f56f63ccab570e83b8a53..b5d3bfb68d8f167e5d6439d15024597d7d2a40a7 100644 +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -96,6 +96,19 @@ public class Drowned extends Zombie implements RangedAttackMob { + return new AmphibiousPathNavigation(this, level); + } + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.drownedMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.drownedScale); ++ } ++ ++ @Override ++ protected void randomizeReinforcementsChance() { ++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.drownedSpawnReinforcements); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index f18ac33d31ae23db02654658840b89ba03736bf3..03270138e6fae7ee28f958cafee5b74b18dce355 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -46,6 +46,18 @@ public class Husk extends Zombie { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.huskMaxHealth); ++ } ++ ++ @Override ++ protected void randomizeReinforcementsChance() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.huskSpawnReinforcements); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public boolean isSunSensitive() { + return false; +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index b2d322bee9343079c0c21cc7b47493df9cfd861e..d873ca9873e95f3a5869cb63a93b0643a9c867dc 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -120,6 +120,14 @@ public class Zombie extends Monster { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombieScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +@@ -604,7 +612,7 @@ public class Zombie extends Monster { + } + + protected void randomizeReinforcementsChance() { +- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.1F); ++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieSpawnReinforcements); // Purpur - Configurable entity base attributes + } + + class ZombieAttackTurtleEggGoal extends RemoveBlockGoal { +diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +index d91a1aec302a93326a357311f1743fa87ec92fa3..0a02684ec00540b91a2a68e5787e51d15b3743a7 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -102,6 +102,18 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombieVillagerMaxHealth); ++ } ++ ++ @Override ++ protected void randomizeReinforcementsChance() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombieVillagerSpawnReinforcements); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +index e2c5301a465e28cea057f9afec7e9d78090e1c9e..3ff8c95075f9d25c3a2e4160ee6d18057838a7b5 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -81,6 +81,14 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.zombifiedPiglinMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombifiedPiglinScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); +@@ -267,7 +275,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + + @Override + protected void randomizeReinforcementsChance() { +- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0); ++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.zombifiedPiglinSpawnReinforcements); // Purpur - Configurable entity base attributes + } + + @Override +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index 6d4ef7b5d64639eb7fe400b2ed612c3b3552aa52..6f9658af51f1f30434756f871cf6ab5cd2f3a7ea 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -272,6 +272,14 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.villagerMaxHealth); ++ this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.villagerScale); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + public Brain getBrain() { + return (Brain)super.getBrain(); +diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +index 5c7da654ef967356173a9d85a8675a7dc61ef395..9d5dbaeafd899594425547fc58b87a1d0a52066e 100644 +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -85,6 +85,13 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + } + // Purpur end - Ridables + ++ // Purpur start - Configurable entity base attributes ++ @Override ++ public void initAttributes() { ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wanderingTraderMaxHealth); ++ } ++ // Purpur end - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch new file mode 100644 index 000000000..638bae2f4 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -0,0 +1,218 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 23 May 2019 21:50:37 -0500 +Subject: [PATCH] Barrels and enderchests 6 rows + + +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index 80986de9222010f01982cfdab58649429d457778..22f768d805bfc1c6e6e50449e1d275c3195036ad 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -887,6 +887,27 @@ public abstract class PlayerList { + player.getBukkitEntity().recalculatePermissions(); // CraftBukkit + this.server.getCommands().sendCommands(player); + } // Paper - Add sendOpLevel API ++ ++ // Purpur start - Barrels and enderchests 6 rows ++ if (org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) { ++ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkit = player.getBukkitEntity(); ++ if (bukkit.hasPermission("purpur.enderchest.rows.six")) { ++ player.sixRowEnderchestSlotCount = 54; ++ } else if (bukkit.hasPermission("purpur.enderchest.rows.five")) { ++ player.sixRowEnderchestSlotCount = 45; ++ } else if (bukkit.hasPermission("purpur.enderchest.rows.four")) { ++ player.sixRowEnderchestSlotCount = 36; ++ } else if (bukkit.hasPermission("purpur.enderchest.rows.three")) { ++ player.sixRowEnderchestSlotCount = 27; ++ } else if (bukkit.hasPermission("purpur.enderchest.rows.two")) { ++ player.sixRowEnderchestSlotCount = 18; ++ } else if (bukkit.hasPermission("purpur.enderchest.rows.one")) { ++ player.sixRowEnderchestSlotCount = 9; ++ } ++ } else { ++ player.sixRowEnderchestSlotCount = -1; ++ } ++ // Purpur end - Barrels and enderchests 6 rows + } + + // Paper start - whitelist verify event / login event +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 4bf98783e7199bc5c7c05c953729523b0b75b2b5..c4039b1ba46ea72c7cd1cbed9e8edbffc5d5e1cc 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -181,6 +181,7 @@ public abstract class Player extends Avatar implements ContainerUser { + public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage + public int burpDelay = 0; // Purpur - Burp delay + public boolean canPortalInstant = false; // Purpur - Add portal permission bypass ++ public int sixRowEnderchestSlotCount = -1; // Purpur - Barrels and enderchests 6 rows + + // CraftBukkit start + public boolean fauxSleeping; +diff --git a/net/minecraft/world/inventory/ChestMenu.java b/net/minecraft/world/inventory/ChestMenu.java +index 1fb3a34ea872dec73658fac59743e46ef8db5151..4c3b8d7ba777916ffc5ad0feccffbc7634bffe8d 100644 +--- a/net/minecraft/world/inventory/ChestMenu.java ++++ b/net/minecraft/world/inventory/ChestMenu.java +@@ -66,10 +66,30 @@ public class ChestMenu extends AbstractContainerMenu { + return new ChestMenu(MenuType.GENERIC_9x6, containerId, playerInventory, 6); + } + ++ // Purpur start - Barrels and enderchests 6 rows ++ public static ChestMenu oneRow(int syncId, Inventory playerInventory, Container inventory) { ++ return new ChestMenu(MenuType.GENERIC_9x1, syncId, playerInventory, inventory, 1); ++ } ++ ++ public static ChestMenu twoRows(int syncId, Inventory playerInventory, Container inventory) { ++ return new ChestMenu(MenuType.GENERIC_9x2, syncId, playerInventory, inventory, 2); ++ } ++ // Purpur end - Barrels and enderchests 6 rows ++ + public static ChestMenu threeRows(int containerId, Inventory playerInventory, Container container) { + return new ChestMenu(MenuType.GENERIC_9x3, containerId, playerInventory, container, 3); + } + ++ // Purpur start - Barrels and enderchests 6 rows ++ public static ChestMenu fourRows(int syncId, Inventory playerInventory, Container inventory) { ++ return new ChestMenu(MenuType.GENERIC_9x4, syncId, playerInventory, inventory, 4); ++ } ++ ++ public static ChestMenu fiveRows(int syncId, Inventory playerInventory, Container inventory) { ++ return new ChestMenu(MenuType.GENERIC_9x5, syncId, playerInventory, inventory, 5); ++ } ++ // Purpur end - Barrels and enderchests 6 rows ++ + public static ChestMenu sixRows(int containerId, Inventory playerInventory, Container container) { + return new ChestMenu(MenuType.GENERIC_9x6, containerId, playerInventory, container, 6); + } +diff --git a/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/net/minecraft/world/inventory/PlayerEnderChestContainer.java +index 9749909b8253b432bb2f7fba2cd8ff17a8579b30..2c65c6428cb5925f8505cb0836b359f7e18c64eb 100644 +--- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java ++++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java +@@ -26,11 +26,18 @@ public class PlayerEnderChestContainer extends SimpleContainer { + } + + public PlayerEnderChestContainer(Player owner) { +- super(27); ++ super(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? 54 : 27); // Purpur - Barrels and enderchests 6 rows + this.owner = owner; + // CraftBukkit end + } + ++ // Purpur start - Barrels and enderchests 6 rows ++ @Override ++ public int getContainerSize() { ++ return owner.sixRowEnderchestSlotCount < 0 ? super.getContainerSize() : owner.sixRowEnderchestSlotCount; ++ } ++ // Purpur end - Barrels and enderchests 6 rows ++ + public void setActiveChest(EnderChestBlockEntity enderChestBlockEntity) { + this.activeChest = enderChestBlockEntity; + } +diff --git a/net/minecraft/world/level/block/EnderChestBlock.java b/net/minecraft/world/level/block/EnderChestBlock.java +index 383e8285d366c7f594b0b4ff55b367970c9b69e4..077edf449d4faa41abafb94ac41dfe822f449d04 100644 +--- a/net/minecraft/world/level/block/EnderChestBlock.java ++++ b/net/minecraft/world/level/block/EnderChestBlock.java +@@ -85,8 +85,8 @@ public class EnderChestBlock extends AbstractChestBlock i + enderChestInventory.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations + if (level instanceof ServerLevel serverLevel && player.openMenu( + new SimpleMenuProvider( +- (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE +- ) ++ (containerId, playerInventory, player1) -> org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? getEnderChestSixRows(containerId, playerInventory, player, enderChestInventory) : ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE ++ ) // Purpur - Barrels and enderchests 6 rows + ).isPresent()) { + // Paper end - Fix InventoryOpenEvent cancellation - moved up; + player.awardStat(Stats.OPEN_ENDERCHEST); +@@ -100,6 +100,35 @@ public class EnderChestBlock extends AbstractChestBlock i + } + } + ++ // Purpur start - Barrels and enderchests 6 rows ++ private ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) { ++ if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) { ++ org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity(); ++ if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) { ++ player.sixRowEnderchestSlotCount = 54; ++ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer); ++ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.five")) { ++ player.sixRowEnderchestSlotCount = 45; ++ return ChestMenu.fiveRows(syncId, inventory, playerEnderChestContainer); ++ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.four")) { ++ player.sixRowEnderchestSlotCount = 36; ++ return ChestMenu.fourRows(syncId, inventory, playerEnderChestContainer); ++ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.three")) { ++ player.sixRowEnderchestSlotCount = 27; ++ return ChestMenu.threeRows(syncId, inventory, playerEnderChestContainer); ++ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.two")) { ++ player.sixRowEnderchestSlotCount = 18; ++ return ChestMenu.twoRows(syncId, inventory, playerEnderChestContainer); ++ } else if (bukkitPlayer.hasPermission("purpur.enderchest.rows.one")) { ++ player.sixRowEnderchestSlotCount = 9; ++ return ChestMenu.oneRow(syncId, inventory, playerEnderChestContainer); ++ } ++ } ++ player.sixRowEnderchestSlotCount = -1; ++ return ChestMenu.sixRows(syncId, inventory, playerEnderChestContainer); ++ } ++ // Purpur end - Barrels and enderchests 6 rows ++ + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new EnderChestBlockEntity(pos, state); +diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +index 75c09a2079c89f9346391abdd01ef8790b9cbb13..04f6b3c328377091734a111f5a219379e32b5640 100644 +--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +@@ -59,7 +59,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { + } + // CraftBukkit end + private static final Component DEFAULT_NAME = Component.translatable("container.barrel"); +- private NonNullList items = NonNullList.withSize(27, ItemStack.EMPTY); ++ // Purpur start - Barrels and enderchests 6 rows ++ private NonNullList items = NonNullList.withSize(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { ++ case 6 -> 54; ++ case 5 -> 45; ++ case 4 -> 36; ++ case 2 -> 18; ++ case 1 -> 9; ++ default -> 27; ++ }, ItemStack.EMPTY); ++ // Purpur end - Barrels and enderchests 6 rows + public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { + @Override + protected void onOpen(Level level, BlockPos pos, BlockState state) { +@@ -111,7 +120,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { + + @Override + public int getContainerSize() { +- return 27; ++ // Purpur start - Barrels and enderchests 6 rows ++ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { ++ case 6 -> 54; ++ case 5 -> 45; ++ case 4 -> 36; ++ case 2 -> 18; ++ case 1 -> 9; ++ default -> 27; ++ }; ++ // Purpur end - Barrels and enderchests 6 rows + } + + @Override +@@ -131,7 +149,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { + + @Override + protected AbstractContainerMenu createMenu(int id, Inventory player) { +- return ChestMenu.threeRows(id, player, this); ++ // Purpur start - Barrels and enderchests 6 rows ++ return switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { ++ case 6 -> ChestMenu.sixRows(id, player, this); ++ case 5 -> ChestMenu.fiveRows(id, player, this); ++ case 4 -> ChestMenu.fourRows(id, player, this); ++ case 2 -> ChestMenu.twoRows(id, player, this); ++ case 1 -> ChestMenu.oneRow(id, player, this); ++ default -> ChestMenu.threeRows(id, player, this); ++ }; ++ // Purpur end - Barrels and enderchests 6 rows + } + + @Override diff --git a/purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch b/purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch new file mode 100644 index 000000000..3ca516628 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 12 May 2019 00:43:12 -0500 +Subject: [PATCH] Giants AI settings + + +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index 416bb85fc87b5f9e372f33df8fe27f12e83834b9..40cf8f44a6b01d45306dab4ba4f9a7ea7c6590a9 100644 +--- a/net/minecraft/world/entity/monster/Giant.java ++++ b/net/minecraft/world/entity/monster/Giant.java +@@ -30,8 +30,25 @@ public class Giant extends Monster { + + @Override + protected void registerGoals() { +- this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); +- this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ // Purpur start - Giants AI settings ++ if (level().purpurConfig.giantHaveAI) { ++ this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); ++ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ this.goalSelector.addGoal(7, new net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal(this, 1.0D)); ++ this.goalSelector.addGoal(8, new net.minecraft.world.entity.ai.goal.LookAtPlayerGoal(this, net.minecraft.world.entity.player.Player.class, 16.0F)); ++ this.goalSelector.addGoal(8, new net.minecraft.world.entity.ai.goal.RandomLookAroundGoal(this)); ++ this.goalSelector.addGoal(5, new net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal(this, 1.0D)); ++ if (level().purpurConfig.giantHaveHostileAI) { ++ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false)); ++ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this).setAlertOthers(net.minecraft.world.entity.monster.zombie.ZombifiedPiglin.class)); ++ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.player.Player.class, true)); ++ this.targetSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.npc.villager.Villager.class, false)); ++ this.targetSelector.addGoal(4, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.animal.golem.IronGolem.class, true)); ++ this.targetSelector.addGoal(5, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.animal.turtle.Turtle.class, true)); ++ } ++ } ++ // Purpur end - Giants AI settings + } + // Purpur end - Ridables + +@@ -53,8 +70,36 @@ public class Giant extends Monster { + .add(Attributes.CAMERA_DISTANCE, 16.0); + } + ++ // Purpur - Giants AI settings ++ @Override ++ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, @org.jetbrains.annotations.Nullable net.minecraft.world.entity.SpawnGroupData entityData) { ++ net.minecraft.world.entity.SpawnGroupData groupData = super.finalizeSpawn(world, difficulty, spawnReason, entityData); ++ if (groupData == null) { ++ populateDefaultEquipmentSlots(this.random, difficulty); ++ populateDefaultEquipmentEnchantments(world, this.random, difficulty); ++ } ++ return groupData; ++ } ++ ++ @Override ++ protected void populateDefaultEquipmentSlots(net.minecraft.util.RandomSource random, net.minecraft.world.DifficultyInstance difficulty) { ++ super.populateDefaultEquipmentSlots(this.random, difficulty); ++ // TODO make configurable ++ if (random.nextFloat() < (level().getDifficulty() == net.minecraft.world.Difficulty.HARD ? 0.1F : 0.05F)) { ++ this.setItemSlot(net.minecraft.world.entity.EquipmentSlot.MAINHAND, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.IRON_SWORD)); ++ } ++ } ++ ++ @Override ++ public float getJumpPower() { ++ // make giants jump as high as everything else relative to their size ++ // 1.0 makes bottom of feet about as high as their waist when they jump ++ return level().purpurConfig.giantJumpHeight; ++ } ++ // Purpur end - Giants AI settings ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { +- return level.getPathfindingCostFromLightLevels(pos); ++ return super.getWalkTargetValue(pos, level); // Purpur - Giants AI settings - fix light requirements for natural spawns + } + } diff --git a/purpur-server/minecraft-patches/features/0005-Chickens-can-retaliate.patch b/purpur-server/minecraft-patches/features/0005-Chickens-can-retaliate.patch new file mode 100644 index 000000000..deaa95d09 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0005-Chickens-can-retaliate.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 12 Apr 2020 13:19:34 -0500 +Subject: [PATCH] Chickens can retaliate + + +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index bfc5c6639ef1005d2752d5164f3613ddacc46bf1..52a0bf792337e2f1cf11e215b033caae21ee774b 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -95,6 +95,11 @@ public class Chicken extends Animal { + public void initAttributes() { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.chickenMaxHealth); + this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.chickenScale); ++ // Purpur start - Chickens can retaliate ++ if (level().purpurConfig.chickenRetaliate) { ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(2.0D); ++ } ++ // Purpur end - Chickens can retaliate + } + // Purpur end - Configurable entity base attributes + +@@ -102,13 +107,21 @@ public class Chicken extends Animal { + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +- this.goalSelector.addGoal(1, new PanicGoal(this, 1.4)); ++ //this.goalSelector.addGoal(1, new PanicGoal(this, 1.4)); // Purpur - Chickens can retaliate - moved down + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); + this.goalSelector.addGoal(3, new TemptGoal(this, 1.0, itemStack -> itemStack.is(ItemTags.CHICKEN_FOOD), false)); + this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.1)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(7, new RandomLookAroundGoal(this)); ++ // Purpur start - Chickens can retaliate ++ if (level().purpurConfig.chickenRetaliate) { ++ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.0D, false)); ++ this.targetSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal(this)); ++ } else { ++ this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D)); ++ } ++ // Purpur end - Chickens can retaliate + } + + @Override +@@ -117,7 +130,7 @@ public class Chicken extends Animal { + } + + public static AttributeSupplier.Builder createAttributes() { +- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0).add(Attributes.MOVEMENT_SPEED, 0.25); ++ return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0).add(Attributes.MOVEMENT_SPEED, 0.25).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur - Chickens can retaliate + } + + @Override diff --git a/purpur-server/minecraft-patches/features/0006-Minecart-settings-and-WASD-controls.patch b/purpur-server/minecraft-patches/features/0006-Minecart-settings-and-WASD-controls.patch new file mode 100644 index 000000000..790c8a321 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0006-Minecart-settings-and-WASD-controls.patch @@ -0,0 +1,152 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 29 Jun 2019 02:32:40 -0500 +Subject: [PATCH] Minecart settings and WASD controls + + +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 2eba1b0ac8b4a0bb34d04b81c4c279db6e716b3b..01efe1fbd185ebd3a60c6bbb7aa2d82817506e7d 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -1311,6 +1311,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + } else { + // Purpur start - Add boat fall damage config + if (damageSource.is(net.minecraft.tags.DamageTypeTags.IS_FALL)) { ++ // Purpur start - Minecart settings and WASD controls ++ if (getRootVehicle() instanceof AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) { ++ return false; ++ } ++ // Purpur end - Minecart settings and WASD controls + if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.boat.Boat && !level().purpurConfig.boatsDoFallDamage) { + return false; + } +diff --git a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java +index 1123ec0038552e0b40774f4a433ff325695ea071..48099ae66045b6e78ec52832e3b972ac9c9bc246 100644 +--- a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java ++++ b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java +@@ -105,6 +105,10 @@ public abstract class AbstractMinecart extends VehicleEntity { + private double flyingY = 0.95; + private double flyingZ = 0.95; + public @Nullable Double maxSpeed; ++ // Purpur start - Minecart settings and WASD controls ++ public double storedMaxSpeed; ++ public boolean isNewBehavior; ++ // Purpur end - Minecart settings and WASD controls + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API + // CraftBukkit end + +@@ -113,8 +117,13 @@ public abstract class AbstractMinecart extends VehicleEntity { + this.blocksBuilding = true; + if (useExperimentalMovement(level)) { + this.behavior = new NewMinecartBehavior(this); ++ this.isNewBehavior = true; // Purpur - Minecart settings and WASD controls + } else { + this.behavior = new OldMinecartBehavior(this); ++ // Purpur start - Minecart settings and WASD controls ++ this.isNewBehavior = false; ++ maxSpeed = storedMaxSpeed = level.purpurConfig.minecartMaxSpeed; ++ // Purpur end - Minecart settings and WASD controls + } + } + +@@ -278,6 +287,14 @@ public abstract class AbstractMinecart extends VehicleEntity { + + @Override + public void tick() { ++ // Purpur start - Minecart settings and WASD controls ++ if (!this.isNewBehavior) { ++ if (storedMaxSpeed != level().purpurConfig.minecartMaxSpeed) { ++ maxSpeed = storedMaxSpeed = level().purpurConfig.minecartMaxSpeed; ++ } ++ } ++ // Purpur end - Minecart settings and WASD controls ++ + // CraftBukkit start + double prevX = this.getX(); + double prevY = this.getY(); +@@ -392,15 +409,61 @@ public abstract class AbstractMinecart extends VehicleEntity { + this.behavior.moveAlongTrack(level); + } + ++ // Purpur start - Minecart settings and WASD controls ++ private Double lastSpeed; ++ ++ public double getControllableSpeed() { ++ BlockState blockState = level().getBlockState(this.blockPosition()); ++ if (!blockState.isSolid()) { ++ blockState = level().getBlockState(this.blockPosition().relative(Direction.DOWN)); ++ } ++ Double speed = level().purpurConfig.minecartControllableBlockSpeeds.get(blockState.getBlock()); ++ if (!blockState.isSolid()) { ++ speed = lastSpeed; ++ } ++ if (speed == null) { ++ speed = level().purpurConfig.minecartControllableBaseSpeed; ++ } ++ return lastSpeed = speed; ++ } ++ // Purpur end - Minecart settings and WASD controls ++ + protected void comeOffTrack(ServerLevel level) { + double maxSpeed = this.getMaxSpeed(level); + Vec3 deltaMovement = this.getDeltaMovement(); + this.setDeltaMovement(Mth.clamp(deltaMovement.x, -maxSpeed, maxSpeed), deltaMovement.y, Mth.clamp(deltaMovement.z, -maxSpeed, maxSpeed)); ++ ++ // Purpur start - Minecart settings and WASD controls ++ if (level().purpurConfig.minecartControllable && !isInWater() && !isInLava() && !passengers.isEmpty()) { ++ Entity passenger = passengers.get(0); ++ if (passenger instanceof net.minecraft.server.level.ServerPlayer player) { ++ net.minecraft.world.entity.player.Input lastClientInput = player.getLastClientInput(); ++ float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F); ++ if (lastClientInput.jump() && this.onGround) { ++ setDeltaMovement(new Vec3(getDeltaMovement().x, level().purpurConfig.minecartControllableHopBoost, getDeltaMovement().z)); ++ } ++ if (forward != 0.0F) { ++ org.bukkit.util.Vector velocity = player.getBukkitEntity().getEyeLocation().getDirection().normalize().multiply(getControllableSpeed()); ++ if (forward < 0.0) { ++ velocity.multiply(-0.5); ++ } ++ setDeltaMovement(new Vec3(velocity.getX(), getDeltaMovement().y, velocity.getZ())); ++ } ++ this.setYRot(passenger.getYRot() - 90); ++ maxUpStep = level().purpurConfig.minecartControllableStepHeight; ++ } else { ++ maxUpStep = 0.0F; ++ } ++ } else { ++ maxUpStep = 0.0F; ++ } ++ // Purpur end - Minecart settings and WASD controls + if (this.onGround()) { + // CraftBukkit start - replace magic numbers with our variables + this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ)); + // CraftBukkit end + } ++ else if (level().purpurConfig.minecartControllable) setDeltaMovement(new Vec3(getDeltaMovement().x * derailedX, getDeltaMovement().y, getDeltaMovement().z * derailedZ)); // Purpur - Minecart settings and WASD controls + + this.move(MoverType.SELF, this.getDeltaMovement()); + if (!this.onGround()) { +diff --git a/net/minecraft/world/item/MinecartItem.java b/net/minecraft/world/item/MinecartItem.java +index 3b511139985ea1646661ae0a6bcd1f0e386561e7..3a65e1a0b914d113feb727833803ece8f3b2e020 100644 +--- a/net/minecraft/world/item/MinecartItem.java ++++ b/net/minecraft/world/item/MinecartItem.java +@@ -30,8 +30,9 @@ public class MinecartItem extends Item { + BlockPos clickedPos = context.getClickedPos(); + BlockState blockState = level.getBlockState(clickedPos); + if (!blockState.is(BlockTags.RAILS)) { +- return InteractionResult.FAIL; +- } else { ++ if (!level.purpurConfig.minecartPlaceAnywhere) return InteractionResult.FAIL; // Purpur - Minecart settings and WASD controls ++ if (blockState.isSolid()) clickedPos = clickedPos.relative(context.getClickedFace()); ++ } // else { // Purpur - Minecart settings and WASD controls + ItemStack itemInHand = context.getItemInHand(); + RailShape railShape = blockState.getBlock() instanceof BaseRailBlock + ? blockState.getValue(((BaseRailBlock)blockState.getBlock()).getShapeProperty()) +@@ -72,6 +73,6 @@ public class MinecartItem extends Item { + itemInHand.shrink(1); + return InteractionResult.SUCCESS; + } +- } ++ // } // Purpur - Minecart settings and WASD controls + } + } diff --git a/purpur-server/minecraft-patches/features/0007-Villagers-follow-emerald-blocks.patch b/purpur-server/minecraft-patches/features/0007-Villagers-follow-emerald-blocks.patch new file mode 100644 index 000000000..cb6f356d1 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0007-Villagers-follow-emerald-blocks.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Fri, 29 Nov 2019 22:10:12 -0600 +Subject: [PATCH] Villagers follow emerald blocks + + +diff --git a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +index 686776bb00560f9da8838bd5f8dd64aaddfa7a2b..ccdd439a89b7e7e10ee960cfe1e5e119d5367799 100644 +--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java ++++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +@@ -173,7 +173,7 @@ public class DefaultAttributes { + .put(EntityType.VILLAGER, Villager.createAttributes().build()) + .put(EntityType.VINDICATOR, Vindicator.createAttributes().build()) + .put(EntityType.WARDEN, Warden.createAttributes().build()) +- .put(EntityType.WANDERING_TRADER, Mob.createMobAttributes().build()) ++ .put(EntityType.WANDERING_TRADER, net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader.createAttributes().build()) // Purpur - Villagers follow emerald blocks + .put(EntityType.WITCH, Witch.createAttributes().build()) + .put(EntityType.WITHER, WitherBoss.createAttributes().build()) + .put(EntityType.WITHER_SKELETON, AbstractSkeleton.createAttributes().build()) +diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java +index 18030dc04eb7d7971e457637b5320b1e41665658..60ebd57de496eba6ad307195ffacd3b7fc4149ff 100644 +--- a/net/minecraft/world/entity/ai/goal/TemptGoal.java ++++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java +@@ -70,7 +70,7 @@ public class TemptGoal extends Goal { + } + + private boolean shouldFollow(LivingEntity entity) { +- return this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem()); ++ return (this.items.test(entity.getMainHandItem()) || this.items.test(entity.getOffhandItem())) && (!(this.mob instanceof net.minecraft.world.entity.npc.villager.Villager villager) || !villager.isSleeping()); // Purpur - Villagers follow emerald blocks + } + + @Override +diff --git a/net/minecraft/world/entity/npc/villager/AbstractVillager.java b/net/minecraft/world/entity/npc/villager/AbstractVillager.java +index fa8f1ea38192f9ad0a961a53399f295d83af7721..05d1a859e1aee639ad9572f2369e6db3c3d7a111 100644 +--- a/net/minecraft/world/entity/npc/villager/AbstractVillager.java ++++ b/net/minecraft/world/entity/npc/villager/AbstractVillager.java +@@ -38,6 +38,7 @@ import net.minecraft.world.phys.Vec3; + import org.jspecify.annotations.Nullable; + + public abstract class AbstractVillager extends AgeableMob implements InventoryCarrier, Npc, Merchant { ++ public static final net.minecraft.world.item.crafting.Ingredient TEMPT_ITEMS = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.level.block.Blocks.EMERALD_BLOCK.asItem()); // Purpur - Villagers follow emerald blocks + private static final EntityDataAccessor DATA_UNHAPPY_COUNTER = SynchedEntityData.defineId(AbstractVillager.class, EntityDataSerializers.INT); + public static final int VILLAGER_SLOT_OFFSET = 300; + private static final int VILLAGER_INVENTORY_SIZE = 8; +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index 6f9658af51f1f30434756f871cf6ab5cd2f3a7ea..02836bd2e8fda563877d3014ab839734aebc7457 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -269,6 +269,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ if (level().purpurConfig.villagerFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); // Purpur - Villagers follow emerald blocks + } + // Purpur end - Ridables + +@@ -277,6 +278,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + public void initAttributes() { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.villagerMaxHealth); + this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.villagerScale); ++ this.getAttribute(Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.villagerTemptRange); // Purpur - Villagers follow emerald blocks + } + // Purpur end - Configurable entity base attributes + +@@ -345,7 +347,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + + public static AttributeSupplier.Builder createAttributes() { +- return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5); ++ return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.TEMPT_RANGE, 10.0D); // Purpur - Villagers follow emerald blocks + } + + public boolean assignProfessionWhenSpawned() { +diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +index 9d5dbaeafd899594425547fc58b87a1d0a52066e..c6c4f4f2a970db7e782181eaca312931b192b0d5 100644 +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -89,9 +89,16 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + @Override + public void initAttributes() { + this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.wanderingTraderMaxHealth); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.wanderingTraderTemptRange); // Purpur - Villagers follow emerald blocks + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Villagers follow emerald blocks ++ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() { ++ return Mob.createMobAttributes().add(net.minecraft.world.entity.ai.attributes.Attributes.TEMPT_RANGE, 10.0D); ++ } ++ // Purpur end - Villagers follow emerald blocks ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +@@ -126,6 +133,7 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + this.goalSelector.addGoal(1, new PanicGoal(this, 0.5)); + this.goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this)); + this.goalSelector.addGoal(2, new WanderingTrader.WanderToPositionGoal(this, 2.0, 0.35)); ++ if (level().purpurConfig.wanderingTraderFollowEmeraldBlock) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, TEMPT_ITEMS, false)); // Purpur - Villagers follow emerald blocks + this.goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.35)); + this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 0.35)); + this.goalSelector.addGoal(9, new InteractGoal(this, Player.class, 3.0F, 1.0F)); diff --git a/purpur-server/minecraft-patches/features/0008-Implement-elytra-settings.patch b/purpur-server/minecraft-patches/features/0008-Implement-elytra-settings.patch new file mode 100644 index 000000000..1384c26ae --- /dev/null +++ b/purpur-server/minecraft-patches/features/0008-Implement-elytra-settings.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 25 Jul 2019 18:07:37 -0500 +Subject: [PATCH] Implement elytra settings + + +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 983aea0bb8c581f06030adf74f0f7962f99c3c2a..c0a686ba573642f8f666db7c1636e29a770e9c05 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -3883,7 +3883,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + .filter(equipmentSlot1 -> canGlideUsing(this.getItemBySlot(equipmentSlot1), equipmentSlot1)) + .toList(); + EquipmentSlot equipmentSlot = Util.getRandom(list, this.random); +- this.getItemBySlot(equipmentSlot).hurtAndBreak(1, this, equipmentSlot); ++ ++ // Purpur start - Implement elytra settings ++ int damage = level().purpurConfig.elytraDamagePerSecond; ++ if (level().purpurConfig.elytraDamageMultiplyBySpeed > 0) { ++ double speed = getDeltaMovement().lengthSqr(); ++ if (speed > level().purpurConfig.elytraDamageMultiplyBySpeed) { ++ damage *= (int) speed; ++ } ++ } ++ ++ this.getItemBySlot(equipmentSlot).hurtAndBreak(damage, this, equipmentSlot); ++ // Purpur end - Implement elytra settings + } + + this.gameEvent(GameEvent.ELYTRA_GLIDE); +diff --git a/net/minecraft/world/item/FireworkRocketItem.java b/net/minecraft/world/item/FireworkRocketItem.java +index 2c597e20c1ff587f2eadef600bedb9e01b999bbf..e548f7161f14d7dc02d6882c3639ff65d59d7b37 100644 +--- a/net/minecraft/world/item/FireworkRocketItem.java ++++ b/net/minecraft/world/item/FireworkRocketItem.java +@@ -73,6 +73,17 @@ public class FireworkRocketItem extends Item implements ProjectileItem { + if (player.dropAllLeashConnections(null)) { + level.playSound(null, player, SoundEvents.LEAD_BREAK, SoundSource.NEUTRAL, 1.0F, 1.0F); + } ++ // Purpur start - Implement elytra settings ++ if (level.purpurConfig.elytraDamagePerFireworkBoost > 0) { ++ java.util.List list = net.minecraft.world.entity.EquipmentSlot.VALUES.stream().filter((enumitemslot) -> net.minecraft.world.entity.LivingEntity.canGlideUsing(player.getItemBySlot(enumitemslot), enumitemslot)).toList(); ++ net.minecraft.world.entity.EquipmentSlot enumitemslot = net.minecraft.util.Util.getRandom(list, player.random); ++ ++ ItemStack glideItem = player.getItemBySlot(enumitemslot); ++ if (player.canGlide()) { ++ glideItem.hurtAndBreak(level.purpurConfig.elytraDamagePerFireworkBoost, player, enumitemslot); ++ } ++ } ++ // Purpur end - Implement elytra settings + if (event.shouldConsume() && !player.hasInfiniteMaterials()) { + itemInHand.shrink(1); // Moved up from below + } else { +diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java +index b8e38ce9b5c4b11e5bcbe6cf5603d6d39454759c..fd90ba8eba88c8d4fa372cb1047e0af8528f914d 100644 +--- a/net/minecraft/world/item/ItemStack.java ++++ b/net/minecraft/world/item/ItemStack.java +@@ -702,6 +702,14 @@ public final class ItemStack implements DataComponentHolder { + org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent(serverPlayer, this); // Paper - Add EntityDamageItemEvent + } + // CraftBukkit end ++ ++ // Purpur start - Implement elytra settings ++ if (this.has(DataComponents.GLIDER)) { ++ setDamageValue(this.getMaxDamage() - 1); ++ return; ++ } ++ // Purpur end - Implement elytra settings ++ + this.shrink(1); + onBreak.accept(item); + } +diff --git a/net/minecraft/world/item/TridentItem.java b/net/minecraft/world/item/TridentItem.java +index b87861c089f849e855d10c15ede53fd7c9e6a47c..633bb1a6d69b55717a9a07978b92f625ff5d066a 100644 +--- a/net/minecraft/world/item/TridentItem.java ++++ b/net/minecraft/world/item/TridentItem.java +@@ -126,6 +126,17 @@ public class TridentItem extends Item implements ProjectileItem { + f1 *= tridentSpinAttackStrength / squareRoot; + f2 *= tridentSpinAttackStrength / squareRoot; + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(player, stack, f, f1, f2)) return false; // Paper - Add player riptide event ++ // Purpur start - Implement elytra settings ++ List list = net.minecraft.world.entity.EquipmentSlot.VALUES.stream().filter((enumitemslot) -> LivingEntity.canGlideUsing(entity.getItemBySlot(enumitemslot), enumitemslot)).toList(); ++ if (!list.isEmpty()) { ++ net.minecraft.world.entity.EquipmentSlot enumitemslot = net.minecraft.util.Util.getRandom(list, entity.random); ++ ItemStack glideItem = entity.getItemBySlot(enumitemslot); ++ if (glideItem.has(net.minecraft.core.component.DataComponents.GLIDER) && level.purpurConfig.elytraDamagePerTridentBoost > 0) { ++ glideItem.hurtAndBreak(level.purpurConfig.elytraDamagePerTridentBoost, entity, enumitemslot); ++ } ++ } ++ // Purpur end - Implement elytra settings ++ + player.push(f, f1, f2); + player.startAutoSpinAttack(20, 8.0F, stack); + if (player.onGround()) { diff --git a/purpur-server/minecraft-patches/features/0009-Configurable-jockey-options.patch b/purpur-server/minecraft-patches/features/0009-Configurable-jockey-options.patch new file mode 100644 index 000000000..0857bb625 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0009-Configurable-jockey-options.patch @@ -0,0 +1,177 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 26 Mar 2020 21:39:32 -0500 +Subject: [PATCH] Configurable jockey options + + +diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java +index 0b6d2bcec8506686eb6e0aaeb14870c14bd84e9d..3b1e3e7c1218cbfdfe48db3fad15280f43fd3311 100644 +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -109,6 +109,23 @@ public class Drowned extends Zombie implements RangedAttackMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Configurable jockey options ++ @Override ++ public boolean jockeyOnlyBaby() { ++ return level().purpurConfig.drownedJockeyOnlyBaby; ++ } ++ ++ @Override ++ public double jockeyChance() { ++ return level().purpurConfig.drownedJockeyChance; ++ } ++ ++ @Override ++ public boolean jockeyTryExistingChickens() { ++ return level().purpurConfig.drownedJockeyTryExistingChickens; ++ } ++ // Purpur end - Configurable jockey options ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index 03270138e6fae7ee28f958cafee5b74b18dce355..d31145fee0f646d734e90199288b29f07854d066 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -58,6 +58,23 @@ public class Husk extends Zombie { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Configurable jockey options ++ @Override ++ public boolean jockeyOnlyBaby() { ++ return level().purpurConfig.huskJockeyOnlyBaby; ++ } ++ ++ @Override ++ public double jockeyChance() { ++ return level().purpurConfig.huskJockeyChance; ++ } ++ ++ @Override ++ public boolean jockeyTryExistingChickens() { ++ return level().purpurConfig.huskJockeyTryExistingChickens; ++ } ++ // Purpur end - Configurable jockey options ++ + @Override + public boolean isSunSensitive() { + return false; +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index d873ca9873e95f3a5869cb63a93b0643a9c867dc..638642628c3dc9fa25d25c589029219c23d1e602 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -128,6 +128,20 @@ public class Zombie extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Configurable jockey options ++ public boolean jockeyOnlyBaby() { ++ return level().purpurConfig.zombieJockeyOnlyBaby; ++ } ++ ++ public double jockeyChance() { ++ return level().purpurConfig.zombieJockeyChance; ++ } ++ ++ public boolean jockeyTryExistingChickens() { ++ return level().purpurConfig.zombieJockeyTryExistingChickens; ++ } ++ // Purpur end - Configurable jockey options ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +@@ -531,19 +545,18 @@ public class Zombie extends Monster { + } + + if (spawnGroupData instanceof Zombie.ZombieGroupData zombieGroupData) { +- if (zombieGroupData.isBaby) { +- this.setBaby(true); ++ if (!jockeyOnlyBaby() || zombieGroupData.isBaby) { // Purpur - Configurable jockey options ++ this.setBaby(zombieGroupData.isBaby); // Purpur - Configurable jockey options + if (zombieGroupData.canSpawnJockey) { +- if (random.nextFloat() < 0.05) { +- List entitiesOfClass = level.getEntitiesOfClass( ++ if (random.nextFloat() < jockeyChance()) { // Purpur - Configurable jockey options ++ List entitiesOfClass = jockeyTryExistingChickens() ? level.getEntitiesOfClass( // Purpur - Configurable jockey options + Chicken.class, this.getBoundingBox().inflate(5.0, 3.0, 5.0), EntitySelector.ENTITY_NOT_BEING_RIDDEN +- ); ++ ) : java.util.Collections.emptyList(); // Purpur - Configurable jockey options + if (!entitiesOfClass.isEmpty()) { + Chicken chicken = entitiesOfClass.get(0); + chicken.setChickenJockey(true); + this.startRiding(chicken, false, false); +- } +- } else if (random.nextFloat() < 0.05) { ++ } else { // Purpur - Configurable jockey options + Chicken chicken1 = EntityType.CHICKEN.create(this.level(), EntitySpawnReason.JOCKEY); + if (chicken1 != null) { + chicken1.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F); +@@ -552,6 +565,7 @@ public class Zombie extends Monster { + this.startRiding(chicken1, false, false); + level.addFreshEntity(chicken1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit + } ++ } // Purpur - Configurable jockey options + } + } + } +diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +index 0a02684ec00540b91a2a68e5787e51d15b3743a7..62f55763d95e496da8b8fcf7d95752777e323b48 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -114,6 +114,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Configurable jockey options ++ @Override ++ public boolean jockeyOnlyBaby() { ++ return level().purpurConfig.zombieVillagerJockeyOnlyBaby; ++ } ++ ++ @Override ++ public double jockeyChance() { ++ return level().purpurConfig.zombieVillagerJockeyChance; ++ } ++ ++ @Override ++ public boolean jockeyTryExistingChickens() { ++ return level().purpurConfig.zombieVillagerJockeyTryExistingChickens; ++ } ++ // Purpur end - Configurable jockey options ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +index 3ff8c95075f9d25c3a2e4160ee6d18057838a7b5..ec39ad6740361774f9ecfda7186cab9d8fac90f2 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -89,6 +89,23 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Configurable jockey options ++ @Override ++ public boolean jockeyOnlyBaby() { ++ return level().purpurConfig.zombifiedPiglinJockeyOnlyBaby; ++ } ++ ++ @Override ++ public double jockeyChance() { ++ return level().purpurConfig.zombifiedPiglinJockeyChance; ++ } ++ ++ @Override ++ public boolean jockeyTryExistingChickens() { ++ return level().purpurConfig.zombifiedPiglinJockeyTryExistingChickens; ++ } ++ // Purpur end - Configurable jockey options ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); diff --git a/patches/server/0067-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/purpur-server/minecraft-patches/features/0010-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch similarity index 62% rename from patches/server/0067-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch rename to purpur-server/minecraft-patches/features/0010-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch index ddfa22677..b49a87897 100644 --- a/patches/server/0067-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch +++ b/purpur-server/minecraft-patches/features/0010-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch @@ -4,28 +4,28 @@ Date: Thu, 9 May 2019 18:26:06 -0500 Subject: [PATCH] Phantoms attracted to crystals and crystals shoot phantoms -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -index ffc5b68c4246a7111845230a75552bb15875a209..ef0098e46bda8abc456f2bb5929d874c6aeb8698 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java -@@ -30,6 +30,12 @@ public class EndCrystal extends Entity { - private static final EntityDataAccessor DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN); +diff --git a/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +index d1c593ccfab7bee4366ee7c56606a230964e3fb7..21d678008fac473dff4c3e10890882ab94d0087c 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +@@ -27,6 +27,12 @@ public class EndCrystal extends Entity { + private static final boolean DEFAULT_SHOW_BOTTOM = true; public int time; public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals -+ // Purpur start ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms + private net.minecraft.world.entity.monster.Phantom targetPhantom; + private int phantomBeamTicks = 0; + private int phantomDamageCooldown = 0; + private int idleCooldown = 0; -+ // Purpur end ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms - public EndCrystal(EntityType type, Level world) { - super(type, world); -@@ -79,7 +85,50 @@ public class EndCrystal extends Entity { - // Paper end - } + public EndCrystal(EntityType type, Level level) { + super(type, level); +@@ -95,6 +101,49 @@ public class EndCrystal extends Entity { + // Paper end - Fix invulnerable end crystals + if (this.level().purpurConfig.endCrystalCramming > 0 && this.level().getEntitiesOfClass(EndCrystal.class, getBoundingBox()).size() > this.level().purpurConfig.endCrystalCramming) this.hurt(this.damageSources().cramming(), 6.0F); // Purpur - End Crystal Cramming -+ // Purpur start ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms + if (level().purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) { + return; // on cooldown + } @@ -67,81 +67,78 @@ index ffc5b68c4246a7111845230a75552bb15875a209..ef0098e46bda8abc456f2bb5929d874c + phantomBeamTicks = 0; + phantomDamageCooldown = 0; + idleCooldown = 60; ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms } -+ // Purpur end @Override - protected void addAdditionalSaveData(CompoundTag nbt) { -diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 961300cb8bcc7b0aff476a435aa33e713bd520a6..86df67578334a4743909c748213c2e1ed5d19bd9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -49,6 +49,7 @@ public class Phantom extends FlyingMob implements Enemy { - Vec3 moveTargetPoint; - public BlockPos anchorPoint; - Phantom.AttackPhase attackPhase; -+ Vec3 crystalPosition; // Purpur - - public Phantom(EntityType type, Level world) { - super(type, world); -@@ -116,6 +117,23 @@ public class Phantom extends FlyingMob implements Enemy { - level().addFreshEntity(flames); - return true; +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 046d6d3b665d0dbb5403ebe91d18503ec7613936..7077beafe2251fcc4e37578091bfaa02714268b9 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -49,6 +49,7 @@ public class Phantom extends Mob implements Enemy { + Vec3 moveTargetPoint = Vec3.ZERO; + @Nullable public BlockPos anchorPoint; + Phantom.AttackPhase attackPhase = Phantom.AttackPhase.CIRCLE; ++ Vec3 crystalPosition; // Purpur - Phantoms attracted to crystals and crystals shoot phantoms + // Paper start + public java.util.@Nullable UUID spawningEntity; + public boolean shouldBurnInDay = true; +@@ -107,6 +108,25 @@ public class Phantom extends Mob implements Enemy { } -+ + // Purpur end - Ridables + ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms + @Override -+ protected void dropFromLootTable(DamageSource damageSource, boolean causedByPlayer) { ++ protected void dropFromLootTable(ServerLevel world, DamageSource damageSource, boolean causedByPlayer) { + boolean dropped = false; + if (lastHurtByPlayer == null && damageSource.getEntity() instanceof net.minecraft.world.entity.boss.enderdragon.EndCrystal) { + if (random.nextInt(5) < 1) { -+ dropped = spawnAtLocation(new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.PHANTOM_MEMBRANE)) != null; ++ dropped = spawnAtLocation(world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.PHANTOM_MEMBRANE)) != null; + } + } + if (!dropped) { -+ super.dropFromLootTable(damageSource, causedByPlayer); ++ super.dropFromLootTable(world, damageSource, causedByPlayer); + } + } + + public boolean isCirclingCrystal() { + return crystalPosition != null; + } - // Purpur end - ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms ++ @Override -@@ -130,11 +148,17 @@ public class Phantom extends FlyingMob implements Enemy { - + public boolean isFlapping() { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +@@ -120,9 +140,15 @@ public class Phantom extends Mob implements Enemy { @Override protected void registerGoals() { -- this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.goalSelector.addGoal(1, new Phantom.PhantomAttackStrategyGoal()); - this.goalSelector.addGoal(2, new Phantom.PhantomSweepAttackGoal()); - this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal()); -- this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur -+ // Purpur start -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms + if (level().purpurConfig.phantomOrbitCrystalRadius > 0) { -+ this.goalSelector.addGoal(1, new FindCrystalGoal(this)); -+ this.goalSelector.addGoal(2, new OrbitCrystalGoal(this)); ++ this.goalSelector.addGoal(1, new PhantomFindCrystalGoal(this)); ++ this.goalSelector.addGoal(2, new PhantomOrbitCrystalGoal(this)); + } + this.goalSelector.addGoal(3, new Phantom.PhantomAttackStrategyGoal()); + this.goalSelector.addGoal(4, new Phantom.PhantomSweepAttackGoal()); + this.goalSelector.addGoal(5, new Phantom.PhantomCircleAroundAnchorGoal()); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); -+ // Purpur end ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms + this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal()); } - -@@ -348,6 +372,124 @@ public class Phantom extends FlyingMob implements Enemy { - private AttackPhase() {} +@@ -506,6 +532,124 @@ public class Phantom extends Mob implements Enemy { + } } -+ // Purpur start -+ class FindCrystalGoal extends Goal { ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms ++ class PhantomFindCrystalGoal extends Goal { + private final Phantom phantom; + private net.minecraft.world.entity.boss.enderdragon.EndCrystal crystal; + private Comparator comparator; + -+ FindCrystalGoal(Phantom phantom) { ++ PhantomFindCrystalGoal(Phantom phantom) { + this.phantom = phantom; + this.comparator = Comparator.comparingDouble(phantom::distanceToSqr); + this.setFlags(EnumSet.of(Flag.LOOK)); @@ -189,14 +186,14 @@ index 961300cb8bcc7b0aff476a435aa33e713bd520a6..86df67578334a4743909c748213c2e1e + } + } + -+ class OrbitCrystalGoal extends Goal { ++ class PhantomOrbitCrystalGoal extends Goal { + private final Phantom phantom; + private float offset; + private float radius; + private float verticalChange; + private float direction; + -+ OrbitCrystalGoal(Phantom phantom) { ++ PhantomOrbitCrystalGoal(Phantom phantom) { + this.phantom = phantom; + this.setFlags(EnumSet.of(Flag.MOVE)); + } @@ -251,32 +248,8 @@ index 961300cb8bcc7b0aff476a435aa33e713bd520a6..86df67578334a4743909c748213c2e1e + this.radius * Mth.sin(this.offset)); + } + } -+ // Purpur end ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms + - private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - + class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables private float speed = 0.1F; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c74027c71eecf76512d5737b496d98a6cffb5eab..49af5d8feae2b267a12273a91ee637c7a6e7021e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1069,6 +1069,9 @@ public class PurpurWorldConfig { - public String phantomAttackDamage = "6 + size"; - public Map phantomMaxHealthCache = new HashMap<>(); - public Map phantomAttackDamageCache = new HashMap<>(); -+ public double phantomAttackedByCrystalRadius = 0.0D; -+ public float phantomAttackedByCrystalDamage = 1.0F; -+ public double phantomOrbitCrystalRadius = 0.0D; - private void phantomSettings() { - phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); - phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); -@@ -1090,6 +1093,9 @@ public class PurpurWorldConfig { - phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage); - phantomMaxHealthCache.clear(); - phantomAttackDamageCache.clear(); -+ phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius); -+ phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage); -+ phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius); - } - public boolean pigRidable = false; diff --git a/purpur-server/minecraft-patches/features/0011-Phantoms-burn-in-light.patch b/purpur-server/minecraft-patches/features/0011-Phantoms-burn-in-light.patch new file mode 100644 index 000000000..0e9dd06e4 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0011-Phantoms-burn-in-light.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: draycia +Date: Sun, 12 Apr 2020 20:41:59 -0700 +Subject: [PATCH] Phantoms burn in light + + +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 7077beafe2251fcc4e37578091bfaa02714268b9..6e8fe1b694ade45ffbf0b9bb39b954deffeb2402 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -54,6 +54,7 @@ public class Phantom extends Mob implements Enemy { + public java.util.@Nullable UUID spawningEntity; + public boolean shouldBurnInDay = true; + // Paper end ++ private static final net.minecraft.world.item.crafting.Ingredient TORCH = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.item.Items.TORCH, net.minecraft.world.item.Items.SOUL_TORCH); // Purpur - Phantoms burn in light + + public Phantom(EntityType type, Level level) { + super(type, level); +@@ -238,7 +239,11 @@ public class Phantom extends Mob implements Enemy { + // Paper start + @Override + public boolean isSunBurnTick() { +- return this.shouldBurnInDay && super.isSunBurnTick(); ++ // Purpur start - API for any mob to burn daylight ++ boolean burnFromDaylight = this.shouldBurnInDay && super.isSunBurnTick() && this.level().purpurConfig.phantomBurnInDaylight; ++ boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; ++ return burnFromDaylight || burnFromLightSource; ++ // Purpur end - API for any mob to burn daylight + } + // Paper end + +@@ -366,6 +371,7 @@ public class Phantom extends Mob implements Enemy { + List nearbyPlayers = serverLevel.getNearbyPlayers( + this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0, 64.0, 16.0) + ); ++ if (level().purpurConfig.phantomIgnorePlayersWithTorch) nearbyPlayers.removeIf(human -> TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(human.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND))); // Purpur - Phantoms burn in light + if (!nearbyPlayers.isEmpty()) { + nearbyPlayers.sort(Comparator.comparing(Entity::getY).reversed()); + +@@ -736,6 +742,12 @@ public class Phantom extends Mob implements Enemy { + return false; + } else if (!target.isAlive()) { + return false; ++ // Purpur start - Phantoms burn in light ++ } else if (level().purpurConfig.phantomBurnInLight > 0 && level().getLightEmission(new BlockPos(Phantom.this)) >= level().purpurConfig.phantomBurnInLight) { ++ return false; ++ } else if (level().purpurConfig.phantomIgnorePlayersWithTorch && (TORCH.test(target.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(target.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)))) { ++ return false; ++ // Purpur end - Phantoms burn in light + } else if (target instanceof Player player && (target.isSpectator() || player.isCreative())) { + return false; + } else if (!this.canUse()) { diff --git a/purpur-server/minecraft-patches/features/0012-Make-entity-breeding-times-configurable.patch b/purpur-server/minecraft-patches/features/0012-Make-entity-breeding-times-configurable.patch new file mode 100644 index 000000000..2e0ef55a0 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0012-Make-entity-breeding-times-configurable.patch @@ -0,0 +1,668 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Sun, 15 Nov 2020 02:18:15 -0800 +Subject: [PATCH] Make entity breeding times configurable + + +diff --git a/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java b/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java +index 35aec6c3aa2f09f13954fda67902b3c975f566e3..fff139abc9341f3ab2dac568a922235dc5e81b3e 100644 +--- a/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java ++++ b/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java +@@ -118,8 +118,10 @@ public class VillagerMakeLove extends Behavior { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(breedOffspring, parent, partner, null, null, 0).isCancelled()) { + return Optional.empty(); + } +- parent.setAge(6000); +- partner.setAge(6000); ++ // Purpur start - Make entity breeding times configurable ++ parent.setAge(level.purpurConfig.villagerBreedingTicks); ++ partner.setAge(level.purpurConfig.villagerBreedingTicks); ++ // Purpur end - Make entity breeding times configurable + level.addFreshEntityWithPassengers(breedOffspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); + // CraftBukkit end - call EntityBreedEvent + level.broadcastEntityEvent(breedOffspring, EntityEvent.LOVE_HEARTS); +diff --git a/net/minecraft/world/entity/animal/Animal.java b/net/minecraft/world/entity/animal/Animal.java +index 2e7e7c1913f5cbc20ce116c5ae3e185fc83094c0..53d04c412d36015a30ae2680f61651db6acbf2f2 100644 +--- a/net/minecraft/world/entity/animal/Animal.java ++++ b/net/minecraft/world/entity/animal/Animal.java +@@ -46,6 +46,7 @@ public abstract class Animal extends AgeableMob { + public int inLove = 0; + public @Nullable EntityReference loveCause; + public @Nullable ItemStack breedItem; // CraftBukkit - Add breedItem variable ++ public abstract int getPurpurBreedTime(); // Purpur - Make entity breeding times configurable + + protected Animal(EntityType type, Level level) { + super(type, level); +@@ -271,8 +272,10 @@ public abstract class Animal extends AgeableMob { + player.awardStat(Stats.ANIMALS_BRED); + CriteriaTriggers.BRED_ANIMALS.trigger(player, this, animal, baby); + } // Paper - Call EntityBreedEvent +- this.setAge(6000); +- animal.setAge(6000); ++ // Purpur start - Make entity breeding times configurable ++ this.setAge(this.getPurpurBreedTime()); ++ animal.setAge(animal.getPurpurBreedTime()); ++ // Purpur end - Make entity breeding times configurable + this.resetLove(); + animal.resetLove(); + level.broadcastEntityEvent(this, EntityEvent.IN_LOVE_HEARTS); +diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +index 1f6b28531127ea2e5b291583f6bb6a236868fbf0..8ae1f8471f35028c1c469dc9cb9eac9b564a5c5e 100644 +--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -107,6 +107,13 @@ public class Armadillo extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.armadilloBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 3f9e15685ba52a5b9bd4282ba6de6751296975bf..79dfebbd72424d5a3a13d4ba89a555fdf782e703 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -146,6 +146,13 @@ public class Axolotl extends Animal implements Bucketable { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.axolotlBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return 0.0F; +diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java +index 7d7ab4c5092ac085e8cd6d3a432feb683db5a282..473aa29e3075fcab44a14c2bc14d3f222b55cbad 100644 +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -485,6 +485,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.beeBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public long getPersistentAngerEndTime() { + return this.entityData.get(DATA_ANGER_END_TIME); +diff --git a/net/minecraft/world/entity/animal/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 95e86330e1e973c43e50bd4ac310212b8a057430..842b20242c323572d4c04d3a2d5fe21a54d53ed4 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -97,6 +97,13 @@ public class Camel extends AbstractHorse { + } + // Purpur end - Ridables + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.camelBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index 52a0bf792337e2f1cf11e215b033caae21ee774b..029fe4ac952d9f56824de346d98b341bd0b9b65f 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -103,6 +103,13 @@ public class Chicken extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.chickenBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index 5e5b239ebc774ae66f8c35a725ea917993239ef2..400b0a0a99590e3590cb66724acdc5ba9170f2c4 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -54,6 +54,13 @@ public class Cow extends AbstractCow { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.cowBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java +index 85ad44eb3e64232cf1ea326257078c46288a613a..e2aa027dc7a39fa8a8868a7d153e838113fcf97b 100644 +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -85,6 +85,13 @@ public class MushroomCow extends AbstractCow implements Shearable { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.mooshroomBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); +diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java +index b85d967c7c809683e4576be30ed855941c6e68cc..d74a5cc2a43ef41fd00676bbd17fe4df9edb67ff 100644 +--- a/net/minecraft/world/entity/animal/equine/Donkey.java ++++ b/net/minecraft/world/entity/animal/equine/Donkey.java +@@ -40,6 +40,13 @@ public class Donkey extends AbstractChestedHorse { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.donkeyBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java +index 9a97cd588fb2dc0f393b2c8768f4e45066a34850..ba9df659ed019f9e58991059fe99c1471bfc94d8 100644 +--- a/net/minecraft/world/entity/animal/equine/Horse.java ++++ b/net/minecraft/world/entity/animal/equine/Horse.java +@@ -74,6 +74,13 @@ public class Horse extends AbstractHorse { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.horseBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void randomizeAttributes(RandomSource random) { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt)); +diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java +index b54a535add643390a6164ab3f4a2fdd475d60162..960e48f5ae63d8c86758d2573510bf481ca4bca0 100644 +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -152,6 +152,13 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.llamaBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public boolean isTraderLlama() { + return false; + } +diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java +index f49c8a61ac3883f7ff8ad8193ae9ea9136f54c45..180a807026437e949e3f7c3d59864ea328f6f03f 100644 +--- a/net/minecraft/world/entity/animal/equine/Mule.java ++++ b/net/minecraft/world/entity/animal/equine/Mule.java +@@ -39,6 +39,13 @@ public class Mule extends AbstractChestedHorse { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.muleBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.MULE_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +index d8b7b787b98b86d16578abe1cd19321ad2c176c0..af0e326f5ff8fcb82353bac3143103928a6a08e6 100644 +--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +@@ -67,6 +67,13 @@ public class SkeletonHorse extends AbstractHorse { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return 6000; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java +index deb0a1eb867daec57a644ce698fe50d79fd8960e..65ac46335d642d5dd8c01623721257a4fd53441d 100644 +--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java ++++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java +@@ -70,6 +70,13 @@ public class TraderLlama extends Llama { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.traderLlamaBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public boolean isTraderLlama() { + return true; +diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +index 8b8580f8c082a40f958ec61adf32dc7d1485ea68..fa2f18470123f98e336ab5c94bc791e9333356cb 100644 +--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java ++++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +@@ -81,6 +81,13 @@ public class ZombieHorse extends AbstractHorse { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return 6000; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0); + } +diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java +index b7c17aed96dce39cb03780ff251e0e38c3c6adb1..d0aa698ef99cf3b1926018314b4067cc1cd8ac2a 100644 +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -128,6 +128,13 @@ public class Cat extends TamableAnimal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.catBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void registerGoals() { + this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java +index c9a8dcfd1e46f97dee0393db3205049c0db1cefb..bf281fd4c050b87fd277ab68e812ab2dcd3d06aa 100644 +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -91,6 +91,13 @@ public class Ocelot extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.ocelotBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public boolean isTrusting() { + return this.entityData.get(DATA_TRUSTING); + } +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index 6819bcb15ad6c85f41a098a9fdb73ce5a7987d8f..3c2f5e711c013a6f51093e94bc4b5518263607a2 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -197,6 +197,13 @@ public class Fox extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.foxBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +@@ -997,8 +1004,10 @@ public class Fox extends Animal { + CriteriaTriggers.BRED_ANIMALS.trigger(serverPlayer, this.animal, this.partner, fox); + } + +- this.animal.setAge(6000); +- this.partner.setAge(6000); ++ // Purpur start - Make entity breeding times configurable ++ this.animal.setAge(this.animal.getPurpurBreedTime()); ++ this.partner.setAge(this.partner.getPurpurBreedTime()); ++ // Purpur end - Make entity breeding times configurable + this.animal.resetLove(); + this.partner.resetLove(); + this.level.addFreshEntityWithPassengers(fox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index 5b4bb6fd100abc569c5da167735a220014bf5d8c..7d6dbdef68b25e774b5a83bb8aeb2535246459aa 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -164,6 +164,13 @@ public class Frog extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.frogBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index 6e4d611c959960e8d13f79704c176b41ea5c12c4..07ba57ae845024e0f2fa03a68e126ec79bfc6b15 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -132,6 +132,13 @@ public class Goat extends Animal { + } + // Purpur end - Ridables + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.goatBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +index cd888e070a72ff46d35a4425a0013c617b31e159..30204224ed83f6d3cfd64640b332b23cb74d379f 100644 +--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java ++++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +@@ -146,6 +146,13 @@ public class HappyGhast extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return 6000; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void ageBoundaryReached() { + if (this.isBaby()) { +diff --git a/net/minecraft/world/entity/animal/nautilus/Nautilus.java b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +index a32ac3907883f65c2a00173128a4a011fd552932..98199869b70c7c0f2b744ac74961c77d473617ca 100644 +--- a/net/minecraft/world/entity/animal/nautilus/Nautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +@@ -32,6 +32,13 @@ public class Nautilus extends AbstractNautilus { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.nautilusBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected Brain.Provider brainProvider() { + return NautilusAi.brainProvider(); +diff --git a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +index 7ef4a15ba2b315a41480484572a6c6898340466b..94383c5020b6631203ddc4e0a58a222729ffe9a2 100644 +--- a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +@@ -54,6 +54,13 @@ public class ZombieNautilus extends AbstractNautilus { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return 6000; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public static AttributeSupplier.Builder createAttributes() { + return AbstractNautilus.createAttributes().add(Attributes.MOVEMENT_SPEED, 1.1F); + } +diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java +index d5060fb8e9a711e6230f2c4950521d8b4f5c01d2..163fe697f57459b36885fa3a18f41370347cab38 100644 +--- a/net/minecraft/world/entity/animal/panda/Panda.java ++++ b/net/minecraft/world/entity/animal/panda/Panda.java +@@ -143,6 +143,13 @@ public class Panda extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.pandaBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) { + return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot(); +diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java +index 7480a491533a47882eaf4b36c320adf45ebfb190..0d62046cb33ed750ab27229fe4f0b43c3a1dd5f4 100644 +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -207,6 +207,13 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return 6000; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java +index aeb9e57db2233bff20fa5208a679ac9bcb8d5026..0cb09a058c2d21b10677482792b230464ed9f951 100644 +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -88,6 +88,13 @@ public class Pig extends Animal implements ItemSteerable { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.pigBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +index 904e4c641f1892220f263528c65f6f81708399cd..97edd950e2142e73ad947590c99d6d5529cbd44c 100644 +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -126,6 +126,13 @@ public class PolarBear extends Animal implements NeutralMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.polarBearBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index cfbab57dd0527c5e2f17718f3974059eb881c2ea..061418a25d359574c4ff44327082b08b4094ee1c 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -156,6 +156,13 @@ public class Rabbit extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.rabbitBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java +index 1d60d35c1330418009f7d1d0b60d263559b68b7f..0e4505474a50d57338c16648cb6049d35455fdc6 100644 +--- a/net/minecraft/world/entity/animal/sheep/Sheep.java ++++ b/net/minecraft/world/entity/animal/sheep/Sheep.java +@@ -88,6 +88,13 @@ public class Sheep extends Animal implements Shearable { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.sheepBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void registerGoals() { + this.eatBlockGoal = new EatBlockGoal(this); +diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +index eb6675394ecc5bba67e0f8bb0220ad92ef2b5e4f..7ef3c94f63c8a25d09e69b818ecdf79795803570 100644 +--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java ++++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +@@ -114,6 +114,13 @@ public class Sniffer extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.snifferBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java +index 8d9a1fe887c39588e43e4ccfc46151a033c8ccb7..fce1b1a22e091a1c05650c72e1ceb83e1a1d1ed1 100644 +--- a/net/minecraft/world/entity/animal/turtle/Turtle.java ++++ b/net/minecraft/world/entity/animal/turtle/Turtle.java +@@ -112,6 +112,13 @@ public class Turtle extends Animal { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.turtleBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public void setHomePos(BlockPos homePos) { + this.homePos = homePos; + } +@@ -343,8 +350,10 @@ public class Turtle extends Animal { + } + + this.turtle.setHasEgg(true); +- this.animal.setAge(6000); +- this.partner.setAge(6000); ++ // Purpur start - Make entity breeding times configurable ++ this.animal.setAge(this.animal.getPurpurBreedTime()); ++ this.partner.setAge(this.partner.getPurpurBreedTime()); ++ // Purpur end - Make entity breeding times configurable + this.animal.resetLove(); + this.partner.resetLove(); + RandomSource random = this.animal.getRandom(); +diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java +index 7bccee8b5dd689bbff18f34d3afac52bfe34aa42..08a1bafa0e45dbdbf8bdc4d5cb654d080590707d 100644 +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -216,6 +216,13 @@ public class Wolf extends TamableAnimal implements NeutralMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.wolfBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index 3308e954c8f6deff89c6df0af01f7774e36b0385..ad6515c2dcde0079095c6777f56319e9901850f8 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -117,6 +117,13 @@ public class Strider extends Animal implements ItemSteerable { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.striderBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + public static boolean checkStriderSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index baf81e1919e64af9d6da0a49b19e5f34cf962a79..4498fe26dff2cdec8e90d6559a66ebe7859195bb 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -122,6 +122,13 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.hoglinBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @VisibleForTesting + public void setTimeInOverworld(int timeInOverworld) { + this.timeInOverworld = timeInOverworld; diff --git a/purpur-server/minecraft-patches/features/0013-Apply-display-names-from-item-forms-of-entities-to-e.patch b/purpur-server/minecraft-patches/features/0013-Apply-display-names-from-item-forms-of-entities-to-e.patch new file mode 100644 index 000000000..6095f7e7c --- /dev/null +++ b/purpur-server/minecraft-patches/features/0013-Apply-display-names-from-item-forms-of-entities-to-e.patch @@ -0,0 +1,112 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Tue, 17 Nov 2020 03:23:48 -0800 +Subject: [PATCH] Apply display names from item forms of entities to entities + and vice versa + + +diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java +index 959037a01841de06767522512b92e96be7c72b56..c7be4713a526171d2c4b9642d28f1a246e294ff7 100644 +--- a/net/minecraft/world/entity/decoration/ArmorStand.java ++++ b/net/minecraft/world/entity/decoration/ArmorStand.java +@@ -451,6 +451,7 @@ public class ArmorStand extends LivingEntity { + + private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(ServerLevel level, DamageSource damageSource) { // Paper + ItemStack itemStack = new ItemStack(Items.ARMOR_STAND); ++ if (level.purpurConfig.persistentDroppableEntityDisplayNames) // Purpur - Apply display names from item forms of entities to entities and vice versa + itemStack.set(DataComponents.CUSTOM_NAME, this.getCustomName()); + this.drops.add(new DefaultDrop(itemStack, stack -> Block.popResource(this.level(), this.blockPosition(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior + return this.brokenByAnything(level, damageSource); // Paper +diff --git a/net/minecraft/world/entity/decoration/ItemFrame.java b/net/minecraft/world/entity/decoration/ItemFrame.java +index 5f518a7469953f50135ec53d7c6b77b9acbafed9..d5fec174e6accbf93fd70ed1ba64135fb879d0b2 100644 +--- a/net/minecraft/world/entity/decoration/ItemFrame.java ++++ b/net/minecraft/world/entity/decoration/ItemFrame.java +@@ -246,7 +246,11 @@ public class ItemFrame extends HangingEntity { + this.removeFramedMap(item); + } else { + if (dropItem) { +- this.spawnAtLocation(level, this.getFrameItemStack()); ++ // Purpur start - Apply display names from item forms of entities to entities and vice versa ++ final ItemStack itemFrame = this.getFrameItemStack(); ++ if (!level.purpurConfig.persistentDroppableEntityDisplayNames) itemFrame.set(DataComponents.CUSTOM_NAME, null); ++ this.spawnAtLocation(level, itemFrame); ++ // Purpur end - Apply display names from item forms of entities to entities and vice versa + } + + if (!item.isEmpty()) { +diff --git a/net/minecraft/world/entity/decoration/painting/Painting.java b/net/minecraft/world/entity/decoration/painting/Painting.java +index 682d6e8c400e3343815be83d74dafe7f16477ba2..32b7bfec3d169166fb607b68e4abf484756327ee 100644 +--- a/net/minecraft/world/entity/decoration/painting/Painting.java ++++ b/net/minecraft/world/entity/decoration/painting/Painting.java +@@ -182,7 +182,11 @@ public class Painting extends HangingEntity { + if (level.getGameRules().get(GameRules.ENTITY_DROPS)) { + this.playSound(SoundEvents.PAINTING_BREAK, 1.0F, 1.0F); + if (!(entity instanceof Player player && player.hasInfiniteMaterials())) { +- this.spawnAtLocation(level, Items.PAINTING); ++ // Purpur start - Apply display names from item forms of entities to entities and vice versa ++ final ItemStack painting = new ItemStack(Items.PAINTING); ++ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) painting.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, null); ++ this.spawnAtLocation(level, painting); ++ // Purpur end - Apply display names from item forms of entities to entities and vice versa + } + } + } +diff --git a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java +index d17269c9274bd29c761403138bfc56355c800d9c..e5854af03a58dc26a100feac59357862e46bfeed 100644 +--- a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java ++++ b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java +@@ -818,7 +818,13 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { + + @Override + public final ItemStack getPickResult() { +- return new ItemStack(this.dropItem.get()); ++ // Purpur start - Apply display names from item forms of entities to entities and vice versa ++ final ItemStack boat = new ItemStack(this.dropItem.get()); ++ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) { ++ boat.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, null); ++ } ++ return boat; ++ // Purpur end - Apply display names from item forms of entities to entities and vice versa + } + + public static enum Status { +diff --git a/net/minecraft/world/item/ArmorStandItem.java b/net/minecraft/world/item/ArmorStandItem.java +index 962483d6f7225f13f121141882262d36dacad8cb..89d4bc00898fd8f6d40cda87c04c5983e2ea223c 100644 +--- a/net/minecraft/world/item/ArmorStandItem.java ++++ b/net/minecraft/world/item/ArmorStandItem.java +@@ -51,6 +51,10 @@ public class ArmorStandItem extends Item { + return InteractionResult.FAIL; + } + // CraftBukkit end ++ // Purpur start - Apply display names from item forms of entities to entities and vice versa ++ if (!serverLevel.purpurConfig.persistentDroppableEntityDisplayNames) armorStand.setCustomName(null); ++ if (serverLevel.purpurConfig.armorstandSetNameVisible && armorStand.getCustomName() != null) armorStand.setCustomNameVisible(true); ++ // Purpur end - Apply display names from item forms of entities to entities and vice versa + serverLevel.addFreshEntityWithPassengers(armorStand); + level.playSound( + null, armorStand.getX(), armorStand.getY(), armorStand.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F +diff --git a/net/minecraft/world/item/BoatItem.java b/net/minecraft/world/item/BoatItem.java +index a8732056d0086a0932e82f70396a8849bf8d0ee1..2219d5e3e3d1aaa9f729205191a1f549ed662e2e 100644 +--- a/net/minecraft/world/item/BoatItem.java ++++ b/net/minecraft/world/item/BoatItem.java +@@ -63,6 +63,7 @@ public class BoatItem extends Item { + return InteractionResult.FAIL; + } else { + boat.setYRot(player.getYRot()); ++ if (!level.purpurConfig.persistentDroppableEntityDisplayNames) boat.setCustomName(null); // Purpur - Apply display names from item forms of entities to entities and vice versa + if (!level.noCollision(boat, boat.getBoundingBox())) { + return InteractionResult.FAIL; + } else { +diff --git a/net/minecraft/world/item/HangingEntityItem.java b/net/minecraft/world/item/HangingEntityItem.java +index e9403c7c857bfaf09d8f8851b468aa4431c5be54..ecc890ae91475420215b61311ff959d9c522202d 100644 +--- a/net/minecraft/world/item/HangingEntityItem.java ++++ b/net/minecraft/world/item/HangingEntityItem.java +@@ -59,7 +59,7 @@ public class HangingEntityItem extends Item { + hangingEntity = new GlowItemFrame(level, blockPos, clickedFace); + } + +- EntityType.createDefaultStackConfig(level, itemInHand, player).accept(hangingEntity); ++ EntityType.appendDefaultStackConfig(entity -> {if (!level.purpurConfig.persistentDroppableEntityDisplayNames) entity.setCustomName(null);}, level, itemInHand, player).accept(hangingEntity); // Purpur - Apply display names from item forms of entities to entities and vice versa + if (hangingEntity.survives()) { + if (!level.isClientSide()) { + // CraftBukkit start - fire HangingPlaceEvent diff --git a/purpur-server/minecraft-patches/features/0014-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch b/purpur-server/minecraft-patches/features/0014-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch new file mode 100644 index 000000000..5376bc221 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0014-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch @@ -0,0 +1,171 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Sat, 5 Dec 2020 01:20:16 -0800 +Subject: [PATCH] Option for Villager Clerics to farm Nether Wart + +Adds an option so that Villagers with the Cleric profession are able to +farm Nether Wart. Reimplemented based on a feature of the carpet-extra +mod. + +diff --git a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +index 23c9363f03a74a2ddd15ffa1da4e0910dcfe2eb4..83aac5dcc51fbc2cc9ee45c155fb2b5b67293a96 100644 +--- a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java ++++ b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +@@ -31,6 +31,7 @@ public class HarvestFarmland extends Behavior { + private long nextOkStartTime; + private int timeWorkedSoFar; + private final List validFarmlandAroundVillager = Lists.newArrayList(); ++ private boolean clericWartFarmer = false; // Purpur - Option for Villager Clerics to farm Nether Wart + + public HarvestFarmland() { + super( +@@ -49,9 +50,10 @@ public class HarvestFarmland extends Behavior { + protected boolean checkExtraStartConditions(ServerLevel level, Villager owner) { + if (!level.getGameRules().get(GameRules.MOB_GRIEFING)) { + return false; +- } else if (!owner.getVillagerData().profession().is(VillagerProfession.FARMER)) { ++ } else if (!owner.getVillagerData().profession().is(VillagerProfession.FARMER) && !(level.purpurConfig.villagerClericsFarmWarts && owner.getVillagerData().profession().is(VillagerProfession.CLERIC))) { // Purpur - Option for Villager Clerics to farm Nether Wart + return false; + } else { ++ if (!this.clericWartFarmer && owner.getVillagerData().profession().is(VillagerProfession.CLERIC)) this.clericWartFarmer = true; // Purpur - Option for Villager Clerics to farm Nether Wart + BlockPos.MutableBlockPos mutableBlockPos = owner.blockPosition().mutable(); + this.validFarmlandAroundVillager.clear(); + +@@ -81,6 +83,7 @@ public class HarvestFarmland extends Behavior { + BlockState blockState = level.getBlockState(pos); + Block block = blockState.getBlock(); + Block block1 = level.getBlockState(pos.below()).getBlock(); ++ if (this.clericWartFarmer) return block == net.minecraft.world.level.block.Blocks.NETHER_WART && blockState.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3 || blockState.isAir() && block1 == net.minecraft.world.level.block.Blocks.SOUL_SAND; // Purpur - Option for Villager Clerics to farm Nether Wart + return block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState) || blockState.isAir() && block1 instanceof FarmBlock; + } + +@@ -107,19 +110,19 @@ public class HarvestFarmland extends Behavior { + BlockState blockState = level.getBlockState(this.aboveFarmlandPos); + Block block = blockState.getBlock(); + Block block1 = level.getBlockState(this.aboveFarmlandPos.below()).getBlock(); +- if (block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState)) { ++ if (block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState) && !this.clericWartFarmer || this.clericWartFarmer && block == net.minecraft.world.level.block.Blocks.NETHER_WART && blockState.getValue(net.minecraft.world.level.block.NetherWartBlock.AGE) == 3) { // Purpur - Option for Villager Clerics to farm Nether Wart + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(owner, this.aboveFarmlandPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state + level.destroyBlock(this.aboveFarmlandPos, true, owner); + } // CraftBukkit + } + +- if (blockState.isAir() && block1 instanceof FarmBlock && owner.hasFarmSeeds()) { ++ if (blockState.isAir() && block1 instanceof FarmBlock && !this.clericWartFarmer || this.clericWartFarmer && block1 == net.minecraft.world.level.block.Blocks.SOUL_SAND && owner.hasFarmSeeds()) { // Purpur - Option for Villager Clerics to farm Nether Wart + SimpleContainer inventory = owner.getInventory(); + + for (int i = 0; i < inventory.getContainerSize(); i++) { + ItemStack item = inventory.getItem(i); + boolean flag = false; +- if (!item.isEmpty() && item.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) && item.getItem() instanceof BlockItem blockItem) { ++ if (!item.isEmpty() && (item.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) || this.clericWartFarmer && item.getItem() == net.minecraft.world.item.Items.NETHER_WART) && item.getItem() instanceof BlockItem blockItem) { // Purpur - Option for Villager Clerics to farm Nether Wart + BlockState blockState1 = blockItem.getBlock().defaultBlockState(); + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(owner, this.aboveFarmlandPos, blockState1)) { // CraftBukkit + level.setBlockAndUpdate(this.aboveFarmlandPos, blockState1); +@@ -134,7 +137,7 @@ public class HarvestFarmland extends Behavior { + this.aboveFarmlandPos.getX(), + this.aboveFarmlandPos.getY(), + this.aboveFarmlandPos.getZ(), +- SoundEvents.CROP_PLANTED, ++ this.clericWartFarmer ? SoundEvents.NETHER_WART_PLANTED : SoundEvents.CROP_PLANTED, // Purpur - Option for Villager Clerics to farm Nether Wart + SoundSource.BLOCKS, + 1.0F, + 1.0F +diff --git a/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java b/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java +index 256fb863683d66235db4fb2b65fb1495672a67bf..81145178ba1ef75c441a09ab5f376d0528299ad3 100644 +--- a/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java ++++ b/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java +@@ -59,6 +59,12 @@ public class TradeWithVillager extends Behavior { + throwHalfStack(owner, ImmutableSet.of(Items.WHEAT), villager); + } + ++ // Purpur start - Option for Villager Clerics to farm Nether Wart ++ if (level.purpurConfig.villagerClericsFarmWarts && level.purpurConfig.villagerClericFarmersThrowWarts && owner.getVillagerData().profession().is(VillagerProfession.CLERIC) && owner.getInventory().countItem(Items.NETHER_WART) > Items.NETHER_WART.getDefaultMaxStackSize() / 2) { ++ throwHalfStack(owner, ImmutableSet.of(Items.NETHER_WART), villager); ++ } ++ // Purpur end - Option for Villager Clerics to farm Nether Wart ++ + if (!this.trades.isEmpty() && owner.getInventory().hasAnyOf(this.trades)) { + throwHalfStack(owner, this.trades, villager); + } +diff --git a/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java b/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java +index bb3ac5297860c8af6e213d10fdf5144086e9474b..0e72601781235119d62fe74420153b98edb5ce23 100644 +--- a/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java ++++ b/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java +@@ -79,8 +79,13 @@ public class VillagerGoalPackages { + public static ImmutableList>> getWorkPackage( + Holder profession, float speedModifier + ) { ++ // Purpur start - Option for Villager Clerics to farm Nether Wart ++ return getWorkPackage(profession, speedModifier, false); ++ } ++ public static ImmutableList>> getWorkPackage(Holder profession, float speedModifier, boolean clericsFarmWarts) { ++ // Purpur end - Option for Villager Clerics to farm Nether Wart + WorkAtPoi workAtPoi; +- if (profession.is(VillagerProfession.FARMER)) { ++ if (profession.is(VillagerProfession.FARMER) || (clericsFarmWarts && profession.is(VillagerProfession.CLERIC))) { // Purpur - Option for Villager Clerics to farm Nether Wart + workAtPoi = new WorkAtComposter(); + } else { + workAtPoi = new WorkAtPoi(); +diff --git a/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java +index 39a9e9a6f6dfddbf47a7f96150ef832efe61f89e..c268817b170bd54898bb5f8c2e2e12c0faa7eb1e 100644 +--- a/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java +@@ -22,6 +22,13 @@ public class SecondaryPoiSensor extends Sensor { + + @Override + protected void doTick(ServerLevel level, Villager entity) { ++ // Purpur start - Option for Villager Clerics to farm Nether Wart - make sure clerics don't wander to soul sand when the option is off ++ Brain brain = entity.getBrain(); ++ if (!level.purpurConfig.villagerClericsFarmWarts && entity.getVillagerData().profession().is(net.minecraft.world.entity.npc.villager.VillagerProfession.CLERIC)) { ++ brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); ++ return; ++ } ++ // Purpur end - Option for Villager Clerics to farm Nether Wart + ResourceKey resourceKey = level.dimension(); + BlockPos blockPos = entity.blockPosition(); + List list = Lists.newArrayList(); +@@ -38,7 +45,7 @@ public class SecondaryPoiSensor extends Sensor { + } + } + +- Brain brain = entity.getBrain(); ++ //Brain brain = entity.getBrain(); // Purpur - Option for Villager Clerics to farm Nether Wart - moved up + if (!list.isEmpty()) { + brain.setMemory(MemoryModuleType.SECONDARY_JOB_SITE, list); + } else { +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index 02836bd2e8fda563877d3014ab839734aebc7457..ed6642023fccbc8f825d7c5c206d73ffd666f0f9 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -315,7 +315,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + villagerBrain.setSchedule(EnvironmentAttributes.VILLAGER_ACTIVITY); + villagerBrain.addActivityWithConditions( + Activity.WORK, +- VillagerGoalPackages.getWorkPackage(holder, 0.5F), ++ VillagerGoalPackages.getWorkPackage(holder, 0.5F, this.level().purpurConfig.villagerClericsFarmWarts), // Purpur - Option for Villager Clerics to farm Nether Wart + ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT)) + ); + } +@@ -956,7 +956,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + + public boolean hasFarmSeeds() { +- return this.getInventory().hasAnyMatching(stack -> stack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)); ++ return this.getInventory().hasAnyMatching(stack -> this.level().purpurConfig.villagerClericsFarmWarts && this.getVillagerData().profession().is(VillagerProfession.CLERIC) ? stack.is(Items.NETHER_WART) : stack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)); // Purpur - Option for Villager Clerics to farm Nether Wart + } + + @Override +diff --git a/net/minecraft/world/entity/npc/villager/VillagerProfession.java b/net/minecraft/world/entity/npc/villager/VillagerProfession.java +index 0acfa8f9d2eab00cf0d8b4bc65c3af52046272bd..8f2edbe693a793d31295d035bc01e4c2b7742b4e 100644 +--- a/net/minecraft/world/entity/npc/villager/VillagerProfession.java ++++ b/net/minecraft/world/entity/npc/villager/VillagerProfession.java +@@ -103,7 +103,7 @@ public record VillagerProfession( + register(registry, ARMORER, PoiTypes.ARMORER, SoundEvents.VILLAGER_WORK_ARMORER); + register(registry, BUTCHER, PoiTypes.BUTCHER, SoundEvents.VILLAGER_WORK_BUTCHER); + register(registry, CARTOGRAPHER, PoiTypes.CARTOGRAPHER, SoundEvents.VILLAGER_WORK_CARTOGRAPHER); +- register(registry, CLERIC, PoiTypes.CLERIC, SoundEvents.VILLAGER_WORK_CLERIC); ++ register(registry, CLERIC, PoiTypes.CLERIC, ImmutableSet.of(Items.NETHER_WART), ImmutableSet.of(Blocks.SOUL_SAND), SoundEvents.VILLAGER_WORK_CLERIC); // Purpur - Option for Villager Clerics to farm Nether Wart + register( + registry, + FARMER, diff --git a/purpur-server/minecraft-patches/features/0015-Add-mobGriefing-override-to-everything-affected.patch b/purpur-server/minecraft-patches/features/0015-Add-mobGriefing-override-to-everything-affected.patch new file mode 100644 index 000000000..97f514e50 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0015-Add-mobGriefing-override-to-everything-affected.patch @@ -0,0 +1,378 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Encode42 +Date: Tue, 5 Jan 2021 22:21:56 -0500 +Subject: [PATCH] Add mobGriefing override to everything affected + + +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index e04320155173caa8ce8f998477f36a6b36de935c..34d5283bbc76b4b6c335de354fc2edc1046dcfd1 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -1961,7 +1961,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + if (this.level() instanceof ServerLevel serverLevel) { + boolean var6 = false; + if (this.dead && entitySource instanceof WitherBoss) { // Paper +- if (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.witherMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + BlockPos blockPos = this.blockPosition(); + BlockState blockState = Blocks.WITHER_ROSE.defaultBlockState(); + if (this.level().getBlockState(blockPos).isAir() && blockState.canSurvive(this.level(), blockPos)) { +diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java +index a3569262099dce237c3feb5e29d24bf47f4c1e38..70807b4a6c94b0231eb541c7121d925d88022ac6 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -554,7 +554,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + && this.canPickUpLoot() + && this.isAlive() + && !this.dead +- && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.entitiesPickUpLootMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + Vec3i pickupReach = this.getPickupReach(); + + for (ItemEntity itemEntity : this.level() +diff --git a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +index 83aac5dcc51fbc2cc9ee45c155fb2b5b67293a96..c68399986583a215da156b9057a61f22cbf6143d 100644 +--- a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java ++++ b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +@@ -48,7 +48,7 @@ public class HarvestFarmland extends Behavior { + + @Override + protected boolean checkExtraStartConditions(ServerLevel level, Villager owner) { +- if (!level.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (!level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.villagerMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + return false; + } else if (!owner.getVillagerData().profession().is(VillagerProfession.FARMER) && !(level.purpurConfig.villagerClericsFarmWarts && owner.getVillagerData().profession().is(VillagerProfession.CLERIC))) { // Purpur - Option for Villager Clerics to farm Nether Wart + return false; +diff --git a/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java b/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java +index 3da917a428b9300d839df610e0e9dceb1d0f0668..a43c0d99458e8aff6a0b0a9eee60ed82b75ffdda 100644 +--- a/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java ++++ b/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java +@@ -31,7 +31,7 @@ public class BreakDoorGoal extends DoorInteractGoal { + @Override + public boolean canUse() { + return super.canUse() +- && getServerLevel(this.mob).getGameRules().get(GameRules.MOB_GRIEFING) ++ && getServerLevel(this.mob).getGameRules().get(GameRules.MOB_GRIEFING, this.mob.level().purpurConfig.zombieMobGriefingOverride) // Purpur - Add mobGriefing override to everything affected + && this.isValidDifficulty(this.mob.level().getDifficulty()) + && !this.isOpen(); + } +diff --git a/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +index 087861886eeb55324ffe54bda78e19f83bf33e72..0f0311eb836b8da39651ece499fe4bda44bce178 100644 +--- a/net/minecraft/world/entity/ai/goal/EatBlockGoal.java ++++ b/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +@@ -64,7 +64,7 @@ public class EatBlockGoal extends Goal { + BlockPos blockPos = this.mob.blockPosition(); + final BlockState blockState = this.level.getBlockState(blockPos); // Paper - fix wrong block state + if (IS_EDIBLE.test(blockState)) { // Paper - fix wrong block state +- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - fix wrong block state ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(this.level).purpurConfig.sheepMobGriefingOverride))) { // CraftBukkit // Paper - fix wrong block state // Purpur - Add mobGriefing override to everything affected + this.level.destroyBlock(blockPos, false); + } + +@@ -72,7 +72,7 @@ public class EatBlockGoal extends Goal { + } else { + BlockPos blockPos1 = blockPos.below(); + if (this.level.getBlockState(blockPos1).is(Blocks.GRASS_BLOCK)) { +- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - Fix wrong block state ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(this.level).purpurConfig.sheepMobGriefingOverride))) { // CraftBukkit // Paper - Fix wrong block state // Purpur - Add mobGriefing override to everything affected + this.level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); + this.level.setBlock(blockPos1, Blocks.DIRT.defaultBlockState(), Block.UPDATE_CLIENTS); + } +diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +index 50cc4c462640a9d40abef6245439d71e8c593ae6..83be00350765db043b6bf32e7814d9c7119eacc5 100644 +--- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java ++++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +@@ -35,7 +35,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal { + + @Override + public boolean canUse() { +- if (!getServerLevel(this.removerMob).getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (!getServerLevel(this.removerMob).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(this.removerMob).purpurConfig.zombieMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + return false; + } else if (this.nextStartTick > 0) { + this.nextStartTick--; +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index 3c2f5e711c013a6f51093e94bc4b5518263607a2..a0ed30e8ccd41729de8e0ff977a61fa607386640 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -1062,7 +1062,7 @@ public class Fox extends Animal { + } + + protected void onReachedTarget() { +- if (getServerLevel(Fox.this.level()).getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (getServerLevel(Fox.this.level()).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(Fox.this.level()).purpurConfig.foxMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + BlockState blockState = Fox.this.level().getBlockState(this.blockPos); + if (blockState.is(Blocks.SWEET_BERRY_BUSH)) { + this.pickSweetBerries(blockState); +diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java +index ab44bc401438d589696d9f25ebaca0fc39648bed..f011369a67809d54230984a22a909bfea44f47f3 100644 +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -135,7 +135,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + this.hurtServer(serverLevel, this.damageSources().onFire().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.MELTING), 1.0F); // CraftBukkit + } + +- if (!serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (!serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.snowGolemMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + return; + } + +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index 061418a25d359574c4ff44327082b08b4094ee1c..66fda64006c0a70ab4502f088637bb6871c3bb37 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -648,7 +648,7 @@ public class Rabbit extends Animal { + @Override + public boolean canUse() { + if (this.nextStartTick <= 0) { +- if (!getServerLevel(this.rabbit).getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (!getServerLevel(this.rabbit).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(this.rabbit).purpurConfig.rabbitMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + return false; + } + +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index 844c989fc4e0d131d823bf8a59951f35f30b7641..e14347ccc7b21213801e59fa2e0e47964bc943ad 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -542,7 +542,7 @@ public class EnderDragon extends Mob implements Enemy { + BlockPos blockPos = new BlockPos(i, i1, i2); + BlockState blockState = level.getBlockState(blockPos); + if (!blockState.isAir() && !blockState.is(BlockTags.DRAGON_TRANSPARENT)) { +- if (level.getGameRules().get(GameRules.MOB_GRIEFING) && !blockState.is(BlockTags.DRAGON_IMMUNE)) { ++ if (level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.enderDragonMobGriefingOverride) && !blockState.is(BlockTags.DRAGON_IMMUNE)) { // Purpur - Add mobGriefing override to everything affected + // CraftBukkit start - Add blocks to list rather than destroying them + //flag1 = level.removeBlock(blockPos, false) || flag1; + flag1 = true; +diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index 612df8799b80f1793ab9781212442098633e9d65..73dbc5ae28c5af00a36528e0397ad1c2eed14f2c 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -495,7 +495,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + if (this.destroyBlocksTick > 0) { + this.destroyBlocksTick--; +- if (this.destroyBlocksTick == 0 && level.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (this.destroyBlocksTick == 0 && level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.witherMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + boolean flag = false; + int alternativeTarget = Mth.floor(this.getBbWidth() / 2.0F + 1.0F); + int floor = Mth.floor(this.getBbHeight()); +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index 29570047bfc78a8993b0bbd7168cb4fe318a6be6..79a1d8f67a22a8fa4a6c3b657e44bb9503687c27 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -509,7 +509,7 @@ public class EnderMan extends Monster implements NeutralMob { + public boolean canUse() { + if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls + return this.enderman.getCarriedBlock() != null +- && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING) ++ && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING, this.enderman.level().purpurConfig.endermanMobGriefingOverride) // Purpur - Add mobGriefing override to everything affected + && this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; + } + +@@ -658,7 +658,7 @@ public class EnderMan extends Monster implements NeutralMob { + public boolean canUse() { + if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls + return this.enderman.getCarriedBlock() == null +- && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING) ++ && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING, this.enderman.level().purpurConfig.endermanMobGriefingOverride) // Purpur - Add mobGriefing override to everything affected + && this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; + } + +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index c3546b0841aff52ef758b0e8e48c9a70726f412d..c1ae9826d15456762ac0c0ab7380763242fa48de 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -179,7 +179,7 @@ public class Ravager extends Raider { + this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(Mth.lerp(0.1, baseValue, d)); + } + +- if (this.level() instanceof ServerLevel serverLevel && this.horizontalCollision && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (this.level() instanceof ServerLevel serverLevel && this.horizontalCollision && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.ravagerMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + boolean flag = false; + AABB aabb = this.getBoundingBox().inflate(0.2); + +diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java +index 1df006a0a49038f1e737194e7da8e0b27e6eeb95..3e8e3a6018120576b0773769db7b2037148fae96 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -168,7 +168,7 @@ public class Silverfish extends Monster { + return false; + } else { + RandomSource random = this.mob.getRandom(); +- if (getServerLevel(this.mob).getGameRules().get(GameRules.MOB_GRIEFING) && random.nextInt(reducedTickDelay(10)) == 0) { ++ if (getServerLevel(this.mob).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(this.mob).purpurConfig.silverfishMobGriefingOverride) && random.nextInt(reducedTickDelay(10)) == 0) { // Purpur - Add mobGriefing override to everything affected + this.selectedDirection = Direction.getRandom(random); + BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); + BlockState blockState = this.mob.level().getBlockState(blockPos); +@@ -245,7 +245,7 @@ public class Silverfish extends Monster { + Block block = blockState.getBlock(); + if (block instanceof InfestedBlock) { + // CraftBukkit start +- BlockState afterState = getServerLevel(level).getGameRules().get(GameRules.MOB_GRIEFING) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(blockPos1)); // Paper - fix wrong block state ++ BlockState afterState = getServerLevel(level).getGameRules().get(GameRules.MOB_GRIEFING, getServerLevel(level).purpurConfig.silverfishMobGriefingOverride) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(blockPos1)); // Paper - fix wrong block state // Purpur - Add mobGriefing override to everything affected + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockPos1, afterState)) { // Paper - fix wrong block state + continue; + } +diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java +index 46f79ed345ec51125364b49b244d6d005a3e64ae..334786b2c358c41058f2ac4b1fa4b185833cbd84 100644 +--- a/net/minecraft/world/entity/monster/illager/Evoker.java ++++ b/net/minecraft/world/entity/monster/illager/Evoker.java +@@ -306,7 +306,7 @@ public class Evoker extends SpellcasterIllager { + return false; + } else { + ServerLevel serverLevel = getServerLevel(Evoker.this.level()); +- if (!serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { ++ if (!serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.evokerMobGriefingOverride)) { // Purpur - Add mobGriefing override to everything affected + return false; + } else { + List nearbyEntities = serverLevel.getNearbyEntities( +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 98241af88cc961470e07df47d128d8912338bd44..4fccd122a227e5fe80965c37afaa031c4a673978 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -448,7 +448,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + @Override + public boolean wantsToPickUp(ServerLevel level, ItemStack stack) { +- return level.getGameRules().get(GameRules.MOB_GRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); ++ return level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.piglinMobGriefingOverride) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); // Purpur - Add mobGriefing override to everything affected + } + + protected boolean canReplaceCurrentItem(ItemStack candidate) { +diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java +index 6298e6de25f1dd9ff880a9ad2882dc7ccb2f100c..36479ed544ce812113c9a9c982a74a950cb9b26a 100644 +--- a/net/minecraft/world/entity/projectile/Projectile.java ++++ b/net/minecraft/world/entity/projectile/Projectile.java +@@ -434,7 +434,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { + @Override + public boolean mayInteract(ServerLevel level, BlockPos pos) { + Entity owner = this.getOwner(); +- return owner instanceof Player ? owner.mayInteract(level, pos) : owner == null || level.getGameRules().get(GameRules.MOB_GRIEFING); ++ return owner instanceof Player ? owner.mayInteract(level, pos) : owner == null || level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.projectilesMobGriefingOverride); // Purpur - Add mobGriefing override to everything affected + } + + public boolean mayBreak(ServerLevel level) { +diff --git a/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java b/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java +index de0f086da9fe4afa14d2b77f6b0a2c0910745b94..775aa79a0d503217dbe42268f4feccbc2d3e1edb 100644 +--- a/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java ++++ b/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java +@@ -20,20 +20,20 @@ public class LargeFireball extends Fireball { + + public LargeFireball(EntityType type, Level level) { + super(type, level); +- this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.fireballsMobGriefingOverride); // CraftBukkit // Purpur - Add mobGriefing override to everything affected + } + + public LargeFireball(Level level, LivingEntity owner, Vec3 movement, int explosionPower) { + super(EntityType.FIREBALL, owner, movement, level); + this.explosionPower = explosionPower; +- this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.fireballsMobGriefingOverride); // CraftBukkit // Purpur - Add mobGriefing override to everything affected + } + + @Override + protected void onHit(HitResult result) { + super.onHit(result); + if (this.level() instanceof ServerLevel serverLevel) { +- // boolean flag = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit - baked into fields (see constructor) ++ // boolean flag = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.fireballsMobGriefingOverride); // CraftBukkit - baked into fields (see constructor) // Purpur - Add mobGriefing override to everything affected + // CraftBukkit start - fire ExplosionPrimeEvent + org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity()); + if (event.callEvent()) { +diff --git a/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java b/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java +index 043ddaa0e8718f50881d16892eab188aed70f67f..a10617c29098f28c5b5586a43071a47ef17e5744 100644 +--- a/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java ++++ b/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java +@@ -25,7 +25,7 @@ public class SmallFireball extends Fireball { + super(EntityType.SMALL_FIREBALL, owner, movement, level); + // CraftBukkit start + if (this.getOwner() != null && this.getOwner() instanceof Mob) { +- this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.fireballsMobGriefingOverride); // Purpur - Add mobGriefing override to everything affected + } + // CraftBukkit end + } +diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java +index 298d6749bdd5b6f09530096f8f546d9f46910b19..b03faa7600f182a12c3a739deefc38028cf45615 100644 +--- a/net/minecraft/world/entity/raid/Raider.java ++++ b/net/minecraft/world/entity/raid/Raider.java +@@ -396,7 +396,7 @@ public abstract class Raider extends PatrollingMonster { + } + + private boolean cannotPickUpBanner() { +- if (!getServerLevel(this.mob).getGameRules().get(net.minecraft.world.level.gamerules.GameRules.MOB_GRIEFING)) return true; // Paper - respect game and entity rules for picking up items ++ if (!getServerLevel(this.mob).getGameRules().get(net.minecraft.world.level.gamerules.GameRules.MOB_GRIEFING, this.mob.level().purpurConfig.pillagerMobGriefingOverride)) return true; // Paper - respect game and entity rules for picking up items // Purpur - Add mobGriefing override to everything affected + if (!this.mob.hasActiveRaid()) { + return true; + } else if (this.mob.getCurrentRaid().isOver()) { +diff --git a/net/minecraft/world/level/block/CropBlock.java b/net/minecraft/world/level/block/CropBlock.java +index 4ad9b4afeb6958cba98d8413b3371754730741e5..0b748fac353d6a8b5351ad67df7d239d3efa43b0 100644 +--- a/net/minecraft/world/level/block/CropBlock.java ++++ b/net/minecraft/world/level/block/CropBlock.java +@@ -169,7 +169,7 @@ public class CropBlock extends VegetationBlock implements BonemealableBlock { + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent +- if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.purpurConfig.ravagerGriefableBlocks.contains(serverLevel.getBlockState(pos).getBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list ++ if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.purpurConfig.ravagerGriefableBlocks.contains(serverLevel.getBlockState(pos).getBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.ravagerMobGriefingOverride))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list // Purpur - Add mobGriefing override to everything affected + serverLevel.destroyBlock(pos, true, entity); + } + +diff --git a/net/minecraft/world/level/block/FarmBlock.java b/net/minecraft/world/level/block/FarmBlock.java +index cd90b5151cae59a63836e88c6457c2954e24a345..3fd6a259788503f2a6d33d29a9247796f75e719a 100644 +--- a/net/minecraft/world/level/block/FarmBlock.java ++++ b/net/minecraft/world/level/block/FarmBlock.java +@@ -114,7 +114,7 @@ public class FarmBlock extends Block { + if (level instanceof ServerLevel serverLevel + && (serverLevel.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= serverLevel.purpurConfig.farmlandTrampleHeight : level.random.nextFloat() < fallDistance - 0.5) // Purpur - Configurable farmland trample height + && entity instanceof LivingEntity +- && (entity instanceof Player || serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) ++ && (entity instanceof Player || serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.farmlandMobGriefingOverride)) // Purpur - Add mobGriefing override to everything affected + && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { + // CraftBukkit start - Interact soil + org.bukkit.event.Cancellable cancellable; +diff --git a/net/minecraft/world/level/block/PowderSnowBlock.java b/net/minecraft/world/level/block/PowderSnowBlock.java +index f3a9117bd066861a1b447e8a13108ad212cc499f..b0165c839b145e82af38fe8f61bbd0161015d867 100644 +--- a/net/minecraft/world/level/block/PowderSnowBlock.java ++++ b/net/minecraft/world/level/block/PowderSnowBlock.java +@@ -96,7 +96,7 @@ public class PowderSnowBlock extends Block implements BucketPickup { + // CraftBukkit - move down + && entity1.mayInteract(serverLevel, blockPos)) { + // CraftBukkit start +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity1, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.getGameRules().get(GameRules.MOB_GRIEFING) || entity1 instanceof Player))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity1, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.getGameRules().get(GameRules.MOB_GRIEFING, serverLevel.purpurConfig.powderSnowMobGriefingOverride) || entity1 instanceof Player))) { // Purpur - Add mobGriefing override to everything affected + return; + } + // CraftBukkit end +diff --git a/net/minecraft/world/level/block/TurtleEggBlock.java b/net/minecraft/world/level/block/TurtleEggBlock.java +index 77c2fb39c961180cd8280b97205273a5562481f7..f10a56052c51509784fb1f3851e41548c04d6a3c 100644 +--- a/net/minecraft/world/level/block/TurtleEggBlock.java ++++ b/net/minecraft/world/level/block/TurtleEggBlock.java +@@ -214,7 +214,7 @@ public class TurtleEggBlock extends Block { + // Purpur end - Option to disable turtle egg trampling with feather falling + if (entity instanceof Player) return true; + +- return level.getGameRules().get(GameRules.MOB_GRIEFING); ++ return level.getGameRules().get(GameRules.MOB_GRIEFING, level.purpurConfig.turtleEggsMobGriefingOverride); // Purpur - Add mobGriefing override to everything affected + // Purpur end - Add turtle egg block options + } + } +diff --git a/net/minecraft/world/level/gamerules/GameRules.java b/net/minecraft/world/level/gamerules/GameRules.java +index e71a9ffdb6c081baa79113f34b179671c179152d..746bc21fc09644366caa8bfd309f596c3cac59ec 100644 +--- a/net/minecraft/world/level/gamerules/GameRules.java ++++ b/net/minecraft/world/level/gamerules/GameRules.java +@@ -113,6 +113,13 @@ public class GameRules { + return this.rules.keySet().stream(); + } + ++ public boolean get(GameRule rule, Boolean gameRuleOverride) { ++ if (gameRuleOverride != null) { ++ return gameRuleOverride; ++ } ++ return (Boolean) this.get(rule); ++ } ++ + public T get(GameRule rule) { + T object = this.rules.get(rule); + if (object == null) { diff --git a/purpur-server/minecraft-patches/features/0016-Toggle-for-water-sensitive-mob-damage.patch b/purpur-server/minecraft-patches/features/0016-Toggle-for-water-sensitive-mob-damage.patch new file mode 100644 index 000000000..3d05d0bdd --- /dev/null +++ b/purpur-server/minecraft-patches/features/0016-Toggle-for-water-sensitive-mob-damage.patch @@ -0,0 +1,1372 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: YouHaveTrouble +Date: Fri, 5 Feb 2021 01:11:22 +0100 +Subject: [PATCH] Toggle for water sensitive mob damage + + +diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index a09b5035e91d50e12f613a7a1211c6869fbbf4df..e83900f8d6adbe8a48294f250bd6cc6e3fad8160 100644 +--- a/net/minecraft/world/entity/ambient/Bat.java ++++ b/net/minecraft/world/entity/ambient/Bat.java +@@ -109,6 +109,13 @@ public class Bat extends AmbientCreature { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.batTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean isFlapping() { + return !this.isResting() && this.tickCount % 10.0F == 0.0F; +diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 79dfebbd72424d5a3a13d4ba89a555fdf782e703..947160c4dfc4e50bb75a1a797cb2f22602b22643 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -153,6 +153,13 @@ public class Axolotl extends Animal implements Bucketable { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.axolotlTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return 0.0F; +diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java +index 473aa29e3075fcab44a14c2bc14d3f222b55cbad..e57200d6560a38cbecd681a30b75409412fce170 100644 +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -185,7 +185,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + // Paper end - Fix MC-167279 + this.lookControl = new Bee.BeeLookControl(this); + this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F); +- if (this.level().purpurConfig.beeCanInstantlyStartDrowning) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - bee can instantly start drowning in water option ++ if (this.level().purpurConfig.beeCanInstantlyStartDrowning || isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - bee can instantly start drowning in water option // Purpur - Toggle for water sensitive mob damage + this.setPathfindingMalus(PathType.WATER_BORDER, 16.0F); + this.setPathfindingMalus(PathType.COCOA, -1.0F); + this.setPathfindingMalus(PathType.FENCE, -1.0F); +@@ -492,6 +492,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.beeTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public long getPersistentAngerEndTime() { + return this.entityData.get(DATA_ANGER_END_TIME); +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index 029fe4ac952d9f56824de346d98b341bd0b9b65f..6308bb606b34d781e315f56a55e6544b3c156d85 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -110,6 +110,13 @@ public class Chicken extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.chickenTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index 400b0a0a99590e3590cb66724acdc5ba9170f2c4..bb8c75d20e6a1300d68ae75233962bdc26736c77 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -61,6 +61,13 @@ public class Cow extends AbstractCow { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.cowTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java +index e2aa027dc7a39fa8a8868a7d153e838113fcf97b..a8e6e703a51130066547724dd08bdfe5e11c99e5 100644 +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -92,6 +92,13 @@ public class MushroomCow extends AbstractCow implements Shearable { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.mooshroomTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); +diff --git a/net/minecraft/world/entity/animal/dolphin/Dolphin.java b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +index 94f92080de46f2af67e1d28753208691da534ddf..d73789bb8ce0f65be94437484c3ed41e26cd7510 100644 +--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java ++++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +@@ -160,6 +160,13 @@ public class Dolphin extends AgeableWaterCreature { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.dolphinTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java +index d74a5cc2a43ef41fd00676bbd17fe4df9edb67ff..6cf6853c244b7589d2a57e7ae77933d4a1503ccd 100644 +--- a/net/minecraft/world/entity/animal/equine/Donkey.java ++++ b/net/minecraft/world/entity/animal/equine/Donkey.java +@@ -47,6 +47,13 @@ public class Donkey extends AbstractChestedHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.donkeyTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java +index ba9df659ed019f9e58991059fe99c1471bfc94d8..1c17ab94901ce1c6370670c06bf0801ee6b5b393 100644 +--- a/net/minecraft/world/entity/animal/equine/Horse.java ++++ b/net/minecraft/world/entity/animal/equine/Horse.java +@@ -81,6 +81,13 @@ public class Horse extends AbstractHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.horseTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void randomizeAttributes(RandomSource random) { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt)); +diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java +index 960e48f5ae63d8c86758d2573510bf481ca4bca0..eb2fc0e6d497241f1b4f3971155b10d14f5427f8 100644 +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -159,6 +159,13 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.llamaTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public boolean isTraderLlama() { + return false; + } +diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java +index 180a807026437e949e3f7c3d59864ea328f6f03f..9bafe3e204b3e4624a95b130552c8230c0835a30 100644 +--- a/net/minecraft/world/entity/animal/equine/Mule.java ++++ b/net/minecraft/world/entity/animal/equine/Mule.java +@@ -46,6 +46,13 @@ public class Mule extends AbstractChestedHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.muleTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.MULE_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +index af0e326f5ff8fcb82353bac3143103928a6a08e6..3865937e4a56636cde44f7d8f16873ae16512323 100644 +--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +@@ -74,6 +74,13 @@ public class SkeletonHorse extends AbstractHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.skeletonHorseTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java +index 65ac46335d642d5dd8c01623721257a4fd53441d..4c07cd57aa3518adb4bc9b1213cc572a5b81f935 100644 +--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java ++++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java +@@ -77,6 +77,13 @@ public class TraderLlama extends Llama { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.traderLlamaTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean isTraderLlama() { + return true; +diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +index fa2f18470123f98e336ab5c94bc791e9333356cb..b770d3118452b0b7a362b1f541bb602f4d3cbcec 100644 +--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java ++++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +@@ -88,6 +88,13 @@ public class ZombieHorse extends AbstractHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.zombieHorseTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0); + } +diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java +index d0aa698ef99cf3b1926018314b4067cc1cd8ac2a..92b67565485b2fade3e80fb4b944edfc12d9e0a2 100644 +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -135,6 +135,13 @@ public class Cat extends TamableAnimal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.catTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java +index bf281fd4c050b87fd277ab68e812ab2dcd3d06aa..13272516003f763d2c69dc599c8ff7696bec718b 100644 +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -98,6 +98,13 @@ public class Ocelot extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.ocelotTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public boolean isTrusting() { + return this.entityData.get(DATA_TRUSTING); + } +diff --git a/net/minecraft/world/entity/animal/fish/Cod.java b/net/minecraft/world/entity/animal/fish/Cod.java +index e00e623d7df9e179d8bb5ee4812538578c5f4c08..8655aae805f239cbd049065232293854b18c73cf 100644 +--- a/net/minecraft/world/entity/animal/fish/Cod.java ++++ b/net/minecraft/world/entity/animal/fish/Cod.java +@@ -32,6 +32,13 @@ public class Cod extends AbstractSchoolingFish { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.codTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public ItemStack getBucketItemStack() { + return new ItemStack(Items.COD_BUCKET); +diff --git a/net/minecraft/world/entity/animal/fish/Pufferfish.java b/net/minecraft/world/entity/animal/fish/Pufferfish.java +index b2932aeb6000a4e268db12cb7b05be746788569c..4d21718441b0272774ec69e9b72a180fe417cbb0 100644 +--- a/net/minecraft/world/entity/animal/fish/Pufferfish.java ++++ b/net/minecraft/world/entity/animal/fish/Pufferfish.java +@@ -66,6 +66,13 @@ public class Pufferfish extends AbstractFish { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.pufferfishTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/fish/Salmon.java b/net/minecraft/world/entity/animal/fish/Salmon.java +index 97194d5c844bf96ba431c12ccc1a82cb8944c52b..424bd56ec9df8bd0a80d2fa79bc4f482eae2ab5c 100644 +--- a/net/minecraft/world/entity/animal/fish/Salmon.java ++++ b/net/minecraft/world/entity/animal/fish/Salmon.java +@@ -59,6 +59,13 @@ public class Salmon extends AbstractSchoolingFish { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.salmonTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public int getMaxSchoolSize() { + return 5; +diff --git a/net/minecraft/world/entity/animal/fish/TropicalFish.java b/net/minecraft/world/entity/animal/fish/TropicalFish.java +index 8ffb253c114d882c9459f74a27bd8a086ae82f01..04c30c60c76122018fa69cf1f3f64cae8ed62fad 100644 +--- a/net/minecraft/world/entity/animal/fish/TropicalFish.java ++++ b/net/minecraft/world/entity/animal/fish/TropicalFish.java +@@ -96,6 +96,13 @@ public class TropicalFish extends AbstractSchoolingFish { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.tropicalFishTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static String getPredefinedName(int variantId) { + return "entity.minecraft.tropical_fish.predefined." + variantId; + } +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index a0ed30e8ccd41729de8e0ff977a61fa607386640..bc119c2f61adb6c6122119fda1df437bab40f9b4 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -204,6 +204,13 @@ public class Fox extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.foxTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index 07ba57ae845024e0f2fa03a68e126ec79bfc6b15..e5f5bc2c4b4f36e0e911b2c5ef67ef6e0d4cd0b1 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -139,6 +139,13 @@ public class Goat extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.goatTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/golem/CopperGolem.java b/net/minecraft/world/entity/animal/golem/CopperGolem.java +index e3c5b6fe7d6cdb674fbf5ac22001c3d917bf91ec..2d0ce5d420f88a95eda34a3fe81c815999bde300 100644 +--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java +@@ -140,6 +140,13 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.copperGolemTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0); + } +diff --git a/net/minecraft/world/entity/animal/golem/IronGolem.java b/net/minecraft/world/entity/animal/golem/IronGolem.java +index 677f584b38aeb6805db0bb867f62d5617e309f5e..d31c1f1b681922ca8f1657ffa333e8a6794e619f 100644 +--- a/net/minecraft/world/entity/animal/golem/IronGolem.java ++++ b/net/minecraft/world/entity/animal/golem/IronGolem.java +@@ -99,6 +99,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.ironGolemTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options +diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java +index f011369a67809d54230984a22a909bfea44f47f3..0fe7f18390490ccf4e944ac3378150ebcc53991d 100644 +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -124,7 +124,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + + @Override + public boolean isSensitiveToWater() { +- return true; ++ return this.level().purpurConfig.snowGolemTakeDamageFromWater; // Purpur - Toggle for water sensitive mob damage + } + + @Override +diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +index 30204224ed83f6d3cfd64640b332b23cb74d379f..828793aaa2992fd1077040309154f814b302476a 100644 +--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java ++++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +@@ -153,6 +153,13 @@ public class HappyGhast extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.happyGhastTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void ageBoundaryReached() { + if (this.isBaby()) { +diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java +index 163fe697f57459b36885fa3a18f41370347cab38..e9aa07dea0515f53a08a7066fa9e23e0ee69d69e 100644 +--- a/net/minecraft/world/entity/animal/panda/Panda.java ++++ b/net/minecraft/world/entity/animal/panda/Panda.java +@@ -150,6 +150,13 @@ public class Panda extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.pandaTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) { + return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot(); +diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java +index 0d62046cb33ed750ab27229fe4f0b43c3a1dd5f4..e97782bc8232120d4e7d1feeb8ca87fd37a1fcf9 100644 +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -214,6 +214,13 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.parrotTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java +index 0cb09a058c2d21b10677482792b230464ed9f951..020932539727739b54ed2f7899cbf11ad940f4df 100644 +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -95,6 +95,13 @@ public class Pig extends Animal implements ItemSteerable { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.pigTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +index 97edd950e2142e73ad947590c99d6d5529cbd44c..13d26a6981d6907f35f1f2bde93934c7c1fe8120 100644 +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -133,6 +133,13 @@ public class PolarBear extends Animal implements NeutralMob { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.polarBearTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index 66fda64006c0a70ab4502f088637bb6871c3bb37..b1c72047f7aa63f62b0ad0274811b587b1d3486b 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -163,6 +163,13 @@ public class Rabbit extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.rabbitTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java +index 0e4505474a50d57338c16648cb6049d35455fdc6..7eca49c9237c61ab12f60ff16085aa8fd10c99ac 100644 +--- a/net/minecraft/world/entity/animal/sheep/Sheep.java ++++ b/net/minecraft/world/entity/animal/sheep/Sheep.java +@@ -95,6 +95,13 @@ public class Sheep extends Animal implements Shearable { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.sheepTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.eatBlockGoal = new EatBlockGoal(this); +diff --git a/net/minecraft/world/entity/animal/squid/GlowSquid.java b/net/minecraft/world/entity/animal/squid/GlowSquid.java +index 7d41aaa0546a9ca02e46ce46e61ecc57120bfca9..2aa2a84a7ada369c45a85119a933eb92297af4dc 100644 +--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java ++++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java +@@ -57,6 +57,13 @@ public class GlowSquid extends Squid { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.glowSquidTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected ParticleOptions getInkParticle() { + return ParticleTypes.GLOW_SQUID_INK; +diff --git a/net/minecraft/world/entity/animal/squid/Squid.java b/net/minecraft/world/entity/animal/squid/Squid.java +index 5e4181f95711c6b0299f28de14d95da421dda373..e307ebfb8b2781b6175b520e148e6bd58d2b9dc4 100644 +--- a/net/minecraft/world/entity/animal/squid/Squid.java ++++ b/net/minecraft/world/entity/animal/squid/Squid.java +@@ -101,6 +101,13 @@ public class Squid extends AgeableWaterCreature { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.squidTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); +diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java +index fce1b1a22e091a1c05650c72e1ceb83e1a1d1ed1..7d5987fe641646ecbf8e389d57a0a9a04e9c0959 100644 +--- a/net/minecraft/world/entity/animal/turtle/Turtle.java ++++ b/net/minecraft/world/entity/animal/turtle/Turtle.java +@@ -119,6 +119,13 @@ public class Turtle extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.turtleTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public void setHomePos(BlockPos homePos) { + this.homePos = homePos; + } +diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java +index 08a1bafa0e45dbdbf8bdc4d5cb654d080590707d..20f945ee06bcdb4736e6d3a8b20a5cbd3d79df0f 100644 +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -223,6 +223,13 @@ public class Wolf extends TamableAnimal implements NeutralMob { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.wolfTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index e14347ccc7b21213801e59fa2e0e47964bc943ad..5efddef6e48bf28aa75041c291d954bbad527726 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -170,6 +170,13 @@ public class EnderDragon extends Mob implements Enemy { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.enderDragonTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0).add(Attributes.CAMERA_DISTANCE, 16.0); + } +diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index 73dbc5ae28c5af00a36528e0397ad1c2eed14f2c..ef6dcc8183d9963d1e683f2cc74fec6443d175a9 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -221,6 +221,13 @@ public class WitherBoss extends Monster implements RangedAttackMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.witherTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +diff --git a/net/minecraft/world/entity/monster/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java +index 4cd7975a8e9e55d6b6ded0b7e13d8da436e49c6c..e2afd7677986c70dd924017e8822fd6abd808f48 100644 +--- a/net/minecraft/world/entity/monster/Blaze.java ++++ b/net/minecraft/world/entity/monster/Blaze.java +@@ -35,7 +35,7 @@ public class Blaze extends Monster { + public Blaze(EntityType type, Level level) { + super(type, level); + this.moveControl = new org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD(this, 0.3F); // Purpur - Ridables +- this.setPathfindingMalus(PathType.WATER, -1.0F); ++ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - Toggle for water sensitive mob damage + this.setPathfindingMalus(PathType.LAVA, 8.0F); + this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F); + this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F); +@@ -158,7 +158,7 @@ public class Blaze extends Monster { + + @Override + public boolean isSensitiveToWater() { +- return true; ++ return this.level().purpurConfig.blazeTakeDamageFromWater; // Purpur - Toggle for water sensitive mob damage + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index ed1ccd712a2fe00740beeee4fe615976258f5c6c..b0ed96c243f9abb3868d92bb513ad1620aa8e269 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -266,6 +266,13 @@ public class Creeper extends Monster { + } + // Purpur end - Config to make Creepers explode on death + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.creeperTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.CREEPER_HURT; +diff --git a/net/minecraft/world/entity/monster/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index 57fb334f670d35aa181b13e12fa2c5f36da1dda6..1ea323a17440abbe36c9528e80e169816267005b 100644 +--- a/net/minecraft/world/entity/monster/ElderGuardian.java ++++ b/net/minecraft/world/entity/monster/ElderGuardian.java +@@ -51,6 +51,13 @@ public class ElderGuardian extends Guardian { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.elderGuardianTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0); + } +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index 79a1d8f67a22a8fa4a6c3b657e44bb9503687c27..47c621f01658f3392b58f3f5c1f31bd539f63dd6 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -86,7 +86,7 @@ public class EnderMan extends Monster implements NeutralMob { + + public EnderMan(EntityType type, Level level) { + super(type, level); +- this.setPathfindingMalus(PathType.WATER, -1.0F); ++ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - Toggle for water sensitive mob damage + } + + // Purpur start - Ridables +@@ -285,7 +285,7 @@ public class EnderMan extends Monster implements NeutralMob { + + @Override + public boolean isSensitiveToWater() { +- return true; ++ return this.level().purpurConfig.endermanTakeDamageFromWater; // Purpur - Toggle for water sensitive mob damage + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java +index c6ae00f6ab83743b9ce43d9e55f04ba23b0de0f1..c79c689cb99ad2a6cab88cf8b583ff0f9e91b115 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -72,6 +72,13 @@ public class Endermite extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.endermiteTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index 7d97c00ae49629b97104631317aa6174741cdd5e..443eb9f12d6df950c59c9d4db4853087fd23d24e 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -83,6 +83,13 @@ public class Ghast extends Mob implements Enemy { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.ghastTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index 40cf8f44a6b01d45306dab4ba4f9a7ea7c6590a9..f1176f73d4468b2f39e48fe5454c5f5bf481afd9 100644 +--- a/net/minecraft/world/entity/monster/Giant.java ++++ b/net/minecraft/world/entity/monster/Giant.java +@@ -62,6 +62,13 @@ public class Giant extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.giantTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 100.0) +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index 1eaa4f255b01eddc93ddbc5615ad05e7d8273581..9948511659de9222a19f6b9f660e4441ea98a4a9 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -97,6 +97,13 @@ public class Guardian extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.guardianTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0); +diff --git a/net/minecraft/world/entity/monster/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index e0fda9a975e00c49ba09db65d7b3fba8fa434757..1bf9d04e5e44272b02753290995795f194cbdae1 100644 +--- a/net/minecraft/world/entity/monster/MagmaCube.java ++++ b/net/minecraft/world/entity/monster/MagmaCube.java +@@ -68,6 +68,13 @@ public class MagmaCube extends Slime { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.magmaCubeTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 6e8fe1b694ade45ffbf0b9bb39b954deffeb2402..2fa22088ac507eceb36f3c77f24a733076641d8f 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -128,6 +128,13 @@ public class Phantom extends Mob implements Enemy { + } + // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.phantomTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean isFlapping() { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index 7c69423eee05992b9c70321d528cc9d73b3e63ea..ad2913b02ded9b5087c69ba279cebfd81d9ef75e 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -103,6 +103,13 @@ public class Ravager extends Raider { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.ravagerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Shulker.java b/net/minecraft/world/entity/monster/Shulker.java +index 30f2503ebd045b05f5594cef28389a694338d13a..0a5a6f23cd8e4317db4e7c0ba8883e99f3aff148 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -133,6 +133,13 @@ public class Shulker extends AbstractGolem implements Enemy { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.shulkerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java +index 3e8e3a6018120576b0773769db7b2037148fae96..1692e7c93234506e22039da071e9c8e8a8567495 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -65,6 +65,13 @@ public class Silverfish extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.silverfishTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this); +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index f93a2c58b5835e9ce8318d28e944533ef69df8ef..c4222e17d9a1f4cba6a74099a167cff13856f3e3 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -135,6 +135,13 @@ public class Slime extends Mob implements Enemy { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.slimeTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index ad6515c2dcde0079095c6777f56319e9901850f8..758887f3fcec2bf0946cddb184e19a1c9b64eee3 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -86,7 +86,7 @@ public class Strider extends Animal implements ItemSteerable { + public Strider(EntityType type, Level level) { + super(type, level); + this.blocksBuilding = true; +- this.setPathfindingMalus(PathType.WATER, -1.0F); ++ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - Toggle for water sensitive mob damage + this.setPathfindingMalus(PathType.LAVA, 0.0F); + this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F); + this.setPathfindingMalus(PathType.DAMAGE_FIRE, 0.0F); +@@ -388,7 +388,7 @@ public class Strider extends Animal implements ItemSteerable { + + @Override + public boolean isSensitiveToWater() { +- return true; ++ return this.level().purpurConfig.striderTakeDamageFromWater; // Purpur - Toggle for water sensitive mob damage + } + + @Override +diff --git a/net/minecraft/world/entity/monster/Vex.java b/net/minecraft/world/entity/monster/Vex.java +index 32759864a8ff0c4e28ce80ae8906cbcf1927094e..6ead673c44891c6b1c1688f80bf299527d7bc777 100644 +--- a/net/minecraft/world/entity/monster/Vex.java ++++ b/net/minecraft/world/entity/monster/Vex.java +@@ -110,6 +110,13 @@ public class Vex extends Monster implements TraceableEntity { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.vexTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean isFlapping() { + return this.tickCount % TICKS_PER_FLAP == 0; +diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 3c3c258d4cc7cf7376d0d3ad0794ec3611ab81ce..86d9952ed0f692e8f229b0c0bcb6cacf53a00c8b 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -82,6 +82,13 @@ public class Witch extends Raider implements RangedAttackMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.witchTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index e0192a13b8d7131024471569bf34329ba0f28287..e2708eb836d60588c1bedb75c520e5007185034a 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -112,6 +112,13 @@ public class Zoglin extends Monster implements HoglinBase { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.zoglinTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 4498fe26dff2cdec8e90d6559a66ebe7859195bb..5e3f7005a7167c786038823a8437ad2e90a41d2c 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -129,6 +129,13 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.hoglinTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @VisibleForTesting + public void setTimeInOverworld(int timeInOverworld) { + this.timeInOverworld = timeInOverworld; +diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java +index 334786b2c358c41058f2ac4b1fa4b185833cbd84..c4f45f6303a46ad54991c9013608a4e5ba8b9fee 100644 +--- a/net/minecraft/world/entity/monster/illager/Evoker.java ++++ b/net/minecraft/world/entity/monster/illager/Evoker.java +@@ -74,6 +74,13 @@ public class Evoker extends SpellcasterIllager { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.evokerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Illusioner.java b/net/minecraft/world/entity/monster/illager/Illusioner.java +index 8fa53cccb73b2d8c599c898f298681735007d2a0..32278f51f4973d1294e52f0775697e9f5b295ee0 100644 +--- a/net/minecraft/world/entity/monster/illager/Illusioner.java ++++ b/net/minecraft/world/entity/monster/illager/Illusioner.java +@@ -86,6 +86,13 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.illusionerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Pillager.java b/net/minecraft/world/entity/monster/illager/Pillager.java +index 1e43c69366287c7a191a6f8e3a7d5459743f07a2..6190286ab2f454382d497b4fce47632e6f771215 100644 +--- a/net/minecraft/world/entity/monster/illager/Pillager.java ++++ b/net/minecraft/world/entity/monster/illager/Pillager.java +@@ -90,6 +90,13 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.pillagerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Vindicator.java b/net/minecraft/world/entity/monster/illager/Vindicator.java +index 5f3857186e86e27fe237c62cec4af13ebf58debe..86b463a16e8630af4918ea43a2546995aa00244f 100644 +--- a/net/minecraft/world/entity/monster/illager/Vindicator.java ++++ b/net/minecraft/world/entity/monster/illager/Vindicator.java +@@ -82,6 +82,13 @@ public class Vindicator extends AbstractIllager { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.vindicatorTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 4fccd122a227e5fe80965c37afaa031c4a673978..d6c834914ddea76280466bf0f7d001a76af34f08 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -166,6 +166,13 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.piglinTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index 3d34bef7ffe9e66e77e3fc10b2c5869d98a4a5c9..4bf2038fea712e65f420ade915a18470b79318fc 100644 +--- a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +@@ -90,6 +90,13 @@ public class PiglinBrute extends AbstractPiglin { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.piglinBruteTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 50.0) +diff --git a/net/minecraft/world/entity/monster/skeleton/Skeleton.java b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +index 96d989cffc6ff72954ed92e2535c72992d489372..30d79be765118a50e2e2c5bfde6bf6bbc957cf40 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +@@ -50,6 +50,13 @@ public class Skeleton extends AbstractSkeleton { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.skeletonTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Stray.java b/net/minecraft/world/entity/monster/skeleton/Stray.java +index 575f30cc9a6166a4e0733cc33e8b5814acb92660..ec19c49d195daff5839c991cce63b1ffc15a7e0b 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Stray.java ++++ b/net/minecraft/world/entity/monster/skeleton/Stray.java +@@ -47,6 +47,13 @@ public class Stray extends AbstractSkeleton { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.strayTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + public static boolean checkStraySpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +index f38bb54c0f5d7797179e5e75eb8092baea7b9dcb..3e8cc658b333f1259784e1426896ac8b8c1537fa 100644 +--- a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +@@ -59,6 +59,13 @@ public class WitherSkeleton extends AbstractSkeleton { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.witherSkeletonTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); +diff --git a/net/minecraft/world/entity/monster/spider/CaveSpider.java b/net/minecraft/world/entity/monster/spider/CaveSpider.java +index a1391173cc4997df723c59f176726dde88491978..fad3e951e443f04f5bf0423cde42c0ae299a4236 100644 +--- a/net/minecraft/world/entity/monster/spider/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/spider/CaveSpider.java +@@ -51,6 +51,13 @@ public class CaveSpider extends Spider { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.caveSpiderTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean doHurtTarget(ServerLevel level, Entity target) { + if (super.doHurtTarget(level, target)) { +diff --git a/net/minecraft/world/entity/monster/spider/Spider.java b/net/minecraft/world/entity/monster/spider/Spider.java +index 6a9c807ed50dab4b65787d9f7269385103fa5f26..81c500de41b11d4aa6c52dc290f132ad2c23f009 100644 +--- a/net/minecraft/world/entity/monster/spider/Spider.java ++++ b/net/minecraft/world/entity/monster/spider/Spider.java +@@ -77,6 +77,13 @@ public class Spider extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.spiderTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java +index 327086c972423e4f73402ea2e64519a355fa7b5a..ec36b854dccae7cef905aeb2fcd4ec177828139c 100644 +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -126,6 +126,13 @@ public class Drowned extends Zombie implements RangedAttackMob { + } + // Purpur end - Configurable jockey options + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.drownedTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index d31145fee0f646d734e90199288b29f07854d066..928e2c95146bc3fc9b8c41c5b6c3970fc919bb15 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -75,6 +75,13 @@ public class Husk extends Zombie { + } + // Purpur end - Configurable jockey options + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.huskTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public boolean isSunSensitive() { + return false; +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index 638642628c3dc9fa25d25c589029219c23d1e602..dfdf9ee98a25ec1f7a1f41326c98155131494d93 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -142,6 +142,13 @@ public class Zombie extends Monster { + } + // Purpur end - Configurable jockey options + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.zombieTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +index 62f55763d95e496da8b8fcf7d95752777e323b48..3da4f757d879f8b855c5d36688f1f5dd50fc88a0 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -131,6 +131,13 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + } + // Purpur end - Configurable jockey options + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.zombieVillagerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +index ec39ad6740361774f9ecfda7186cab9d8fac90f2..9a1da88752b0dd708365dd41a074cc0e90f36e1c 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -106,6 +106,13 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + } + // Purpur end - Configurable jockey options + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.zombifiedPiglinTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index ed6642023fccbc8f825d7c5c206d73ffd666f0f9..b6bfe06a2ad8e4219d99de050b30ce1ad15b8b34 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -282,6 +282,13 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.villagerTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + public Brain getBrain() { + return (Brain)super.getBrain(); +diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +index c6c4f4f2a970db7e782181eaca312931b192b0d5..cabebbc25788a8c41912f97dac85af41c9433b68 100644 +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -99,6 +99,13 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + } + // Purpur end - Villagers follow emerald blocks + ++ // Purpur start - Toggle for water sensitive mob damage ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.wanderingTraderTakeDamageFromWater; ++ } ++ // Purpur end - Toggle for water sensitive mob damage ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); diff --git a/purpur-server/minecraft-patches/features/0017-API-for-any-mob-to-burn-daylight.patch b/purpur-server/minecraft-patches/features/0017-API-for-any-mob-to-burn-daylight.patch new file mode 100644 index 000000000..c6d1063e0 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0017-API-for-any-mob-to-burn-daylight.patch @@ -0,0 +1,279 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ben Kerllenevich +Date: Tue, 25 May 2021 16:31:09 -0400 +Subject: [PATCH] API for any mob to burn daylight + +Co-authored by: Encode42 + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 468df93a0302f200c2bd5e9bc65feccdd8649bf3..663cd67250c516fa8e16fa55dd91c02131507fd0 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -544,6 +544,24 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + } + // Purpur end - Add canSaveToDisk to Entity + ++ // Purpur start - copied from Mob - API for any mob to burn daylight ++ public boolean isSunBurnTick() { ++ if (!this.level().isClientSide() && this.level().environmentAttributes().getValue(EnvironmentAttributes.MONSTERS_BURN, this.position())) { ++ float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); ++ BlockPos blockPos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); ++ boolean flag = this.isInWater() || this.isInPowderSnow || this.wasInPowderSnow; ++ if (lightLevelDependentMagicValue > 0.5F ++ && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F ++ && !flag ++ && this.level().canSeeSky(blockPos)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ // Purpur end - copied from Mob - API for any mob to burn daylight ++ + public Entity(EntityType type, Level level) { + this.type = type; + this.level = level; +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index f991e7a6988e98ec64b6af3c6c56522b19d929d2..78e68335805270ff1f942fe28e0e833adb41fd85 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -290,6 +290,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API + public int invulnerableDuration = LivingEntity.INVULNERABLE_DURATION; // Paper - configurable invulnerable duration ++ protected boolean shouldBurnInDay = false; public boolean shouldBurnInDay() { return this.shouldBurnInDay; } public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } // Purpur - API for any mob to burn daylight + // CraftBukkit end + + protected LivingEntity(EntityType type, Level level) { +@@ -793,6 +794,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + this.getSleepingPos().ifPresent(blockPos -> output.store("sleeping_pos", BlockPos.CODEC, blockPos)); + DataResult> dataResult = this.brain.serializeStart(NbtOps.INSTANCE).map(tag -> new Dynamic<>(NbtOps.INSTANCE, tag)); + dataResult.resultOrPartial(LOGGER::error).ifPresent(dynamic -> output.store("Brain", Codec.PASSTHROUGH, (Dynamic)dynamic)); ++ output.putBoolean("Purpur.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - API for any mob to burn daylight + if (this.lastHurtByPlayer != null) { + this.lastHurtByPlayer.store(output, "last_hurt_by_player"); + output.putInt("last_hurt_by_player_memory_time", this.lastHurtByPlayerMemoryTime); +@@ -917,6 +919,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + } // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong + }, this::clearSleepingPos); + input.read("Brain", Codec.PASSTHROUGH).ifPresent(dynamic -> this.brain = this.makeBrain((Dynamic)dynamic)); ++ this.shouldBurnInDay = input.getBooleanOr("Purpur.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - API for any mob to burn daylight + this.lastHurtByPlayer = EntityReference.read(input, "last_hurt_by_player"); + this.lastHurtByPlayerMemoryTime = input.getIntOr("last_hurt_by_player_memory_time", 0); + this.lastHurtByMob = EntityReference.read(input, "last_hurt_by_mob"); +@@ -3850,6 +3853,37 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin + if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterOrRain()) { + this.hurtServer(serverLevel, this.damageSources().drown(), 1.0F); + } ++ ++ // Purpur start - copied from Mob - API for any mob to burn daylight ++ if (this.getType().is(EntityTypeTags.BURN_IN_DAYLIGHT) && this.isAlive()) { ++ boolean flag = this.shouldBurnInDay() && this.isSunBurnTick(); // Paper - shouldBurnInDay API // Purpur - use shouldBurnInDay() method to handle Phantoms properly - API for any mob to burn daylight ++ if (flag) { ++ EquipmentSlot equipmentSlot = this.sunProtectionSlot(); ++ ItemStack itemBySlot = this.getItemBySlot(equipmentSlot); ++ if (!itemBySlot.isEmpty()) { ++ if (itemBySlot.isDamageableItem()) { ++ Item item = itemBySlot.getItem(); ++ itemBySlot.setDamageValue(itemBySlot.getDamageValue() + this.random.nextInt(2)); ++ if (itemBySlot.getDamageValue() >= itemBySlot.getMaxDamage()) { ++ this.onEquippedItemBroken(item, equipmentSlot); ++ this.setItemSlot(equipmentSlot, ItemStack.EMPTY); ++ } ++ } ++ ++ flag = false; ++ } ++ ++ if (flag) { ++ if (getRider() == null || !this.isControllable()) // Purpur - ignore mobs which are uncontrollable or without rider - API for any mob to burn daylight ++ this.igniteForSeconds(8.0F); ++ } ++ } ++ } ++ } ++ ++ protected EquipmentSlot sunProtectionSlot() { ++ return net.minecraft.world.entity.EquipmentSlot.HEAD; ++ // Purpur end - copied from Mob - API for any mob to burn daylight + } + + protected void applyInput() { +diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java +index c94a1bd594bcd3b2f7525f1541d2e55897954623..327982be3748e82a36c7a24ede989dacebfb04db 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -544,9 +544,9 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + @Override + public void aiStep() { + super.aiStep(); +- if (this.getType().is(EntityTypeTags.BURN_IN_DAYLIGHT)) { ++ /*if (this.getType().is(EntityTypeTags.BURN_IN_DAYLIGHT)) { // Purpur start - implemented in LivingEntity - API for any mob to burn daylight + this.burnUndead(); +- } ++ }*/ // Purpur end - implemented in LivingEntity - API for any mob to burn daylight + + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("looting"); +@@ -601,19 +601,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + + public boolean isSunBurnTick() { +- if (!this.level().isClientSide() && this.level().environmentAttributes().getValue(EnvironmentAttributes.MONSTERS_BURN, this.position())) { +- float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); +- BlockPos blockPos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); +- boolean flag = this.isInWaterOrRain() || this.isInPowderSnow || this.wasInPowderSnow; +- if (lightLevelDependentMagicValue > 0.5F +- && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F +- && !flag +- && this.level().canSeeSky(blockPos)) { +- return true; +- } +- } +- +- return false; ++ // Purpur - implemented in Entity - API for any mob to burn daylight ++ return super.isSunBurnTick(); + } + + protected Vec3i getPickupReach() { +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 2fa22088ac507eceb36f3c77f24a733076641d8f..ff944ad57d4e9e4a910cd63282e5e7594626365e 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -52,7 +52,7 @@ public class Phantom extends Mob implements Enemy { + Vec3 crystalPosition; // Purpur - Phantoms attracted to crystals and crystals shoot phantoms + // Paper start + public java.util.@Nullable UUID spawningEntity; +- public boolean shouldBurnInDay = true; ++ //public boolean shouldBurnInDay = true; // Purpur - API for any mob to burn daylight + // Paper end + private static final net.minecraft.world.item.crafting.Ingredient TORCH = net.minecraft.world.item.crafting.Ingredient.of(net.minecraft.world.item.Items.TORCH, net.minecraft.world.item.Items.SOUL_TORCH); // Purpur - Phantoms burn in light + +@@ -61,6 +61,7 @@ public class Phantom extends Mob implements Enemy { + this.xpReward = 5; + this.moveControl = new Phantom.PhantomMoveControl(this); + this.lookControl = new Phantom.PhantomLookControl(this); ++ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight + } + + // Purpur start - Ridables +@@ -297,7 +298,7 @@ public class Phantom extends Mob implements Enemy { + this.setPhantomSize(input.getIntOr("size", 0)); + // Paper start + this.spawningEntity = input.read("Paper.SpawningEntity", net.minecraft.core.UUIDUtil.CODEC).orElse(null); +- this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); ++ //this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Purpur - implemented in LivingEntity - API for any mob to burn daylight + // Paper end + } + +@@ -308,7 +309,7 @@ public class Phantom extends Mob implements Enemy { + output.putInt("size", this.getPhantomSize()); + // Paper start + output.storeNullable("Paper.SpawningEntity", net.minecraft.core.UUIDUtil.CODEC, this.spawningEntity); +- output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); ++ //output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - implemented in LivingEntity - API for any mob to burn daylight + // Paper end + } + +diff --git a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +index 1ab860be69bcc1ab5cc07418c2d7e733afdc482b..60afd81d3bf671889fbff5d4a3fabb38a8e2d461 100644 +--- a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +@@ -66,11 +66,12 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + AbstractSkeleton.this.setAggressive(true); + } + }; +- private boolean shouldBurnInDay = true; // Paper - shouldBurnInDay API ++ //private boolean shouldBurnInDay = true; // Paper - shouldBurnInDay API // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight + + protected AbstractSkeleton(EntityType type, Level level) { + super(type, level); + this.reassessWeaponGoal(); ++ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight + } + + @Override +@@ -223,14 +224,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + protected void readAdditionalSaveData(ValueInput input) { + super.readAdditionalSaveData(input); + this.reassessWeaponGoal(); +- this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Paper - shouldBurnInDay API ++ //this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Paper - shouldBurnInDay API // Purpur - implemented in LivingEntity - API for any mob to burn daylight + } + + // Paper start - shouldBurnInDay API + @Override + protected void addAdditionalSaveData(final net.minecraft.world.level.storage.ValueOutput output) { + super.addAdditionalSaveData(output); +- output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); ++ //output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - implemented in LivingEntity - API for any mob to burn daylight + } + // Paper end - shouldBurnInDay API + +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index 928e2c95146bc3fc9b8c41c5b6c3970fc919bb15..31b91dbc3f3e1875fbe6750bb815514686d14f7f 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -27,6 +27,7 @@ import org.jspecify.annotations.Nullable; + public class Husk extends Zombie { + public Husk(EntityType type, Level level) { + super(type, level); ++ this.setShouldBurnInDay(false); // Purpur - API for any mob to burn daylight + } + + // Purpur start - Ridables +@@ -84,7 +85,7 @@ public class Husk extends Zombie { + + @Override + public boolean isSunSensitive() { +- return false; ++ return this.shouldBurnInDay; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight + } + + @Override +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index dfdf9ee98a25ec1f7a1f41326c98155131494d93..d32a831ba76f65c4719c2672ffaec81a861cc7e6 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -92,11 +92,12 @@ public class Zombie extends Monster { + private boolean canBreakDoors = false; + private int inWaterTime = 0; + public int conversionTime; +- private boolean shouldBurnInDay = true; // Paper - Add more Zombie API ++ //private boolean shouldBurnInDay = true; // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight + + public Zombie(EntityType type, Level level) { + super(type, level); + this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(level.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, level.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper - Configurable door breaking difficulty ++ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight + } + + public Zombie(Level level) { +@@ -347,6 +348,7 @@ public class Zombie extends Monster { + // CraftBukkit end + } + ++ public boolean shouldBurnInDay() { return this.isSunSensitive(); } // Purpur - for ABI compatibility - API for any mob to burn daylight + public boolean isSunSensitive() { + return this.shouldBurnInDay; // Paper - Add more Zombie API + } +@@ -486,7 +488,7 @@ public class Zombie extends Monster { + output.putBoolean("CanBreakDoors", this.canBreakDoors()); + output.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1); + output.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1); +- output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API ++ //output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight + } + + @Override +@@ -501,7 +503,7 @@ public class Zombie extends Monster { + } else { + this.getEntityData().set(DATA_DROWNED_CONVERSION_ID, false); + } +- this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Paper - Add more Zombie API ++ //this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight + } + + @Override diff --git a/purpur-server/minecraft-patches/features/0018-Cows-naturally-aggressive-to-players-chance.patch b/purpur-server/minecraft-patches/features/0018-Cows-naturally-aggressive-to-players-chance.patch new file mode 100644 index 000000000..afa3b58ca --- /dev/null +++ b/purpur-server/minecraft-patches/features/0018-Cows-naturally-aggressive-to-players-chance.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Mon, 30 Aug 2021 22:49:53 -0500 +Subject: [PATCH] Cows naturally aggressive to players chance + + +diff --git a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +index ccdd439a89b7e7e10ee960cfe1e5e119d5367799..49292ae53f2b4554064daa4953a8cec00aa1fa98 100644 +--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java ++++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +@@ -135,7 +135,7 @@ public class DefaultAttributes { + .put(EntityType.LLAMA, Llama.createAttributes().build()) + .put(EntityType.MAGMA_CUBE, MagmaCube.createAttributes().build()) + .put(EntityType.MANNEQUIN, LivingEntity.createLivingAttributes().build()) +- .put(EntityType.MOOSHROOM, Cow.createAttributes().build()) ++ .put(EntityType.MOOSHROOM, net.minecraft.world.entity.animal.cow.AbstractCow.createAttributes().build()) // Purpur - Cows naturally aggressive to players chance + .put(EntityType.MULE, AbstractChestedHorse.createBaseChestedHorseAttributes().build()) + .put(EntityType.NAUTILUS, Nautilus.createAttributes().build()) + .put(EntityType.OCELOT, Ocelot.createAttributes().build()) +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index bb8c75d20e6a1300d68ae75233962bdc26736c77..9fa18b190091bbfcb9a65d8d35f68fc7375f504f 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -23,6 +23,8 @@ import net.minecraft.world.level.storage.ValueOutput; + import org.jspecify.annotations.Nullable; + + public class Cow extends AbstractCow { ++ private boolean isNaturallyAggressiveToPlayers; // Purpur - Cows naturally aggressive to players chance ++ + private static final EntityDataAccessor> DATA_VARIANT_ID = SynchedEntityData.defineId(Cow.class, EntityDataSerializers.COW_VARIANT); + + public Cow(EntityType type, Level level) { +@@ -49,8 +51,9 @@ public class Cow extends AbstractCow { + // Purpur start - Configurable entity base attributes + @Override + public void initAttributes() { +- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); +- this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); ++ this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur - Cows naturally aggressive to players chance + } + // Purpur end - Configurable entity base attributes + +@@ -68,6 +71,13 @@ public class Cow extends AbstractCow { + } + // Purpur end - Toggle for water sensitive mob damage + ++ @Override ++ protected void registerGoals() { ++ super.registerGoals(); ++ this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - Cows naturally aggressive to players chance ++ this.targetSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.player.Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Cows naturally aggressive to players chance ++ } ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +@@ -80,6 +90,12 @@ public class Cow extends AbstractCow { + VariantUtils.writeVariant(output, this.getVariant()); + } + ++ // Purpur start - Cows naturally aggressive to players chance ++ public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() { ++ return AbstractCow.createAttributes().add(net.minecraft.world.entity.ai.attributes.Attributes.ATTACK_DAMAGE, 0.0D); ++ } ++ // Purpur end - Cows naturally aggressive to players chance ++ + @Override + protected void readAdditionalSaveData(ValueInput input) { + super.readAdditionalSaveData(input); +@@ -100,6 +116,7 @@ public class Cow extends AbstractCow { + public SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData + ) { ++ this.isNaturallyAggressiveToPlayers = level.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= level.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance; // Purpur - Cows naturally aggressive to players chance + VariantUtils.selectVariantToSpawn(SpawnContext.create(level, this.blockPosition()), Registries.COW_VARIANT).ifPresent(this::setVariant); + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); + } diff --git a/purpur-server/minecraft-patches/features/0019-Mobs-always-drop-experience.patch b/purpur-server/minecraft-patches/features/0019-Mobs-always-drop-experience.patch new file mode 100644 index 000000000..04eb4894b --- /dev/null +++ b/purpur-server/minecraft-patches/features/0019-Mobs-always-drop-experience.patch @@ -0,0 +1,1374 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Tue, 21 Dec 2021 20:40:42 -0600 +Subject: [PATCH] Mobs always drop experience + + +diff --git a/net/minecraft/world/entity/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index e83900f8d6adbe8a48294f250bd6cc6e3fad8160..b642f71d282773afb9fe273cbe65765afa2d5868 100644 +--- a/net/minecraft/world/entity/ambient/Bat.java ++++ b/net/minecraft/world/entity/ambient/Bat.java +@@ -116,6 +116,13 @@ public class Bat extends AmbientCreature { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.batAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isFlapping() { + return !this.isResting() && this.tickCount % 10.0F == 0.0F; +diff --git a/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 947160c4dfc4e50bb75a1a797cb2f22602b22643..e5ac43bf5631254c31d91abb1c5de408807429dd 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -160,6 +160,13 @@ public class Axolotl extends Animal implements Bucketable { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.axolotlAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return 0.0F; +diff --git a/net/minecraft/world/entity/animal/bee/Bee.java b/net/minecraft/world/entity/animal/bee/Bee.java +index e57200d6560a38cbecd681a30b75409412fce170..10dfa28ec46727e60f8a9c90d7e8914aa8b96ea1 100644 +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -499,6 +499,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.beeAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public long getPersistentAngerEndTime() { + return this.entityData.get(DATA_ANGER_END_TIME); +diff --git a/net/minecraft/world/entity/animal/chicken/Chicken.java b/net/minecraft/world/entity/animal/chicken/Chicken.java +index 6308bb606b34d781e315f56a55e6544b3c156d85..341d6ecefb126be61b5f43a73638847e2bf9f125 100644 +--- a/net/minecraft/world/entity/animal/chicken/Chicken.java ++++ b/net/minecraft/world/entity/animal/chicken/Chicken.java +@@ -117,6 +117,13 @@ public class Chicken extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.chickenAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/cow/Cow.java b/net/minecraft/world/entity/animal/cow/Cow.java +index 9fa18b190091bbfcb9a65d8d35f68fc7375f504f..1463e94d3851614caee216b50e90d1980de75298 100644 +--- a/net/minecraft/world/entity/animal/cow/Cow.java ++++ b/net/minecraft/world/entity/animal/cow/Cow.java +@@ -71,6 +71,13 @@ public class Cow extends AbstractCow { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.cowAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/animal/cow/MushroomCow.java b/net/minecraft/world/entity/animal/cow/MushroomCow.java +index a8e6e703a51130066547724dd08bdfe5e11c99e5..3327b93f095b6b6b6d5620bffd03232d4fb47d20 100644 +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -99,6 +99,13 @@ public class MushroomCow extends AbstractCow implements Shearable { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.mooshroomAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public float getWalkTargetValue(BlockPos pos, LevelReader level) { + return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); +diff --git a/net/minecraft/world/entity/animal/dolphin/Dolphin.java b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +index d73789bb8ce0f65be94437484c3ed41e26cd7510..bea132780ef9b8aac23dbd38cebed080082c2a87 100644 +--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java ++++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +@@ -167,6 +167,13 @@ public class Dolphin extends AgeableWaterCreature { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.dolphinAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/equine/Donkey.java b/net/minecraft/world/entity/animal/equine/Donkey.java +index 6cf6853c244b7589d2a57e7ae77933d4a1503ccd..ca02355276286a88d618467d9191d648a3cf7b12 100644 +--- a/net/minecraft/world/entity/animal/equine/Donkey.java ++++ b/net/minecraft/world/entity/animal/equine/Donkey.java +@@ -54,6 +54,13 @@ public class Donkey extends AbstractChestedHorse { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.donkeyAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/Horse.java b/net/minecraft/world/entity/animal/equine/Horse.java +index 1c17ab94901ce1c6370670c06bf0801ee6b5b393..bb7ae0bc176fc873b0e1c760b62c5c4e0ee05ae8 100644 +--- a/net/minecraft/world/entity/animal/equine/Horse.java ++++ b/net/minecraft/world/entity/animal/equine/Horse.java +@@ -88,6 +88,13 @@ public class Horse extends AbstractHorse { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.horseAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void randomizeAttributes(RandomSource random) { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth(random::nextInt)); +diff --git a/net/minecraft/world/entity/animal/equine/Llama.java b/net/minecraft/world/entity/animal/equine/Llama.java +index eb2fc0e6d497241f1b4f3971155b10d14f5427f8..eef4b602bd03df53823da6f576246c330e46d0d4 100644 +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -166,6 +166,13 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.llamaAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public boolean isTraderLlama() { + return false; + } +diff --git a/net/minecraft/world/entity/animal/equine/Mule.java b/net/minecraft/world/entity/animal/equine/Mule.java +index 9bafe3e204b3e4624a95b130552c8230c0835a30..8514c0b7657366a1c40217b950c74c95631c328a 100644 +--- a/net/minecraft/world/entity/animal/equine/Mule.java ++++ b/net/minecraft/world/entity/animal/equine/Mule.java +@@ -53,6 +53,13 @@ public class Mule extends AbstractChestedHorse { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.muleAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public SoundEvent getAmbientSound() { + return SoundEvents.MULE_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +index 3865937e4a56636cde44f7d8f16873ae16512323..2502716b8efe4584fba9d9f9989ba9cba67568bc 100644 +--- a/net/minecraft/world/entity/animal/equine/SkeletonHorse.java ++++ b/net/minecraft/world/entity/animal/equine/SkeletonHorse.java +@@ -81,6 +81,13 @@ public class SkeletonHorse extends AbstractHorse { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.skeletonHorseAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 15.0).add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/animal/equine/TraderLlama.java b/net/minecraft/world/entity/animal/equine/TraderLlama.java +index 4c07cd57aa3518adb4bc9b1213cc572a5b81f935..0515d1071324d82c14be4b9e97c8a583dd45a721 100644 +--- a/net/minecraft/world/entity/animal/equine/TraderLlama.java ++++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java +@@ -84,6 +84,13 @@ public class TraderLlama extends Llama { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.traderLlamaAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isTraderLlama() { + return true; +diff --git a/net/minecraft/world/entity/animal/equine/ZombieHorse.java b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +index b770d3118452b0b7a362b1f541bb602f4d3cbcec..5b671d468698444569b1d97437d746d49c0db757 100644 +--- a/net/minecraft/world/entity/animal/equine/ZombieHorse.java ++++ b/net/minecraft/world/entity/animal/equine/ZombieHorse.java +@@ -95,6 +95,13 @@ public class ZombieHorse extends AbstractHorse { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zombieHorseAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return createBaseHorseAttributes().add(Attributes.MAX_HEALTH, 25.0); + } +diff --git a/net/minecraft/world/entity/animal/feline/Cat.java b/net/minecraft/world/entity/animal/feline/Cat.java +index 92b67565485b2fade3e80fb4b944edfc12d9e0a2..4422cc2f0ed1b603348f3f5c0d6650f871190ffd 100644 +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -142,6 +142,13 @@ public class Cat extends TamableAnimal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.catAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.temptGoal = new Cat.CatTemptGoal(this, 0.6, stack -> stack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/feline/Ocelot.java b/net/minecraft/world/entity/animal/feline/Ocelot.java +index 13272516003f763d2c69dc599c8ff7696bec718b..8583fefcb37dc043293bf622b5a25adbbcc8128e 100644 +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -105,6 +105,13 @@ public class Ocelot extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.ocelotAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public boolean isTrusting() { + return this.entityData.get(DATA_TRUSTING); + } +diff --git a/net/minecraft/world/entity/animal/fish/Cod.java b/net/minecraft/world/entity/animal/fish/Cod.java +index 8655aae805f239cbd049065232293854b18c73cf..05de579a16726454034dc4a913161676b5c73552 100644 +--- a/net/minecraft/world/entity/animal/fish/Cod.java ++++ b/net/minecraft/world/entity/animal/fish/Cod.java +@@ -39,6 +39,13 @@ public class Cod extends AbstractSchoolingFish { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.codAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public ItemStack getBucketItemStack() { + return new ItemStack(Items.COD_BUCKET); +diff --git a/net/minecraft/world/entity/animal/fish/Pufferfish.java b/net/minecraft/world/entity/animal/fish/Pufferfish.java +index 4d21718441b0272774ec69e9b72a180fe417cbb0..ba648a0ac7c60ddd0f5fb5b327c887f58e79a2eb 100644 +--- a/net/minecraft/world/entity/animal/fish/Pufferfish.java ++++ b/net/minecraft/world/entity/animal/fish/Pufferfish.java +@@ -73,6 +73,13 @@ public class Pufferfish extends AbstractFish { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.pufferfishAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/fish/Salmon.java b/net/minecraft/world/entity/animal/fish/Salmon.java +index 424bd56ec9df8bd0a80d2fa79bc4f482eae2ab5c..b76b2c1e7ac93fe01bdf9ee6e3c811f448babcbb 100644 +--- a/net/minecraft/world/entity/animal/fish/Salmon.java ++++ b/net/minecraft/world/entity/animal/fish/Salmon.java +@@ -66,6 +66,13 @@ public class Salmon extends AbstractSchoolingFish { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.salmonAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public int getMaxSchoolSize() { + return 5; +diff --git a/net/minecraft/world/entity/animal/fish/TropicalFish.java b/net/minecraft/world/entity/animal/fish/TropicalFish.java +index 04c30c60c76122018fa69cf1f3f64cae8ed62fad..c7742f06cf2bed3496c28b541e45948e609c1999 100644 +--- a/net/minecraft/world/entity/animal/fish/TropicalFish.java ++++ b/net/minecraft/world/entity/animal/fish/TropicalFish.java +@@ -103,6 +103,13 @@ public class TropicalFish extends AbstractSchoolingFish { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.tropicalFishAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static String getPredefinedName(int variantId) { + return "entity.minecraft.tropical_fish.predefined." + variantId; + } +diff --git a/net/minecraft/world/entity/animal/fox/Fox.java b/net/minecraft/world/entity/animal/fox/Fox.java +index bc119c2f61adb6c6122119fda1df437bab40f9b4..f35f845e3e18c0bb0e4a076298c30be2ae479792 100644 +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -211,6 +211,13 @@ public class Fox extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.foxAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index e5f5bc2c4b4f36e0e911b2c5ef67ef6e0d4cd0b1..64916356de4b9981e04c5befef15b067914f6d75 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -146,6 +146,13 @@ public class Goat extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.goatAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/golem/CopperGolem.java b/net/minecraft/world/entity/animal/golem/CopperGolem.java +index 2d0ce5d420f88a95eda34a3fe81c815999bde300..4f92ac01a6d362a7ef748b74b75773575970859f 100644 +--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java +@@ -147,6 +147,13 @@ public class CopperGolem extends AbstractGolem implements ContainerUser, Shearab + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.copperGolemAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0); + } +diff --git a/net/minecraft/world/entity/animal/golem/IronGolem.java b/net/minecraft/world/entity/animal/golem/IronGolem.java +index d31c1f1b681922ca8f1657ffa333e8a6794e619f..ddde4297b77d174034300165b591a224c0401743 100644 +--- a/net/minecraft/world/entity/animal/golem/IronGolem.java ++++ b/net/minecraft/world/entity/animal/golem/IronGolem.java +@@ -106,6 +106,13 @@ public class IronGolem extends AbstractGolem implements NeutralMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.ironGolemAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options +diff --git a/net/minecraft/world/entity/animal/golem/SnowGolem.java b/net/minecraft/world/entity/animal/golem/SnowGolem.java +index 0fe7f18390490ccf4e944ac3378150ebcc53991d..846b1a064398b455dbb2f05521870b4519edd5b8 100644 +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -87,6 +87,13 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.snowGolemAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +index 828793aaa2992fd1077040309154f814b302476a..8034ce67266b6baab35c72eaebf5ad67fc1a29c6 100644 +--- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java ++++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +@@ -160,6 +160,13 @@ public class HappyGhast extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.happyGhastAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void ageBoundaryReached() { + if (this.isBaby()) { +diff --git a/net/minecraft/world/entity/animal/nautilus/Nautilus.java b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +index 98199869b70c7c0f2b744ac74961c77d473617ca..7c4467933061f097425d6c82188a65fd5e4c4d3b 100644 +--- a/net/minecraft/world/entity/animal/nautilus/Nautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/Nautilus.java +@@ -39,6 +39,13 @@ public class Nautilus extends AbstractNautilus { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.nautilusAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected Brain.Provider brainProvider() { + return NautilusAi.brainProvider(); +diff --git a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +index 94383c5020b6631203ddc4e0a58a222729ffe9a2..7e4d005053e6812f329ab7ac1f252c547d4c9a12 100644 +--- a/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java ++++ b/net/minecraft/world/entity/animal/nautilus/ZombieNautilus.java +@@ -61,6 +61,13 @@ public class ZombieNautilus extends AbstractNautilus { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zombieNautilusAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return AbstractNautilus.createAttributes().add(Attributes.MOVEMENT_SPEED, 1.1F); + } +diff --git a/net/minecraft/world/entity/animal/panda/Panda.java b/net/minecraft/world/entity/animal/panda/Panda.java +index e9aa07dea0515f53a08a7066fa9e23e0ee69d69e..5116e9e16070e23d13b526d21facb7b5ad0988ba 100644 +--- a/net/minecraft/world/entity/animal/panda/Panda.java ++++ b/net/minecraft/world/entity/animal/panda/Panda.java +@@ -157,6 +157,13 @@ public class Panda extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.pandaAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected boolean canDispenserEquipIntoSlot(EquipmentSlot slot) { + return slot == EquipmentSlot.MAINHAND && this.canPickUpLoot(); +diff --git a/net/minecraft/world/entity/animal/parrot/Parrot.java b/net/minecraft/world/entity/animal/parrot/Parrot.java +index e97782bc8232120d4e7d1feeb8ca87fd37a1fcf9..bbe3977939004b7d77e38b14d48c9c26e596d123 100644 +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -221,6 +221,13 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.parrotAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +diff --git a/net/minecraft/world/entity/animal/pig/Pig.java b/net/minecraft/world/entity/animal/pig/Pig.java +index 020932539727739b54ed2f7899cbf11ad940f4df..45142ce558ef28455f9b74ca81d51b3ecdb7a458 100644 +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -102,6 +102,13 @@ public class Pig extends Animal implements ItemSteerable { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.pigAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/polarbear/PolarBear.java b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +index 13d26a6981d6907f35f1f2bde93934c7c1fe8120..592711e205ee959cc66de80933ad5a2dcec3092f 100644 +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -140,6 +140,13 @@ public class PolarBear extends Animal implements NeutralMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.polarBearAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +diff --git a/net/minecraft/world/entity/animal/rabbit/Rabbit.java b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +index b1c72047f7aa63f62b0ad0274811b587b1d3486b..c39004a546c28c9f8af26e727c67dbff85ba1212 100644 +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -170,6 +170,13 @@ public class Rabbit extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.rabbitAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/sheep/Sheep.java b/net/minecraft/world/entity/animal/sheep/Sheep.java +index 7eca49c9237c61ab12f60ff16085aa8fd10c99ac..d712673d237f4e4d99a714ddf09c4582387f0e3c 100644 +--- a/net/minecraft/world/entity/animal/sheep/Sheep.java ++++ b/net/minecraft/world/entity/animal/sheep/Sheep.java +@@ -102,6 +102,13 @@ public class Sheep extends Animal implements Shearable { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.sheepAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.eatBlockGoal = new EatBlockGoal(this); +diff --git a/net/minecraft/world/entity/animal/squid/GlowSquid.java b/net/minecraft/world/entity/animal/squid/GlowSquid.java +index 2aa2a84a7ada369c45a85119a933eb92297af4dc..dd1bd8b1a348a0cc398b123a77d3cb28dd1273ae 100644 +--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java ++++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java +@@ -64,6 +64,13 @@ public class GlowSquid extends Squid { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.glowSquidAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected ParticleOptions getInkParticle() { + return ParticleTypes.GLOW_SQUID_INK; +diff --git a/net/minecraft/world/entity/animal/squid/Squid.java b/net/minecraft/world/entity/animal/squid/Squid.java +index e307ebfb8b2781b6175b520e148e6bd58d2b9dc4..6b5be8e58aa7e3acd14ff3cb1ba25d15f8d37410 100644 +--- a/net/minecraft/world/entity/animal/squid/Squid.java ++++ b/net/minecraft/world/entity/animal/squid/Squid.java +@@ -108,6 +108,13 @@ public class Squid extends AgeableWaterCreature { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.squidAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); +diff --git a/net/minecraft/world/entity/animal/turtle/Turtle.java b/net/minecraft/world/entity/animal/turtle/Turtle.java +index 7d5987fe641646ecbf8e389d57a0a9a04e9c0959..a3f2076e6f0e51ac51a8af94cae91baf82c247a1 100644 +--- a/net/minecraft/world/entity/animal/turtle/Turtle.java ++++ b/net/minecraft/world/entity/animal/turtle/Turtle.java +@@ -126,6 +126,13 @@ public class Turtle extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.turtleAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public void setHomePos(BlockPos homePos) { + this.homePos = homePos; + } +diff --git a/net/minecraft/world/entity/animal/wolf/Wolf.java b/net/minecraft/world/entity/animal/wolf/Wolf.java +index 20f945ee06bcdb4736e6d3a8b20a5cbd3d79df0f..7fbe841818d08fd930f7ce405e84e5ed4badb614 100644 +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -230,6 +230,13 @@ public class Wolf extends TamableAnimal implements NeutralMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.wolfAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index ef6dcc8183d9963d1e683f2cc74fec6443d175a9..fe4362040da72b51c14b3c65f75fe8a72757bab3 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -228,6 +228,13 @@ public class WitherBoss extends Monster implements RangedAttackMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.witherAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +diff --git a/net/minecraft/world/entity/monster/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java +index e2afd7677986c70dd924017e8822fd6abd808f48..187da2d55cdb039b62abe708dadd6c05e13ac64f 100644 +--- a/net/minecraft/world/entity/monster/Blaze.java ++++ b/net/minecraft/world/entity/monster/Blaze.java +@@ -84,6 +84,13 @@ public class Blaze extends Monster { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.blazeAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index b0ed96c243f9abb3868d92bb513ad1620aa8e269..608d787b73dce1726ccece9e0372acf995f261d4 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -273,6 +273,13 @@ public class Creeper extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.creeperAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.CREEPER_HURT; +diff --git a/net/minecraft/world/entity/monster/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index 1ea323a17440abbe36c9528e80e169816267005b..abb7496c293175828fde54ae2b4991aa3ff6b30f 100644 +--- a/net/minecraft/world/entity/monster/ElderGuardian.java ++++ b/net/minecraft/world/entity/monster/ElderGuardian.java +@@ -58,6 +58,13 @@ public class ElderGuardian extends Guardian { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.elderGuardianAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0); + } +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index 47c621f01658f3392b58f3f5c1f31bd539f63dd6..c90a955b14ae10037f31c7e9b8a86df3323602cc 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -114,6 +114,13 @@ public class EnderMan extends Monster implements NeutralMob { + } + // Purpur end - Configurable entity base attributes + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.endermanAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java +index c79c689cb99ad2a6cab88cf8b583ff0f9e91b115..4a778ad22f92e3fd95665a9b40ae932b427bac16 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -79,6 +79,13 @@ public class Endermite extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.endermiteAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index 443eb9f12d6df950c59c9d4db4853087fd23d24e..673d4e06d6dc4c94ddaafcdc86cbdbd686964ddd 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -90,6 +90,13 @@ public class Ghast extends Mob implements Enemy { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.ghastAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index f1176f73d4468b2f39e48fe5454c5f5bf481afd9..9a081135025f114d473c50e68a778e034f33ab82 100644 +--- a/net/minecraft/world/entity/monster/Giant.java ++++ b/net/minecraft/world/entity/monster/Giant.java +@@ -69,6 +69,13 @@ public class Giant extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.giantAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 100.0) +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index 9948511659de9222a19f6b9f660e4441ea98a4a9..60490d0611a352ba0546b39fd41259713a7f18b5 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -104,6 +104,13 @@ public class Guardian extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.guardianAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0); +diff --git a/net/minecraft/world/entity/monster/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index 1bf9d04e5e44272b02753290995795f194cbdae1..765a0d0376e344d2f108a5abe97e654bd972c80b 100644 +--- a/net/minecraft/world/entity/monster/MagmaCube.java ++++ b/net/minecraft/world/entity/monster/MagmaCube.java +@@ -75,6 +75,13 @@ public class MagmaCube extends Slime { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.magmaCubeAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F); + } +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index ff944ad57d4e9e4a910cd63282e5e7594626365e..1f3389185ccf997165dc2caf9f78ec560cfd68bd 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -136,6 +136,13 @@ public class Phantom extends Mob implements Enemy { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.phantomAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isFlapping() { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index ad2913b02ded9b5087c69ba279cebfd81d9ef75e..5e6fd02985eef337f2ff8e48fbd904a064c0560a 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -110,6 +110,13 @@ public class Ravager extends Raider { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.ravagerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Shulker.java b/net/minecraft/world/entity/monster/Shulker.java +index 0a5a6f23cd8e4317db4e7c0ba8883e99f3aff148..2b73382e5bcf47da483ea88e2eae480ebb48e8f9 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -140,6 +140,13 @@ public class Shulker extends AbstractGolem implements Enemy { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.shulkerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Silverfish.java b/net/minecraft/world/entity/monster/Silverfish.java +index 1692e7c93234506e22039da071e9c8e8a8567495..81976287402a701e3ef12c474cb68eb602cc2951 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -72,6 +72,13 @@ public class Silverfish extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.silverfishAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this); +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index c4222e17d9a1f4cba6a74099a167cff13856f3e3..29d6d0c9687b835d24fe51c3824deeb67af1ba4f 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -142,6 +142,13 @@ public class Slime extends Mob implements Enemy { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.slimeAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index 758887f3fcec2bf0946cddb184e19a1c9b64eee3..37b0c8f55ed4ac4feac6a35bd4377007a19ea2ce 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -124,6 +124,13 @@ public class Strider extends Animal implements ItemSteerable { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.striderAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static boolean checkStriderSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Vex.java b/net/minecraft/world/entity/monster/Vex.java +index 6ead673c44891c6b1c1688f80bf299527d7bc777..acd196d435758a8730569ba4e772bb954c4f85b6 100644 +--- a/net/minecraft/world/entity/monster/Vex.java ++++ b/net/minecraft/world/entity/monster/Vex.java +@@ -117,6 +117,13 @@ public class Vex extends Monster implements TraceableEntity { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.vexAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isFlapping() { + return this.tickCount % TICKS_PER_FLAP == 0; +diff --git a/net/minecraft/world/entity/monster/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 86d9952ed0f692e8f229b0c0bcb6cacf53a00c8b..97a427faa37ce9b0c558da40d582b16ac1e96da2 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -89,6 +89,13 @@ public class Witch extends Raider implements RangedAttackMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.witchAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index e2708eb836d60588c1bedb75c520e5007185034a..d6de61ed4415cd00858375406567ea9407030e1e 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -119,6 +119,13 @@ public class Zoglin extends Monster implements HoglinBase { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zoglinAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 5e3f7005a7167c786038823a8437ad2e90a41d2c..460640f5512be6553f1ec69e20e27c7cc797068b 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -136,6 +136,13 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.hoglinAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @VisibleForTesting + public void setTimeInOverworld(int timeInOverworld) { + this.timeInOverworld = timeInOverworld; +diff --git a/net/minecraft/world/entity/monster/illager/Evoker.java b/net/minecraft/world/entity/monster/illager/Evoker.java +index c4f45f6303a46ad54991c9013608a4e5ba8b9fee..1572f7653045902fa05f0499d920eb2b2ff46931 100644 +--- a/net/minecraft/world/entity/monster/illager/Evoker.java ++++ b/net/minecraft/world/entity/monster/illager/Evoker.java +@@ -81,6 +81,13 @@ public class Evoker extends SpellcasterIllager { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.evokerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Illusioner.java b/net/minecraft/world/entity/monster/illager/Illusioner.java +index 32278f51f4973d1294e52f0775697e9f5b295ee0..ea9b61b6f8463edf3497d240b010f8da3e04f62f 100644 +--- a/net/minecraft/world/entity/monster/illager/Illusioner.java ++++ b/net/minecraft/world/entity/monster/illager/Illusioner.java +@@ -93,6 +93,13 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.illusionerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Pillager.java b/net/minecraft/world/entity/monster/illager/Pillager.java +index 6190286ab2f454382d497b4fce47632e6f771215..68fe150d0da04084690670f29a0a4ea035e8c921 100644 +--- a/net/minecraft/world/entity/monster/illager/Pillager.java ++++ b/net/minecraft/world/entity/monster/illager/Pillager.java +@@ -97,6 +97,13 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.pillagerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/illager/Vindicator.java b/net/minecraft/world/entity/monster/illager/Vindicator.java +index 86b463a16e8630af4918ea43a2546995aa00244f..367c74123ee9d07795527eec6ba2f83a9e953081 100644 +--- a/net/minecraft/world/entity/monster/illager/Vindicator.java ++++ b/net/minecraft/world/entity/monster/illager/Vindicator.java +@@ -89,6 +89,13 @@ public class Vindicator extends AbstractIllager { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.vindicatorAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + super.registerGoals(); +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index d6c834914ddea76280466bf0f7d001a76af34f08..49ed463ae7ef65b2630a5b914689e04fa729a40c 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -173,6 +173,13 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.piglinAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index 4bf2038fea712e65f420ade915a18470b79318fc..7baccc23f868ac9b8720bfd77b2af6b7dd368820 100644 +--- a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +@@ -97,6 +97,13 @@ public class PiglinBrute extends AbstractPiglin { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.piglinBruteAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static AttributeSupplier.Builder createAttributes() { + return Monster.createMonsterAttributes() + .add(Attributes.MAX_HEALTH, 50.0) +diff --git a/net/minecraft/world/entity/monster/skeleton/Skeleton.java b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +index 30d79be765118a50e2e2c5bfde6bf6bbc957cf40..03e441e7e9033961dbcf60e574fae48621513c87 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +@@ -57,6 +57,13 @@ public class Skeleton extends AbstractSkeleton { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.skeletonAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/skeleton/Stray.java b/net/minecraft/world/entity/monster/skeleton/Stray.java +index ec19c49d195daff5839c991cce63b1ffc15a7e0b..5cea4ff662c7136d2778b84c21f368cb5ac1c81a 100644 +--- a/net/minecraft/world/entity/monster/skeleton/Stray.java ++++ b/net/minecraft/world/entity/monster/skeleton/Stray.java +@@ -54,6 +54,13 @@ public class Stray extends AbstractSkeleton { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.strayAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + public static boolean checkStraySpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +index 3e8cc658b333f1259784e1426896ac8b8c1537fa..40134013e3254c6c6ed370150adb4bda7c0f2d9a 100644 +--- a/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java +@@ -66,6 +66,13 @@ public class WitherSkeleton extends AbstractSkeleton { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.witherSkeletonAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractPiglin.class, true)); +diff --git a/net/minecraft/world/entity/monster/spider/CaveSpider.java b/net/minecraft/world/entity/monster/spider/CaveSpider.java +index fad3e951e443f04f5bf0423cde42c0ae299a4236..fb4caabbbe5bd0befdaba49ee3cfe70c326b50a7 100644 +--- a/net/minecraft/world/entity/monster/spider/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/spider/CaveSpider.java +@@ -58,6 +58,13 @@ public class CaveSpider extends Spider { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.caveSpiderAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean doHurtTarget(ServerLevel level, Entity target) { + if (super.doHurtTarget(level, target)) { +diff --git a/net/minecraft/world/entity/monster/spider/Spider.java b/net/minecraft/world/entity/monster/spider/Spider.java +index 81c500de41b11d4aa6c52dc290f132ad2c23f009..7993ea937772cabe848726c942b6df0ae52e74e2 100644 +--- a/net/minecraft/world/entity/monster/spider/Spider.java ++++ b/net/minecraft/world/entity/monster/spider/Spider.java +@@ -84,6 +84,13 @@ public class Spider extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.spiderAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/monster/zombie/Drowned.java b/net/minecraft/world/entity/monster/zombie/Drowned.java +index ec36b854dccae7cef905aeb2fcd4ec177828139c..abafb96df26b3d987ce3ec3e1e96e8fc20cb5b6b 100644 +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -133,6 +133,13 @@ public class Drowned extends Zombie implements RangedAttackMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.drownedAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); +diff --git a/net/minecraft/world/entity/monster/zombie/Husk.java b/net/minecraft/world/entity/monster/zombie/Husk.java +index 31b91dbc3f3e1875fbe6750bb815514686d14f7f..ef289dd3fe0be980c200affb0b84a1066fe52232 100644 +--- a/net/minecraft/world/entity/monster/zombie/Husk.java ++++ b/net/minecraft/world/entity/monster/zombie/Husk.java +@@ -83,6 +83,13 @@ public class Husk extends Zombie { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.huskAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isSunSensitive() { + return this.shouldBurnInDay; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight +diff --git a/net/minecraft/world/entity/monster/zombie/Zombie.java b/net/minecraft/world/entity/monster/zombie/Zombie.java +index d32a831ba76f65c4719c2672ffaec81a861cc7e6..f14fc03577a94dc6bd6e536e819a32c2d6144f02 100644 +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -150,6 +150,13 @@ public class Zombie extends Monster { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zombieAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +diff --git a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +index 3da4f757d879f8b855c5d36688f1f5dd50fc88a0..a9a04a4c9cedf33b68c99b2aec12289e63fa156b 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -138,6 +138,13 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zombieVillagerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +index 9a1da88752b0dd708365dd41a074cc0e90f36e1c..528baf4ca71725d4af6876d195b6d75fdab5ca58 100644 +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -113,6 +113,13 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.zombifiedPiglinAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void addBehaviourGoals() { + this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); +diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java +index b6bfe06a2ad8e4219d99de050b30ce1ad15b8b34..996468c21a79d698a8d842189ffd4f2dc86a4302 100644 +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -289,6 +289,13 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.villagerAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public Brain getBrain() { + return (Brain)super.getBrain(); +diff --git a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +index cabebbc25788a8c41912f97dac85af41c9433b68..9502fe0c4f125f5802fde032954c447b14435da2 100644 +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -106,6 +106,13 @@ public class WanderingTrader extends AbstractVillager implements Consumable.Over + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.wanderingTraderAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); diff --git a/purpur-server/minecraft-patches/features/0020-Setting-to-reintroduce-end-void-rings.patch b/purpur-server/minecraft-patches/features/0020-Setting-to-reintroduce-end-void-rings.patch new file mode 100644 index 000000000..0c35dd916 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0020-Setting-to-reintroduce-end-void-rings.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: granny +Date: Mon, 8 Dec 2025 17:02:40 -0800 +Subject: [PATCH] Setting to reintroduce end void rings + + +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index cfd9596246713030f7c0f28a65abeed6dcc8d81b..952444a26fd13ae0b385b2b7f717d965b4c76d19 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1315,6 +1315,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Wed, 17 Dec 2025 20:20:00 -0800 +Subject: [PATCH] lightning transforms blocks + + +diff --git a/net/minecraft/world/entity/LightningBolt.java b/net/minecraft/world/entity/LightningBolt.java +index e81eabf901e755e1254fff98cdd1e609cf07aab8..d971384c64baf6954f92ac9d2c1c9f782b11a6e3 100644 +--- a/net/minecraft/world/entity/LightningBolt.java ++++ b/net/minecraft/world/entity/LightningBolt.java +@@ -38,6 +38,10 @@ public class LightningBolt extends Entity { + public boolean visualOnly; + private @Nullable ServerPlayer cause; + private final Set hitEntities = Sets.newHashSet(); ++ // Purpur start - lightning transforms blocks ++ public List blocks; ++ private BlockPos blockPos; ++ // Purpur end - lightning transforms blocks + private int blocksSetOnFire; + public boolean isEffect; // Paper - Properly handle lightning effects api + +@@ -46,6 +50,10 @@ public class LightningBolt extends Entity { + this.life = 2; + this.seed = this.random.nextLong(); + this.flashes = this.random.nextInt(3) + 1; ++ // Purpur start - lightning transforms blocks ++ this.blocks = this.getTransformedBlocks(); ++ this.blockPos = this.getStrikePosition(); ++ // Purpur end - lightning transforms blocks + } + + public void setVisualOnly(boolean visualOnly) { +@@ -73,6 +81,65 @@ public class LightningBolt extends Entity { + } + } + ++ // Purpur start - lightning transforms blocks ++ private List getTransformedBlocks() { ++ if (!level().purpurConfig.lightningTransformsBlocks) return new java.util.ArrayList<>(); ++ ++ BlockPos blockposition = this.getStrikePosition(); ++ BlockState iblockdata = this.level().getBlockState(blockposition); ++ BlockPos pos; ++ BlockState iblockdata2; ++ ++ if (iblockdata.is(net.minecraft.world.level.block.Blocks.LIGHTNING_ROD)) { ++ pos = blockposition.below(); ++ iblockdata2 = this.level().getBlockState(pos); ++ } else { ++ iblockdata2 = iblockdata; ++ pos = blockposition; ++ } ++ ++ if (level().purpurConfig.lightningTurnsSandIntoGlass && iblockdata2.is(net.minecraft.world.level.block.Blocks.SAND)) { ++ level().setBlock(pos, net.minecraft.world.level.block.Blocks.GLASS.defaultBlockState(), 2); ++ } else if (level().purpurConfig.lightningTurnsStoneIntoObsidian && iblockdata2.is(net.minecraft.world.level.block.Blocks.STONE)) { ++ level().setBlock(pos, net.minecraft.world.level.block.Blocks.OBSIDIAN.defaultBlockState(), 2); ++ } else if (level().purpurConfig.lightningTurnsWaterIntoStone && iblockdata2.is(net.minecraft.world.level.block.Blocks.WATER)) { ++ level().setBlock(pos, net.minecraft.world.level.block.Blocks.STONE.defaultBlockState(), 2); ++ } else if (!level().purpurConfig.lightningTurnsNearbySandIntoGlass) { ++ return new java.util.ArrayList<>(); ++ } ++ ++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(level()); ++ BlockPos.breadthFirstTraversal( ++ pos, ++ level().purpurConfig.lightningTurnsNearbySandIntoGlassMaxDepth, ++ level().purpurConfig.lightningTurnsNearbySandIntoGlassMaxIterations, ++ (validPos, queueAdder) -> { ++ for (net.minecraft.core.Direction direction : net.minecraft.core.Direction.values()) { ++ queueAdder.accept(validPos.relative(direction)); ++ } ++ }, ++ blockPos -> { ++ if (blockPos.equals(pos)) { ++ return BlockPos.TraversalNodeStatus.ACCEPT; ++ } else { ++ BlockState iblockdata3 = blockList.getBlockState(blockPos); ++ ++ if (!iblockdata3.is(net.minecraft.world.level.block.Blocks.SAND)) { // Purpur ++ return BlockPos.TraversalNodeStatus.SKIP; ++ } else { ++ blockList.setBlock(blockPos, net.minecraft.world.level.block.Blocks.GLASS.defaultBlockState(), 3); ++ return BlockPos.TraversalNodeStatus.ACCEPT; ++ } ++ } ++ } ++ ); ++ ++ this.blockPos = pos; // TODO find a way to not need this variable ++ ++ return blockList.getSnapshotBlocks(); ++ } ++ // Purpur end - lightning transforms blocks ++ + @Override + public void tick() { + super.tick(); +@@ -107,6 +174,7 @@ public class LightningBolt extends Entity { + } + + this.powerLightningRod(); ++ if (level().purpurConfig.lightningTransformsBlocks) net.minecraft.world.level.block.SpongeBlock.processBlocksBreadthFirstSearch(level(), this.blockPos, this.blocks, false); // Purpur - lightning transforms blocks + clearCopperOnLightningStrike(this.level(), this.getStrikePosition(), this); // Paper - Call EntityChangeBlockEvent + this.gameEvent(GameEvent.LIGHTNING_STRIKE); + } +diff --git a/net/minecraft/world/level/block/SpongeBlock.java b/net/minecraft/world/level/block/SpongeBlock.java +index 83185c5341fe0033f542520bc1436a31eed27bf5..73d9f24586e960359be9fd8d941c3d2c7584de43 100644 +--- a/net/minecraft/world/level/block/SpongeBlock.java ++++ b/net/minecraft/world/level/block/SpongeBlock.java +@@ -102,13 +102,20 @@ public class SpongeBlock extends Block { + ); + // CraftBukkit start + java.util.List snapshots = blockList.getSnapshotBlocks(); // Is a clone ++ // Purpur start - lightning transforms blocks ++ return processBlocksBreadthFirstSearch(level, pos, snapshots, true); ++ } ++ public static boolean processBlocksBreadthFirstSearch(net.minecraft.world.level.Level level, net.minecraft.core.BlockPos pos, java.util.List snapshots, boolean emitSpongeAbscorbEvent) { ++ // Purpur end - lightning transforms blocks + if (!snapshots.isEmpty()) { + final org.bukkit.block.Block sponge = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + ++ if (emitSpongeAbscorbEvent) { // Purpur - lightning transforms blocks + org.bukkit.event.block.SpongeAbsorbEvent event = new org.bukkit.event.block.SpongeAbsorbEvent(sponge, (java.util.List) (java.util.List) snapshots); + if (!event.callEvent()) { + return false; + } ++ } // Purpur - lightning transforms blocks + + for (org.bukkit.craftbukkit.block.CraftBlockState snapshot : snapshots) { + BlockPos blockPos = snapshot.getPosition(); diff --git a/purpur-server/minecraft-patches/sources/io/papermc/paper/entity/activation/ActivationRange.java.patch b/purpur-server/minecraft-patches/sources/io/papermc/paper/entity/activation/ActivationRange.java.patch new file mode 100644 index 000000000..70f8de868 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/io/papermc/paper/entity/activation/ActivationRange.java.patch @@ -0,0 +1,19 @@ +--- a/io/papermc/paper/entity/activation/ActivationRange.java ++++ b/io/papermc/paper/entity/activation/ActivationRange.java +@@ -147,6 +_,8 @@ + continue; + } + ++ if (!player.level().purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur - AFK API ++ + final int worldHeight = world.getHeight(); + ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange); + ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange); +@@ -288,6 +_,7 @@ + * @return + */ + public static boolean checkIfActive(final Entity entity) { ++ if (entity.level().purpurConfig.squidImmuneToEAR && entity instanceof net.minecraft.world.entity.animal.squid.Squid) return true; // Purpur - Squid EAR immunity + // Never safe to skip fireworks or item gravity + if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Needed for item gravity, see ItemEntity tick + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/CrashReport.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/CrashReport.java.patch new file mode 100644 index 000000000..736446dfb --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/CrashReport.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/CrashReport.java ++++ b/net/minecraft/CrashReport.java +@@ -30,6 +_,7 @@ + private boolean trackingStackTrace = true; + private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; + private final SystemReport systemReport = new SystemReport(); ++ private List extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!", ""); // Purpur - Rebrand + + public CrashReport(String title, Throwable exception) { + io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper +@@ -130,7 +_,7 @@ + } + + public String getFriendlyReport(ReportType type) { +- return this.getFriendlyReport(type, List.of()); ++ return this.getFriendlyReport(type, extraInfo); // Purpur - Rebrand + } + + public @Nullable Path getSaveFile() { +@@ -160,7 +_,7 @@ + } + + public boolean saveToFile(Path path, ReportType type) { +- return this.saveToFile(path, type, List.of()); ++ return this.saveToFile(path, type, extraInfo); // Purpur - Rebrand + } + + public SystemReport getSystemReport() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/commands/CommandSourceStack.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/commands/CommandSourceStack.java.patch new file mode 100644 index 000000000..a94b0ea45 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/commands/CommandSourceStack.java.patch @@ -0,0 +1,53 @@ +--- a/net/minecraft/commands/CommandSourceStack.java ++++ b/net/minecraft/commands/CommandSourceStack.java +@@ -430,6 +_,19 @@ + } + // CraftBukkit end + ++ // Purpur start - Gamemode extra permissions ++ public boolean testPermission(net.minecraft.server.permissions.Permission permission, String bukkitPermission) { ++ if (hasPermission(permission, bukkitPermission)) { ++ return true; ++ } ++ net.kyori.adventure.text.Component permissionMessage = getLevel().getServer().server.permissionMessage(); ++ if (!permissionMessage.equals(net.kyori.adventure.text.Component.empty())) { ++ sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(permissionMessage.replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(bukkitPermission).build()))); ++ } ++ return false; ++ } ++ // Purpur end - Gamemode extra permissions ++ + public Vec3 getPosition() { + return this.worldPosition; + } +@@ -512,6 +_,30 @@ + } + } + } ++ ++ // Purpur start - Purpur config files ++ public void sendSuccess(@Nullable String message) { ++ sendSuccess(message, false); ++ } ++ ++ public void sendSuccess(@Nullable String message, boolean broadcastToOps) { ++ if (message == null) { ++ return; ++ } ++ sendSuccess(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), broadcastToOps); ++ } ++ ++ public void sendSuccess(net.kyori.adventure.text.@Nullable Component message) { ++ sendSuccess(message, false); ++ } ++ ++ public void sendSuccess(net.kyori.adventure.text.@Nullable Component message, boolean broadcastToOps) { ++ if (message == null) { ++ return; ++ } ++ sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps); ++ } ++ // Purpur end - Purpur config files + + public void sendSuccess(Supplier messageSupplier, boolean allowLogging) { + boolean flag = this.source.acceptsSuccess() && !this.silent; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch new file mode 100644 index 000000000..85a32af62 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch @@ -0,0 +1,48 @@ +--- a/net/minecraft/commands/Commands.java ++++ b/net/minecraft/commands/Commands.java +@@ -265,11 +_,11 @@ + JfrCommand.register(this.dispatcher); + } + +- if (SharedConstants.DEBUG_CHASE_COMMAND) { ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDisabledCommands || SharedConstants.DEBUG_CHASE_COMMAND) { // Purpur - register disabled minecraft commands + ChaseCommand.register(this.dispatcher); + } + +- if (SharedConstants.DEBUG_DEV_COMMANDS || SharedConstants.IS_RUNNING_IN_IDE) { ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.DEBUG_DEV_COMMANDS || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur - register minecraft debug commands + RaidCommand.register(this.dispatcher, context); + DebugPathCommand.register(this.dispatcher); + DebugMobSpawningCommand.register(this.dispatcher); +@@ -297,6 +_,14 @@ + StopCommand.register(this.dispatcher); + TransferCommand.register(this.dispatcher); + WhitelistCommand.register(this.dispatcher); ++ org.purpurmc.purpur.command.CreditsCommand.register(this.dispatcher); // Purpur - Add credits command ++ org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - Add demo command ++ org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - Add ping command ++ org.purpurmc.purpur.command.UptimeCommand.register(this.dispatcher); // Purpur - Add uptime command ++ org.purpurmc.purpur.command.TPSBarCommand.register(this.dispatcher); // Purpur - Implement TPSBar ++ org.purpurmc.purpur.command.CompassCommand.register(this.dispatcher); // Purpur - Add compass command ++ org.purpurmc.purpur.command.RamBarCommand.register(this.dispatcher); // Purpur - Add rambar command ++ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur - Add ram command + } + + if (selection.includeIntegrated) { +@@ -522,6 +_,7 @@ + private void runSync(ServerPlayer player, java.util.Collection bukkit, RootCommandNode rootCommandNode) { + // Paper end - Perf: Async command map building + new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, true).callEvent(); // Paper - Brigadier API ++ if (org.bukkit.event.player.PlayerCommandSendEvent.getHandlerList().getRegisteredListeners().length > 0) { // Purpur - Skip events if there's no listeners + org.bukkit.event.player.PlayerCommandSendEvent event = new org.bukkit.event.player.PlayerCommandSendEvent(player.getBukkitEntity(), new java.util.LinkedHashSet<>(bukkit)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + +@@ -532,6 +_,8 @@ + } + } + // CraftBukkit end ++ } // Purpur - Skip events if there's no listeners ++ + player.connection.send(new ClientboundCommandsPacket(rootCommandNode, COMMAND_NODE_INSPECTOR)); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch new file mode 100644 index 000000000..9c23cd46d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch @@ -0,0 +1,45 @@ +--- a/net/minecraft/commands/arguments/selector/EntitySelector.java ++++ b/net/minecraft/commands/arguments/selector/EntitySelector.java +@@ -190,26 +_,27 @@ + this.checkPermissions(source); + if (this.playerName != null) { + ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName); +- return playerByName == null ? List.of() : List.of(playerByName); ++ return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector + } else if (this.entityUUID != null) { + ServerPlayer playerByName = source.getServer().getPlayerList().getPlayer(this.entityUUID); +- return playerByName == null ? List.of() : List.of(playerByName); ++ return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector + } else { + Vec3 vec3 = this.position.apply(source.getPosition()); + AABB absoluteAabb = this.getAbsoluteAabb(vec3); + Predicate predicate = this.getPredicate(vec3, absoluteAabb, null); + if (this.currentEntity) { +- return source.getEntity() instanceof ServerPlayer serverPlayer && predicate.test(serverPlayer) ? List.of(serverPlayer) : List.of(); ++ return source.getEntity() instanceof ServerPlayer serverPlayer && predicate.test(serverPlayer) && canSee(source, serverPlayer) ? List.of(serverPlayer) : List.of(); // Purpur - Hide hidden players from entity selector + } else { + int resultLimit = this.getResultLimit(); + List players; + if (this.isWorldLimited()) { + players = source.getLevel().getPlayers(predicate, resultLimit); ++ players.removeIf(entityplayer3 -> !canSee(source, entityplayer3)); // Purpur - Hide hidden players from entity selector + } else { + players = new ObjectArrayList<>(); + + for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().getPlayers()) { +- if (predicate.test(serverPlayer1)) { ++ if (predicate.test(serverPlayer1) && canSee(source, serverPlayer1)) { // Purpur - Hide hidden players from entity selector + players.add(serverPlayer1); + if (players.size() >= resultLimit) { + return players; +@@ -267,4 +_,10 @@ + public static Component joinNames(List names) { + return ComponentUtils.formatList(names, Entity::getDisplayName); + } ++ ++ // Purpur start - Hide hidden players from entity selector ++ private boolean canSee(CommandSourceStack sender, ServerPlayer target) { ++ return !org.purpurmc.purpur.PurpurConfig.hideHiddenPlayersFromEntitySelector || !(sender.getEntity() instanceof ServerPlayer player) || player.getBukkitEntity().canSee(target.getBukkitEntity()); ++ } ++ // Purpur end - Hide hidden players from entity selector + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/core/BlockPos.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/core/BlockPos.java.patch new file mode 100644 index 000000000..0d1842e69 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/core/BlockPos.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/core/BlockPos.java ++++ b/net/minecraft/core/BlockPos.java +@@ -61,6 +_,12 @@ + public static final int MAX_HORIZONTAL_COORDINATE = 33554431; + // Paper end - Optimize Bit Operations by inlining + ++ // Purpur start - Ridables ++ public BlockPos(net.minecraft.world.entity.Entity entity) { ++ super(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()); ++ } ++ // Purpur end - Ridables ++ + public BlockPos(int x, int y, int z) { + super(x, y, z); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch new file mode 100644 index 000000000..533c3c88a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java ++++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java +@@ -750,5 +_,22 @@ + DispenserBlock.registerBehavior(Items.TNT_MINECART, new MinecartDispenseItemBehavior(EntityType.TNT_MINECART)); + DispenserBlock.registerBehavior(Items.HOPPER_MINECART, new MinecartDispenseItemBehavior(EntityType.HOPPER_MINECART)); + DispenserBlock.registerBehavior(Items.COMMAND_BLOCK_MINECART, new MinecartDispenseItemBehavior(EntityType.COMMAND_BLOCK_MINECART)); ++ // Purpur start - Dispensers place anvils option ++ DispenserBlock.registerBehavior(Items.ANVIL, (new OptionalDispenseItemBehavior() { ++ @Override ++ public ItemStack execute(BlockSource dispenser, ItemStack stack) { ++ net.minecraft.world.level.Level level = dispenser.level(); ++ if (!level.purpurConfig.dispenserPlaceAnvils) return super.execute(dispenser, stack); ++ Direction facing = dispenser.blockEntity().getBlockState().getValue(DispenserBlock.FACING); ++ BlockPos pos = dispenser.pos().relative(facing); ++ BlockState state = level.getBlockState(pos); ++ if (state.isAir()) { ++ level.setBlockAndUpdate(pos, Blocks.ANVIL.defaultBlockState().setValue(net.minecraft.world.level.block.AnvilBlock.FACING, facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : facing.getClockWise())); ++ stack.shrink(1); ++ } ++ return stack; ++ } ++ })); ++ // Purpur end - Dispensers place anvils option + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch new file mode 100644 index 000000000..9a2c1e083 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java ++++ b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java +@@ -32,7 +_,7 @@ + return false; + } else { + LivingEntity livingEntity = entitiesOfClass.getFirst(); +- EquipmentSlot equipmentSlotForItem = livingEntity.getEquipmentSlotForItem(item); ++ EquipmentSlot equipmentSlotForItem = blockSource.level().purpurConfig.dispenserApplyCursedArmor ? livingEntity.getEquipmentSlotForItem(item) : livingEntity.getEquipmentSlotForDispenserItem(item); if (equipmentSlotForItem == null) return false; // Purpur - Dispenser curse of binding protection + ItemStack itemStack = item.copyWithCount(1); // Paper - shrink below and single item in event + // CraftBukkit start + net.minecraft.world.level.Level world = blockSource.level(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/GameTestHelper.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/GameTestHelper.java.patch new file mode 100644 index 000000000..5a40e03dc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/GameTestHelper.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/gametest/framework/GameTestHelper.java ++++ b/net/minecraft/gametest/framework/GameTestHelper.java +@@ -322,6 +_,8 @@ + return gameType; + } + ++ public void setAfk(final boolean afk) {} // Purpur - AFK API ++ + @Override + public boolean isClientAuthoritative() { + return false; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/TestCommand.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/TestCommand.java.patch new file mode 100644 index 000000000..c9f48d7e7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/gametest/framework/TestCommand.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/gametest/framework/TestCommand.java ++++ b/net/minecraft/gametest/framework/TestCommand.java +@@ -443,7 +_,7 @@ + ) + ) + ); +- if (SharedConstants.IS_RUNNING_IN_IDE) { ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur - register minecraft debug commands + literalArgumentBuilder = literalArgumentBuilder.then( + Commands.literal("export") + .then( diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/network/Connection.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/network/Connection.java.patch new file mode 100644 index 000000000..2d95e190f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/network/Connection.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/network/Connection.java ++++ b/net/minecraft/network/Connection.java +@@ -554,11 +_,20 @@ + private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world + private static int joinAttemptsThisTick; // Paper - Buffer joins to world + private static int currTick; // Paper - Buffer joins to world ++ private static int tickSecond; // Purpur - Max joins per second + public void tick() { + this.flushQueue(); + // Paper start - Buffer joins to world + if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) { + Connection.currTick = net.minecraft.server.MinecraftServer.currentTick; ++ // Purpur start - Max joins per second ++ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) { ++ if (++Connection.tickSecond > 20) { ++ Connection.tickSecond = 0; ++ Connection.joinAttemptsThisTick = 0; ++ } ++ } else ++ // Purpur end - Max joins per second + Connection.joinAttemptsThisTick = 0; + } + // Paper end - Buffer joins to world diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch new file mode 100644 index 000000000..4d67b4695 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/network/chat/SignedMessageChain.java ++++ b/net/minecraft/network/chat/SignedMessageChain.java +@@ -44,7 +_,7 @@ + SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink; + if (signedMessageLink == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN); +- } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { ++ } else if (org.purpurmc.purpur.PurpurConfig.kickForOutOfOrderChat && body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { // Purpur - Option to disable kick for out of order chat + this.setChainBroken(); + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes + } else { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/Main.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/Main.java.patch new file mode 100644 index 000000000..2b6f384e7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/Main.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/server/Main.java ++++ b/net/minecraft/server/Main.java +@@ -105,6 +_,13 @@ + JvmProfiler.INSTANCE.start(Environment.SERVER); + } + ++ // Purpur start - Add toggle for enchant level clamping - load config files early ++ org.bukkit.configuration.file.YamlConfiguration purpurConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionSet.valueOf("purpur-settings")); ++ org.purpurmc.purpur.PurpurConfig.clampEnchantLevels = purpurConfiguration.getBoolean("settings.enchantment.clamp-levels", true); ++ org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands = purpurConfiguration.getBoolean("settings.register-minecraft-debug-commands"); // Purpur - register minecraft debug commands ++ org.purpurmc.purpur.PurpurConfig.registerMinecraftDisabledCommands = purpurConfiguration.getBoolean("settings.register-minecraft-disabled-commands"); // Purpur - register disabled minecraft commands ++ // Purpur end - Add toggle for enchant level clamping - load config files early ++ + io.papermc.paper.plugin.PluginInitializerManager.load(optionSet); // Paper + Bootstrap.bootStrap(); + Bootstrap.validate(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/MinecraftServer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/MinecraftServer.java.patch new file mode 100644 index 000000000..9d58a48aa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/MinecraftServer.java.patch @@ -0,0 +1,99 @@ +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -290,6 +_,7 @@ + public joptsimple.OptionSet options; + public org.bukkit.command.ConsoleCommandSender console; + public static int currentTick; // Paper - improve tick loop ++ public static final long startTimeMillis = System.currentTimeMillis(); // Purpur - Add uptime command + public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; + // Paper - don't store the vanilla dispatcher +@@ -306,6 +_,8 @@ + public boolean isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked + private final Set pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping + public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation ++ public boolean lagging = false; // Purpur - Lagging threshold ++ protected boolean upnp = false; // Purpur - UPnP Port Forwarding + // Paper start - improve tick loop + public final ca.spottedleaf.moonrise.common.time.TickData tickTimes1s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)); + public final ca.spottedleaf.moonrise.common.time.TickData tickTimes5s = new ca.spottedleaf.moonrise.common.time.TickData(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L)); +@@ -375,6 +_,7 @@ + public double[] computeTPS() { + final long interval = this.tickRateManager().nanosecondsPerTick(); + return new double[] { ++ getTPS(this.tickTimes5s, interval), // Purpur - Add 5 second tps average in /tps + getTPS(this.tickTimes1m, interval), + getTPS(this.tickTimes5m, interval), + getTPS(this.tickTimes15m, interval) +@@ -1016,6 +_,15 @@ + + LOGGER.info("Stopping server"); + Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing ++ // Purpur start - UPnP Port Forwarding ++ if (upnp) { ++ if (dev.omega24.upnp4j.UPnP4J.close(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) { ++ LOGGER.info("[UPnP] Port {} closed", this.getPort()); ++ } else { ++ LOGGER.error("[UPnP] Failed to close port {}", this.getPort()); ++ } ++ } ++ // Purpur end - UPnP Port Forwarding + // CraftBukkit start + if (this.server != null) { + this.server.spark.disable(); // Paper - spark +@@ -1114,6 +_,8 @@ + this.safeShutdown(waitForShutdown, false); + } + public void safeShutdown(boolean waitForShutdown, boolean isRestarting) { ++ org.purpurmc.purpur.task.BossBarTask.stopAll(); // Purpur - Implement TPSBar ++ org.purpurmc.purpur.task.BeehiveTask.instance().unregister(); // Purpur - Give bee counts in beehives to Purpur clients + this.isRestarting = isRestarting; + this.hasLoggedStop = true; // Paper - Debugging + if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging +@@ -1291,6 +_,16 @@ + } + // Paper end - Add onboarding message for initial server start + ++ // Purpur start - config for startup commands ++ if (!Boolean.getBoolean("Purpur.IReallyDontWantStartupCommands") && !org.purpurmc.purpur.PurpurConfig.startupCommands.isEmpty()) { ++ LOGGER.info("Purpur: Running startup commands specified in purpur.yml."); ++ for (final String startupCommand : org.purpurmc.purpur.PurpurConfig.startupCommands) { ++ LOGGER.info("Purpur: Running the following command: \"{}\"", startupCommand); ++ ((net.minecraft.server.dedicated.DedicatedServer) this).handleConsoleInput(startupCommand, this.createCommandSourceStack()); ++ } ++ } ++ // Purpur end - config for startup commands ++ + while (this.running) { + final long tickStart = System.nanoTime(); // Paper - improve tick loop + long l; // Paper - improve tick loop - diff on change, expect this to be tick interval +@@ -1304,8 +_,10 @@ + final long ticksBehind = Math.max(1L, this.tickSchedule.getPeriodsAhead(l, tickStart)); + final long catchup = (long)Math.max( + 1, +- 5 //ConfigHolder.getConfig().tickLoop.catchupTicks.getOrDefault(MoonriseConfig.TickLoop.DEFAULT_CATCHUP_TICKS).intValue() ++ org.purpurmc.purpur.PurpurConfig.tpsCatchup ? 5 : 1 //ConfigHolder.getConfig().tickLoop.catchupTicks.getOrDefault(MoonriseConfig.TickLoop.DEFAULT_CATCHUP_TICKS).intValue() // Purpur - Configurable TPS Catchup + ); ++ ++ lagging = getTPS()[0] < org.purpurmc.purpur.PurpurConfig.laggingThreshold; // Purpur - Lagging threshold + + // adjust ticksBehind so that it is not greater-than catchup + if (ticksBehind > catchup) { +@@ -1787,7 +_,7 @@ + long worldTime = level.getGameTime(); + final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight); + for (Player entityhuman : level.players()) { +- if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) { ++ if (!(entityhuman instanceof ServerPlayer) || (!level.isForceTime() && (tickCount + entityhuman.getId()) % 20 != 0)) { // Purpur - Configurable daylight cycle + continue; + } + ServerPlayer entityplayer = (ServerPlayer) entityhuman; +@@ -1954,7 +_,7 @@ + + @DontObfuscate + public String getServerModName() { +- return io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper ++ return org.purpurmc.purpur.PurpurConfig.serverModName; // Paper // Purpur - Configurable server mod name + } + + public SystemReport fillSystemReport(SystemReport systemReport) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/PlayerAdvancements.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/PlayerAdvancements.java.patch new file mode 100644 index 000000000..a029c0fb0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/PlayerAdvancements.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/server/PlayerAdvancements.java ++++ b/net/minecraft/server/PlayerAdvancements.java +@@ -146,6 +_,7 @@ + AdvancementHolder advancementHolder = advancementManager.get(path); + if (advancementHolder == null) { + if (!path.getNamespace().equals(Identifier.DEFAULT_NAMESPACE)) return; // CraftBukkit ++ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressIgnoredAdvancementWarnings) // Purpur - Logger settings (suppressing pointless logs) + LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); + } else { + this.startProgress(advancementHolder, progress); +@@ -193,6 +_,7 @@ + advancement.value().display().ifPresent(displayInfo -> { + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent + if (event.message() != null && this.player.level().getGameRules().get(GameRules.SHOW_ADVANCEMENT_MESSAGES)) { ++ if (org.purpurmc.purpur.PurpurConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur - Configurable broadcast settings + this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); + // Paper end + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/EnchantCommand.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/EnchantCommand.java.patch new file mode 100644 index 000000000..6379c1d94 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/EnchantCommand.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/server/commands/EnchantCommand.java ++++ b/net/minecraft/server/commands/EnchantCommand.java +@@ -70,7 +_,7 @@ + + private static int enchant(CommandSourceStack source, Collection targets, Holder enchantment, int level) throws CommandSyntaxException { + Enchantment enchantment1 = enchantment.value(); +- if (level > enchantment1.getMaxLevel()) { ++ if (!org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && level > enchantment1.getMaxLevel()) { // Purpur - Config to allow unsafe enchants + throw ERROR_LEVEL_TOO_HIGH.create(level, enchantment1.getMaxLevel()); + } else { + int i = 0; +@@ -80,7 +_,7 @@ + ItemStack mainHandItem = livingEntity.getMainHandItem(); + if (!mainHandItem.isEmpty()) { + if (enchantment1.canEnchant(mainHandItem) +- && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(mainHandItem).keySet(), enchantment)) { ++ && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(mainHandItem).keySet(), enchantment) || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && !mainHandItem.hasEnchantment(enchantment))) { // Purpur - Config to allow unsafe enchants + mainHandItem.enchant(enchantment, level); + i++; + } else if (targets.size() == 1) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch new file mode 100644 index 000000000..f06984320 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/server/commands/GameModeCommand.java ++++ b/net/minecraft/server/commands/GameModeCommand.java +@@ -53,6 +_,18 @@ + } + + private static int setMode(CommandContext context, Collection players, GameType gameType) { ++ // Purpur start - Gamemode extra permissions ++ if (org.purpurmc.purpur.PurpurConfig.commandGamemodeRequiresPermission) { ++ String gamemode = gameType.getName(); ++ CommandSourceStack sender = context.getSource(); ++ if (!sender.testPermission(Permissions.COMMANDS_GAMEMASTER, "minecraft.command.gamemode." + gamemode)) { ++ return 0; ++ } ++ if (sender.getEntity() instanceof ServerPlayer player && (players.size() > 1 || !players.contains(player)) && !sender.testPermission(Permissions.COMMANDS_GAMEMASTER, "minecraft.command.gamemode." + gamemode + ".other")) { ++ return 0; ++ } ++ } ++ // Purpur end - Gamemode extra permissions + int i = 0; + + for (ServerPlayer serverPlayer : players) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GiveCommand.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GiveCommand.java.patch new file mode 100644 index 000000000..8037f0654 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/commands/GiveCommand.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/commands/GiveCommand.java ++++ b/net/minecraft/server/commands/GiveCommand.java +@@ -68,6 +_,7 @@ + i1 -= min; + ItemStack itemStack1 = item.createItemStack(min, false); + boolean flag = serverPlayer.getInventory().add(itemStack1); ++ if (org.purpurmc.purpur.PurpurConfig.disableGiveCommandDrops) continue; // Purpur - add config option for toggling give command dropping + if (flag && itemStack1.isEmpty()) { + ItemEntity itemEntity = serverPlayer.drop(itemStack, false, false, false, null); // Paper - do not fire PlayerDropItemEvent for /give command + if (itemEntity != null) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch new file mode 100644 index 000000000..8230e207e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -195,6 +_,7 @@ + public void run() { + if (!org.bukkit.craftbukkit.Main.useConsole) return; // CraftBukkit + // Paper start - Use TerminalConsoleAppender ++ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - GUI Improvements - has no GUI or has console (did not double-click) + new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); + /* + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); +@@ -273,6 +_,15 @@ + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics ++ // Purpur start - Purpur config files ++ try { ++ org.purpurmc.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings")); ++ } catch (Exception e) { ++ DedicatedServer.LOGGER.error("Unable to load server configuration", e); ++ return false; ++ } ++ org.purpurmc.purpur.PurpurConfig.registerCommands(); ++ // Purpur end - Purpur config files + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now + + // this.worldData.setGameType(properties.gameMode.get()); // CraftBukkit - moved to world loading +@@ -315,6 +_,30 @@ + if (true) throw new IllegalStateException("Failed to bind to port", var11); // Paper - Propagate failed to bind to port error + return false; + } ++ // Purpur start - UPnP Port Forwarding ++ if (org.purpurmc.purpur.PurpurConfig.useUPnP) { ++ LOGGER.info("[UPnP] Attempting to start UPnP port forwarding service..."); ++ if (dev.omega24.upnp4j.UPnP4J.isUPnPAvailable()) { ++ if (dev.omega24.upnp4j.UPnP4J.isOpen(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) { ++ this.upnp = false; ++ LOGGER.info("[UPnP] Port {} is already open", this.getPort()); ++ } else if (dev.omega24.upnp4j.UPnP4J.open(this.getPort(), dev.omega24.upnp4j.util.Protocol.TCP)) { ++ this.upnp = true; ++ LOGGER.info("[UPnP] Successfully opened port {}", this.getPort()); ++ } else { ++ this.upnp = false; ++ LOGGER.info("[UPnP] Failed to open port {}", this.getPort()); ++ } ++ ++ if (upnp) { ++ LOGGER.info("[UPnP] {}:{}", dev.omega24.upnp4j.UPnP4J.getExternalIP(), this.getPort()); ++ } ++ } else { ++ this.upnp = false; ++ LOGGER.error("[UPnP] Service is unavailable"); ++ } ++ } ++ // Purpur end - UPnP Port Forwarding + + // CraftBukkit start + this.server.loadPlugins(); +@@ -389,6 +_,9 @@ + MinecraftServerStatistics.registerJmxMonitoring(this); + LOGGER.info("JMX monitoring enabled"); + } ++ ++ org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur - Implement TPSBar ++ if (org.purpurmc.purpur.PurpurConfig.beeCountPayload) org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur - Give bee counts in beehives to Purpur clients + + this.notificationManager().serverStarted(); + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch new file mode 100644 index 000000000..1a7ed8c07 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -57,6 +_,7 @@ + public final boolean onlineMode = this.get("online-mode", true); + public final boolean preventProxyConnections = this.get("prevent-proxy-connections", false); + public final String serverIp = this.get("server-ip", ""); ++ public final String serverName = this.get("server-name", "Unknown Server"); // Purpur - Bring back server name + public final Settings.MutableValue allowFlight = this.getMutable("allow-flight", false); + public final Settings.MutableValue motd = this.getMutable("motd", "A Minecraft Server"); + public final boolean codeOfConduct = this.get("enable-code-of-conduct", false); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch new file mode 100644 index 000000000..910e0b882 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch @@ -0,0 +1,135 @@ +--- a/net/minecraft/server/gui/MinecraftServerGui.java ++++ b/net/minecraft/server/gui/MinecraftServerGui.java +@@ -39,6 +_,11 @@ + private Thread logAppenderThread; + private final Collection finalizers = Lists.newArrayList(); + final AtomicBoolean isClosing = new AtomicBoolean(); ++ // Purpur start - GUI Improvements ++ private final CommandHistory history = new CommandHistory(); ++ private String currentCommand = ""; ++ private int historyIndex = 0; ++ // Purpur end - GUI Improvements + + public static MinecraftServerGui showFrameFor(final DedicatedServer server) { + try { +@@ -46,7 +_,7 @@ + } catch (Exception var3) { + } + +- final JFrame jFrame = new JFrame("Minecraft server"); ++ final JFrame jFrame = new JFrame("Purpur Minecraft server"); // Purpur - Improve GUI + final MinecraftServerGui minecraftServerGui = new MinecraftServerGui(server); + jFrame.setDefaultCloseOperation(2); + jFrame.add(minecraftServerGui); +@@ -54,7 +_,7 @@ + jFrame.setLocationRelativeTo(null); + jFrame.setVisible(true); + // Paper start - Improve ServerGUI +- jFrame.setName("Minecraft server"); ++ jFrame.setName("Purpur Minecraft server"); // Purpur - Improve GUI + try { + jFrame.setIconImage(javax.imageio.ImageIO.read(java.util.Objects.requireNonNull(MinecraftServerGui.class.getClassLoader().getResourceAsStream("logo.png")))); + } catch (java.io.IOException ignore) { +@@ -64,7 +_,7 @@ + @Override + public void windowClosing(WindowEvent event) { + if (!minecraftServerGui.isClosing.getAndSet(true)) { +- jFrame.setTitle("Minecraft server - shutting down!"); ++ jFrame.setTitle("Purpur Minecraft server - shutting down!"); // Purpur - Improve GUI + server.halt(true); + minecraftServerGui.runFinalizers(); + } +@@ -112,7 +_,7 @@ + + private JComponent buildChatPanel() { + JPanel jPanel = new JPanel(new BorderLayout()); +- JTextArea jTextArea = new JTextArea(); ++ org.purpurmc.purpur.gui.JColorTextPane jTextArea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur - GUI Improvements + JScrollPane jScrollPane = new JScrollPane(jTextArea, 22, 30); + jTextArea.setEditable(false); + jTextArea.setFont(MONOSPACED); +@@ -121,10 +_,43 @@ + String trimmed = jTextField.getText().trim(); + if (!trimmed.isEmpty()) { + this.server.handleConsoleInput(trimmed, this.server.createCommandSourceStack()); ++ // Purpur start - GUI Improvements ++ history.add(trimmed); ++ historyIndex = -1; ++ // Purpur end - GUI Improvements + } + + jTextField.setText(""); + }); ++ // Purpur start - GUI Improvements ++ jTextField.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up"); ++ jTextField.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down"); ++ jTextField.getActionMap().put("up", new javax.swing.AbstractAction() { ++ @Override ++ public void actionPerformed(java.awt.event.ActionEvent actionEvent) { ++ if (historyIndex < 0) { ++ currentCommand = jTextField.getText(); ++ } ++ if (historyIndex < history.size() - 1) { ++ jTextField.setText(history.get(++historyIndex)); ++ } ++ } ++ }); ++ jTextField.getActionMap().put("down", new javax.swing.AbstractAction() { ++ @Override ++ public void actionPerformed(java.awt.event.ActionEvent actionEvent) { ++ if (historyIndex >= 0) { ++ if (historyIndex == 0) { ++ --historyIndex; ++ jTextField.setText(currentCommand); ++ } else { ++ --historyIndex; ++ jTextField.setText(history.get(historyIndex)); ++ } ++ } ++ } ++ }); ++ // Purpur end - GUI Improvements + jTextArea.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent event) { +@@ -159,7 +_,7 @@ + } + + private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper +- public void print(JTextArea textArea, JScrollPane scrollPane, String line) { ++ public void print(org.purpurmc.purpur.gui.JColorTextPane textArea, JScrollPane scrollPane, String line) { // Purpur - GUI Improvements + if (!SwingUtilities.isEventDispatchThread()) { + SwingUtilities.invokeLater(() -> this.print(textArea, scrollPane, line)); + } else { +@@ -170,16 +_,29 @@ + flag = verticalScrollBar.getValue() + verticalScrollBar.getSize().getHeight() + MONOSPACED.getSize() * 4 > verticalScrollBar.getMaximum(); + } + +- try { ++ /*try { // Purpur - GUI Improvements + document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(line).replaceAll(""), null); // CraftBukkit + } catch (BadLocationException var8) { +- } ++ }*/ // Purpur - GUI Improvements ++ textArea.append(line); // Purpur - GUI Improvements + + if (flag) { + verticalScrollBar.setValue(Integer.MAX_VALUE); + } + } + } ++ ++ // Purpur start - GUI Improvements ++ public static class CommandHistory extends java.util.LinkedList { ++ @Override ++ public boolean add(String command) { ++ if (size() > 1000) { ++ remove(); ++ } ++ return super.offerFirst(command); ++ } ++ } ++ // Purpur end - GUI Improvements + + // Paper start - Add onboarding message for initial server start + private JComponent buildOnboardingPanel() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/StatsComponent.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/StatsComponent.java.patch new file mode 100644 index 000000000..fb8ae8440 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/gui/StatsComponent.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/gui/StatsComponent.java ++++ b/net/minecraft/server/gui/StatsComponent.java +@@ -41,7 +_,7 @@ + } + this.msgs[0] = "Memory use: " + l / 1024L / 1024L + " mb (" + Runtime.getRuntime().freeMemory() * 100L / Runtime.getRuntime().maxMemory() + "% free)"; + this.msgs[1] = "Avg tick: " + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms"; +- this.msgs[2] = "TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg); ++ this.msgs[2] = "TPS from last 5s, 1m, 5m, 15m: " + String.join(", ", tpsAvg); // Purpur - Add 5 second tps average in /tps + // Paper end - Improve ServerGUI + this.values[this.vp++ & 0xFF] = (int)(l * 100L / Runtime.getRuntime().maxMemory()); + this.repaint(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch new file mode 100644 index 000000000..3c37f2002 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -0,0 +1,216 @@ +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -218,6 +_,8 @@ + private final StructureManager structureManager; + private final StructureCheck structureCheck; + private final boolean tickTime; ++ private double preciseTime; // Purpur - Configurable daylight cycle ++ private boolean forceTime; // Purpur - Configurable daylight cycle + private final RandomSequences randomSequences; + final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this); + +@@ -622,8 +_,25 @@ + // CraftBukkit end + this.tickTime = tickTime; + this.server = server; +- this.customSpawners = customSpawners; ++ this.customSpawners = new ArrayList<>(); // Purpur - Allow toggling special MobSpawners per world + this.serverLevelData = serverLevelData; ++ // Purpur start - Allow toggling special MobSpawners per world ++ if (purpurConfig.phantomSpawning) { ++ this.customSpawners.add(new net.minecraft.world.level.levelgen.PhantomSpawner()); ++ } ++ if (purpurConfig.patrolSpawning) { ++ this.customSpawners.add(new net.minecraft.world.level.levelgen.PatrolSpawner()); ++ } ++ if (purpurConfig.catSpawning) { ++ this.customSpawners.add(new net.minecraft.world.entity.npc.CatSpawner()); ++ } ++ if (purpurConfig.villageSiegeSpawning) { ++ this.customSpawners.add(new net.minecraft.world.entity.ai.village.VillageSiege()); ++ } ++ if (purpurConfig.villagerTraderSpawning) { ++ this.customSpawners.add(new net.minecraft.world.entity.npc.wanderingtrader.WanderingTraderSpawner(serverLevelData)); ++ } ++ // Purpur end - Allow toggling special MobSpawners per world + ChunkGenerator chunkGenerator = levelStem.generator(); + // CraftBukkit start + this.serverLevelData.setWorld(this); +@@ -709,6 +_,7 @@ + this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler); + // Paper end - rewrite chunk system + this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit ++ this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle + } + + // Paper start +@@ -760,7 +_,7 @@ + } + + int i = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE); +- if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { ++ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { // Purpur - Config for skipping night + // Paper start - create time skip event - move up calculations + final long newDayTime = this.levelData.getDayTime() + 24000L; + org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent( +@@ -895,6 +_,13 @@ + this.serverLevelData.getScheduledEvents().tick(this.server, l); + Profiler.get().pop(); + if (this.getGameRules().get(GameRules.ADVANCE_TIME)) { ++ // Purpur start - Configurable daylight cycle ++ int incrementTicks = isBrightOutside() ? this.purpurConfig.daytimeTicks : this.purpurConfig.nighttimeTicks; ++ if (incrementTicks != 12000) { ++ this.preciseTime += 12000 / (double) incrementTicks; ++ this.setDayTime(this.preciseTime); ++ } else ++ // Purpur end - Configurable daylight cycle + this.setDayTime(this.levelData.getDayTime() + 1L); + } + } +@@ -902,6 +_,20 @@ + + public void setDayTime(long time) { + this.serverLevelData.setDayTime(time); ++ // Purpur start - Configurable daylight cycle ++ this.preciseTime = time; ++ this.forceTime = false; ++ } ++ public void setDayTime(double i) { ++ this.serverLevelData.setDayTime((long) i); ++ this.forceTime = true; ++ // Purpur end - Configurable daylight cycle ++ } ++ ++ // Purpur start - Configurable daylight cycle ++ public boolean isForceTime() { ++ return this.forceTime; ++ // Purpur end - Configurable daylight cycle + } + + public long getDayCount() { +@@ -1010,9 +_,17 @@ + && this.random.nextDouble() < currentDifficultyAt.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01) // Paper - Configurable spawn chances for skeleton horses + && !this.getBlockState(blockPos.below()).is(BlockTags.LIGHTNING_RODS); + if (flag) { +- SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); ++ // Purpur start - Special mobs naturally spawn ++ net.minecraft.world.entity.animal.equine.AbstractHorse skeletonHorse; ++ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) { ++ skeletonHorse = EntityType.ZOMBIE_HORSE.create(this, EntitySpawnReason.EVENT); ++ } else { ++ skeletonHorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); ++ if (skeletonHorse != null) ((SkeletonHorse) skeletonHorse).setTrap(true); ++ } ++ // Purpur end - Special mobs naturally spawn + if (skeletonHorse != null) { +- skeletonHorse.setTrap(true); ++ //skeletonHorse.setTrap(true); // Purpur - Special mobs naturally spawn - moved up + skeletonHorse.setAge(0); + skeletonHorse.setPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + this.addFreshEntity(skeletonHorse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit +@@ -1047,9 +_,35 @@ + if (blockState.is(Blocks.SNOW)) { + int layersValue = blockState.getValue(SnowLayerBlock.LAYERS); + if (layersValue < Math.min(i, 8)) { ++ // Purpur start - Smooth snow accumulation ++ boolean canSnow = true; ++ // Ensure snow doesn't get more than N layers taller than its neighbors ++ // We only need to check blocks that are taller than the minimum step height ++ if (org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep > 0 && layersValue >= org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep) { ++ int layersValueMin = layersValue - org.purpurmc.purpur.PurpurConfig.smoothSnowAccumulationStep; ++ for (Direction direction : Direction.Plane.HORIZONTAL) { ++ BlockPos blockPosNeighbor = heightmapPos.relative(direction); ++ BlockState blockStateNeighbor = this.getBlockState(blockPosNeighbor); ++ if (blockStateNeighbor.is(Blocks.SNOW)) { ++ // Special check for snow layers, if neighbors are too short, don't accumulate ++ int layersValueNeighbor = blockStateNeighbor.getValue(SnowLayerBlock.LAYERS); ++ if (layersValueNeighbor <= layersValueMin) { ++ canSnow = false; ++ break; ++ } ++ } else if (!Block.isFaceFull(blockStateNeighbor.getCollisionShape(this, blockPosNeighbor), direction.getOpposite())) { ++ // Since our layer is tall enough already, if we have a non-full neighbor block, don't accumulate ++ canSnow = false; ++ break; ++ } ++ } ++ } ++ if (canSnow) { ++ // Purpur end - Smooth snow accumulation + BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, layersValue + 1); + Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, blockState1, 3, null); // CraftBukkit ++ } // Purpur - Smooth snow accumulation + } + } else { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, Blocks.SNOW.defaultBlockState(), 3, null); // CraftBukkit +@@ -1070,7 +_,7 @@ + poiType -> poiType.is(PoiTypes.LIGHTNING_ROD), + blockPos -> blockPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockPos.getX(), blockPos.getZ()) - 1, + pos, +- 128, ++ org.purpurmc.purpur.PurpurConfig.lightningRodRange, // Purpur - Make lightning rod range configurable + PoiManager.Occupancy.ANY + ); + return optional.map(blockPos -> blockPos.above(1)); +@@ -1119,8 +_,26 @@ + int i = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE); + Component component; + if (this.sleepStatus.areEnoughSleeping(i)) { ++ // Purpur start - Customizable sleeping actionbar messages ++ if (org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.isBlank()) { ++ return; ++ } ++ if (!org.purpurmc.purpur.PurpurConfig.sleepSkippingNight.equalsIgnoreCase("default")) { ++ component = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepSkippingNight)); ++ } else ++ // Purpur end - Customizable sleeping actionbar messages + component = Component.translatable("sleep.skipping_night"); + } else { ++ // Purpur start - Customizable sleeping actionbar messages ++ if (org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.isBlank()) { ++ return; ++ } ++ if (!org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent.equalsIgnoreCase("default")) { ++ component = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepingPlayersPercent, ++ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("count", Integer.toString(this.sleepStatus.amountSleeping())), ++ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed("total", Integer.toString(this.sleepStatus.sleepersNeeded(i))))); ++ } else ++ // Purpur end - Customizable sleeping actionbar messages + component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); + } + +@@ -1275,6 +_,7 @@ + @VisibleForTesting + public void resetWeatherCycle() { + // CraftBukkit start ++ if (this.purpurConfig.rainStopsAfterSleep) // Purpur - Option for if rain and thunder should stop on sleep + this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... +@@ -1282,6 +_,7 @@ + this.serverLevelData.setRainTime(0); + } + // CraftBukkit end ++ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur - Option for if rain and thunder should stop on sleep + this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // CraftBukkit start + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. +@@ -1954,7 +_,7 @@ + Explosion.BlockInteraction blockInteraction = switch (explosionInteraction) { + case NONE -> Explosion.BlockInteraction.KEEP; + case BLOCK -> this.getDestroyType(GameRules.BLOCK_EXPLOSION_DROP_DECAY); +- case MOB -> this.getGameRules().get(GameRules.MOB_GRIEFING) ++ case MOB -> ((source instanceof net.minecraft.world.entity.projectile.hurtingprojectile.LargeFireball) ? this.getGameRules().get(GameRules.MOB_GRIEFING, this.purpurConfig.fireballsMobGriefingOverride) : this.getGameRules().get(GameRules.MOB_GRIEFING)) // Purpur - Add mobGriefing override to everything affected + ? this.getDestroyType(GameRules.MOB_EXPLOSION_DROP_DECAY) + : Explosion.BlockInteraction.KEEP; + case TNT -> this.getDestroyType(GameRules.TNT_EXPLOSION_DROP_DECAY); +@@ -2846,7 +_,7 @@ + // Spigot start + if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message + // Paper start - Fix merchant inventory not closing on entity removal +- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { ++ if (!entity.level().purpurConfig.playerVoidTrading && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Purpur - Allow void trading + merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); + } + // Paper end - Fix merchant inventory not closing on entity removal diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch new file mode 100644 index 000000000..e61821aca --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -0,0 +1,283 @@ +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -433,6 +_,9 @@ + public boolean isRealPlayer; // Paper + public com.destroystokyo.paper.event.entity.@Nullable PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent + public org.bukkit.event.player.PlayerQuitEvent.@Nullable QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event ++ private boolean tpsBar = false; // Purpur - Implement TPSBar ++ private boolean compassBar = false; // Purpur - Add compass command ++ private boolean ramBar = false; // Purpur - Implement rambar commands + + // Paper start - rewrite chunk system + private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; +@@ -506,6 +_,9 @@ + this.respawnConfig = input.read("respawn", ServerPlayer.RespawnConfig.CODEC).orElse(null); + this.spawnExtraParticlesOnFall = input.getBooleanOr("spawn_extra_particles_on_fall", false); + this.raidOmenPosition = input.read("raid_omen_position", BlockPos.CODEC).orElse(null); ++ this.tpsBar = input.getBooleanOr("Purpur.TPSBar", false); // Purpur - Implement TPSBar ++ this.compassBar = input.getBooleanOr("Purpur.CompassBar", false); // Purpur - Add compass command ++ this.ramBar = input.getBooleanOr("Purpur.RamBar", false); // Purpur - Implement rambar command + // Paper start - Expand PlayerGameModeChangeEvent + this.loadGameTypes(input); + } +@@ -547,6 +_,9 @@ + output.store("ShoulderEntityRight", CompoundTag.CODEC, this.getShoulderEntityRight()); + } + this.getBukkitEntity().setExtraData(output); // CraftBukkit ++ output.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur - Implement TPSBar ++ output.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - Add compass command ++ output.putBoolean("Purpur.RamBar", this.ramBar); // Purpur - Add rambar command + } + + private void saveParentVehicle(ValueOutput output) { +@@ -1183,6 +_,7 @@ + // Paper - moved up to sendClientboundPlayerCombatKillPacket() + sendClientboundPlayerCombatKillPacket(event.getShowDeathMessages(), deathScreenMessage); // Paper - Expand PlayerDeathEvent + Team team = this.getTeam(); ++ if (org.purpurmc.purpur.PurpurConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(deathMessage); else // Purpur - Configurable broadcast settings + if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) { + this.server.getPlayerList().broadcastSystemMessage(deathMessage, false); + } else if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { +@@ -1290,6 +_,13 @@ + if (this.isInvulnerableTo(level, damageSource)) { + return false; + } else { ++ // Purpur start - Add boat fall damage config ++ if (damageSource.is(net.minecraft.tags.DamageTypeTags.IS_FALL)) { ++ if (getRootVehicle() instanceof net.minecraft.world.entity.vehicle.boat.Boat && !level().purpurConfig.boatsDoFallDamage) { ++ return false; ++ } ++ } ++ // Purpur end - Add boat fall damage config + Entity entity = damageSource.getEntity(); + if (!( // Paper - split the if statement. If below statement is false, hurtServer would not have been evaluated. Return false. + !(entity instanceof Player player && !this.canHarmPlayer(player)) +@@ -1544,6 +_,7 @@ + + profilerFiller.pop(); + profilerFiller.push("placing"); ++ this.portalPos = org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(exit); // Purpur - Fix stuck in portals + this.setServerLevel(level); + this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); // CraftBukkit - use internal teleport without event + this.connection.resetPosition(); +@@ -1652,7 +_,7 @@ + new AABB(vec3.x() - 8.0, vec3.y() - 5.0, vec3.z() - 8.0, vec3.x() + 8.0, vec3.y() + 5.0, vec3.z() + 8.0), + monster -> monster.isPreventingPlayerRest(this.level(), this) + ); +- if (!entitiesOfClass.isEmpty()) { ++ if (!this.level().purpurConfig.playerSleepNearMonsters && !entitiesOfClass.isEmpty()) { // Purpur - Config to ignore nearby mobs when sleeping + return Either.left(Player.BedSleepingProblem.NOT_SAFE); + } + } +@@ -1692,8 +_,19 @@ + CriteriaTriggers.SLEPT_IN_BED.trigger(this); + }); + if (!this.level().canSleepThroughNights()) { +- this.displayClientMessage(Component.translatable("sleep.not_possible"), true); ++ // Purpur start - Customizable sleeping actionbar messages ++ Component clientMessage; ++ if (org.purpurmc.purpur.PurpurConfig.sleepNotPossible.isBlank()) { ++ clientMessage = null; ++ } else if (!org.purpurmc.purpur.PurpurConfig.sleepNotPossible.equalsIgnoreCase("default")) { ++ clientMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.sleepNotPossible)); ++ } else { ++ clientMessage = Component.translatable("sleep.not_possible"); + } ++ if (clientMessage != null) { ++ this.displayClientMessage(clientMessage, true); ++ }// Purpur end - Customizable sleeping actionbar messages ++ } + + this.level().updateSleepingPlayerList(); + return either; +@@ -1784,6 +_,7 @@ + + @Override + public void openTextEdit(SignBlockEntity signEntity, boolean isFrontText) { ++ if (level().purpurConfig.signAllowColors) this.connection.send(signEntity.getTranslatedUpdatePacket(textFilteringEnabled, isFrontText)); // Purpur - Signs allow color codes + this.connection.send(new ClientboundBlockUpdatePacket(this.level(), signEntity.getBlockPos())); + this.connection.send(new ClientboundOpenSignEditorPacket(signEntity.getBlockPos(), isFrontText)); + } +@@ -2121,6 +_,26 @@ + this.lastSentExp = -1; // CraftBukkit - Added to reset + } + ++ // Purpur start - Component related conveniences ++ public void sendActionBarMessage(@Nullable String message) { ++ if (message != null && !message.isEmpty()) { ++ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); ++ } ++ } ++ ++ public void sendActionBarMessage(net.kyori.adventure.text.@Nullable Component message) { ++ if (message != null) { ++ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); ++ } ++ } ++ ++ public void sendActionBarMessage(@Nullable Component message) { ++ if (message != null) { ++ displayClientMessage(message, true); ++ } ++ } ++ // Purpur end - Component related conveniences ++ + @Override + public void displayClientMessage(Component message, boolean overlay) { + this.sendSystemMessage(message, overlay); +@@ -2355,6 +_,20 @@ + ); + } + ++ // Purpur start - Component related conveniences ++ public void sendMiniMessage(@Nullable String message) { ++ if (message != null && !message.isEmpty()) { ++ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); ++ } ++ } ++ ++ public void sendMessage(net.kyori.adventure.text.@Nullable Component message) { ++ if (message != null) { ++ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); ++ } ++ } ++ // Purpur end - Component related conveniences ++ + public void sendSystemMessage(Component message) { + this.sendSystemMessage(message, false); + } +@@ -2492,7 +_,67 @@ + + public void resetLastActionTime() { + this.lastActionTime = Util.getMillis(); +- } ++ this.setAfk(false); // Purpur - AFK API ++ } ++ ++ // Purpur start - AFK API ++ private boolean isAfk = false; ++ ++ @Override ++ public void setAfk(boolean afk) { ++ if (this.isAfk == afk) { ++ return; ++ } ++ ++ String msg = afk ? org.purpurmc.purpur.PurpurConfig.afkBroadcastAway : org.purpurmc.purpur.PurpurConfig.afkBroadcastBack; ++ ++ org.purpurmc.purpur.event.PlayerAFKEvent event = new org.purpurmc.purpur.event.PlayerAFKEvent(this.getBukkitEntity(), afk, this.level().purpurConfig.idleTimeoutKick, msg, !org.bukkit.Bukkit.isPrimaryThread()); ++ if (!event.callEvent() || event.shouldKick()) { ++ return; ++ } ++ ++ this.isAfk = afk; ++ ++ if (!afk) { ++ resetLastActionTime(); ++ } ++ ++ msg = event.getBroadcastMsg(); ++ if (msg != null && !msg.isEmpty()) { ++ String playerName = this.getGameProfile().name(); ++ if (org.purpurmc.purpur.PurpurConfig.afkBroadcastUseDisplayName) { ++ net.kyori.adventure.text.Component playerDisplayNameComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getBukkitEntity().getDisplayName()); ++ playerName = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(playerDisplayNameComponent); ++ } ++ server.getPlayerList().broadcastMiniMessage(String.format(msg, playerName), false); ++ } ++ ++ if (this.level().purpurConfig.idleTimeoutUpdateTabList) { ++ String scoreboardName = getScoreboardName(); ++ String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName()); ++ String[] split = playerListName.split(scoreboardName); ++ String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, ""); ++ String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, ""); ++ if (afk) { ++ getBukkitEntity().setPlayerListName(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix + prefix + scoreboardName + suffix + org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, true); ++ } else { ++ getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix, true); ++ } ++ } ++ ++ ((ServerLevel) this.level()).updateSleepingPlayerList(); ++ } ++ ++ @Override ++ public boolean isAfk() { ++ return this.isAfk; ++ } ++ ++ @Override ++ public boolean canBeCollidedWith(Entity entity) { ++ return !this.isAfk() && super.canBeCollidedWith(entity); ++ } ++ // Purpur end - AFK API + + public ServerStatsCounter getStats() { + return this.stats; +@@ -3128,4 +_,65 @@ + return (org.bukkit.craftbukkit.entity.CraftPlayer) super.getBukkitEntity(); + } + // CraftBukkit end ++ ++ // Purpur start - Add option to teleport to spawn if outside world border ++ public void teleport(org.bukkit.Location to) { ++ this.ejectPassengers(); ++ this.stopRiding(true); ++ ++ if (this.isSleeping()) { ++ this.stopSleepInBed(true, false); ++ } ++ ++ if (this.containerMenu != this.inventoryMenu) { ++ this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT); ++ } ++ ++ ServerLevel toLevel = ((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(); ++ if (this.level() == toLevel) { ++ this.connection.internalTeleport(to); ++ } else { ++ this.teleport(new TeleportTransition( ++ toLevel, ++ org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), ++ Vec3.ZERO, ++ to.getYaw(), ++ to.getPitch(), ++ net.minecraft.world.entity.Relative.ALL, ++ TeleportTransition.DO_NOTHING, ++ org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN ++ )); ++ } ++ } ++ // Purpur end - Add option to teleport to spawn if outside world border ++ ++ // Purpur start - Implement TPSBar ++ public boolean tpsBar() { ++ return this.tpsBar; ++ } ++ ++ public void tpsBar(boolean tpsBar) { ++ this.tpsBar = tpsBar; ++ } ++ // Purpur end - Implement TPSBar ++ ++ // Purpur start - Add compass command ++ public boolean compassBar() { ++ return this.compassBar; ++ } ++ ++ public void compassBar(boolean compassBar) { ++ this.compassBar = compassBar; ++ } ++ // Purpur end - Add compass command ++ ++ // Purpur start - Add rambar command ++ public boolean ramBar() { ++ return this.ramBar; ++ } ++ ++ public void ramBar(boolean ramBar) { ++ this.ramBar = ramBar; ++ } ++ // Purpur end - Add rambar command + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch new file mode 100644 index 000000000..b7c623e53 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -365,6 +_,7 @@ + } + return false; + } ++ if (this.player.level().purpurConfig.slabHalfBreak && this.player.isShiftKeyDown() && blockState.getBlock() instanceof net.minecraft.world.level.block.SlabBlock && ((net.minecraft.world.level.block.SlabBlock) blockState.getBlock()).halfBreak(blockState, pos, this.player)) return true; // Purpur - Break individual slabs when sneaking + } + // CraftBukkit end + +@@ -482,6 +_,7 @@ + public InteractionHand interactHand; + public ItemStack interactItemStack; + public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) { ++ if (shiftClickMended(stack)) return InteractionResult.SUCCESS; // Purpur - Shift right click to use exp for mending + BlockPos blockPos = hitResult.getBlockPos(); + BlockState blockState = level.getBlockState(blockPos); + boolean cancelledBlock = false; +@@ -524,7 +_,7 @@ + boolean flag = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty(); + boolean flag1 = player.isSecondaryUseActive() && flag; + ItemStack itemStack = stack.copy(); +- if (!flag1) { ++ if (!flag1 || (player.level().purpurConfig.composterBulkProcess && blockState.is(net.minecraft.world.level.block.Blocks.COMPOSTER))) { // Purpur - Sneak to bulk process composter + InteractionResult interactionResult = blockState.useItemOn(player.getItemInHand(hand), level, player, hand, hitResult); + if (interactionResult.consumesAction()) { + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); +@@ -570,4 +_,18 @@ + public void setLevel(ServerLevel level) { + this.level = level; + } ++ ++ // Purpur start - Shift right click to use exp for mending ++ public boolean shiftClickMended(ItemStack itemstack) { ++ if (this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints > 0 && this.player.isShiftKeyDown() && this.player.getBukkitEntity().hasPermission("purpur.mending_shift_click")) { ++ int points = Math.min(this.player.totalExperience, this.player.level().purpurConfig.shiftRightClickRepairsMendingPoints); ++ if (points > 0 && itemstack.isDamaged() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.MENDING, itemstack) > 0) { ++ this.player.giveExperiencePoints(itemstack.getDamageValue() == 1 ? -2 : -points); ++ this.player.level().addFreshEntity(new net.minecraft.world.entity.ExperienceOrb(this.player.level(), this.player.getX(), this.player.getY(), this.player.getZ(), itemstack.getDamageValue() == 1 ? 1 : points, org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN, this.player, this.player)); ++ return true; ++ } ++ } ++ return false; ++ } ++ // Purpur end - Shift right click to use exp for mending + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch new file mode 100644 index 000000000..a68b41c11 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/level/WorldGenRegion.java ++++ b/net/minecraft/server/level/WorldGenRegion.java +@@ -311,6 +_,7 @@ + return true; + } else { + // Paper start - Buffer OOB setBlock calls ++ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressSetBlockFarChunk) // Purpur - Logger settings (suppressing pointless logs) + if (!hasSetFarWarned) { + Util.logAndPauseIfInIde( + "Detected setBlock in a far chunk [" diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch new file mode 100644 index 000000000..eb1276339 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -0,0 +1,82 @@ +--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -40,10 +_,11 @@ + public final Connection connection; // Paper + private final boolean transferred; + //private long keepAliveTime; // Paper - improve keepalives +- //private boolean keepAlivePending; // Paper - improve keepalives ++ private boolean keepAlivePending; // Paper - improve keepalives // Purpur - Alternative Keepalive Handling + //private long keepAliveChallenge; // Paper - improve keepalives + private long closedListenerTime; + private boolean closed = false; ++ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur - Alternative Keepalive Handling + private volatile int latency; // Paper - improve keepalives - make volatile + private final io.papermc.paper.util.KeepAlive keepAlive; // Paper - improve keepalives + private volatile boolean suspendFlushingOnServerThread = false; +@@ -54,6 +_,10 @@ + public final java.util.Map packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks + private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit + protected static final net.minecraft.resources.Identifier MINECRAFT_BRAND = net.minecraft.resources.Identifier.withDefaultNamespace("brand"); // Paper - Brand support ++ // Purpur start - Purpur client support ++ protected static final net.minecraft.resources.Identifier PURPUR_CLIENT = net.minecraft.resources.Identifier.fromNamespaceAndPath("purpur", "client"); ++ public boolean purpurClient; ++ // Purpur end - Purpur client support + // Paper start - retain certain values + public @Nullable String playerBrand; + public final java.util.Set pluginMessagerChannels; +@@ -105,6 +_,18 @@ + // Paper start - improve keepalives + long now = System.nanoTime(); + io.papermc.paper.util.KeepAlive.PendingKeepAlive pending = this.keepAlive.pendingKeepAlives.peek(); ++ // Purpur start - Alternative Keepalive Handling ++ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { ++ if (this.keepAlivePending && !keepAlives.isEmpty() && keepAlives.contains(packet.getId())) { ++ int ping = (int) (Util.getMillis() - packet.getId()); ++ int updatedLatency = (this.latency * 3 + ping) / 4; ++ this.latency = updatedLatency; ++ this.keepAlivePending = false; ++ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest ++ } ++ return; ++ } else ++ // Purpur end - Alternative Keepalive Handling + if (pending != null && pending.challengeId() == packet.getId()) { + this.keepAlive.pendingKeepAlives.remove(pending); + +@@ -179,6 +_,12 @@ + return; + } + ++ // Purpur start - Purpur client support ++ if (identifier.equals(PURPUR_CLIENT)) { ++ this.purpurClient = true; ++ } ++ // Purpur end - Purpur client support ++ + if (identifier.equals(MINECRAFT_BRAND)) { + this.playerBrand = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256); + } +@@ -264,6 +_,23 @@ + Profiler.get().push("keepAlive"); + long millis = Util.getMillis(); + // Paper start - improve keepalives ++ // Purpur start - Alternative Keepalive Handling ++ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { ++ if (this.checkIfClosed(millis) && !this.processedDisconnect) { ++ long currTime = System.nanoTime(); ++ if ((currTime - this.keepAlive.lastKeepAliveTx) >= java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)) { // 1 second ++ this.keepAlive.lastKeepAliveTx = currTime; ++ if (this.keepAlivePending && !this.processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { ++ this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); ++ } else if (this.checkIfClosed(millis)) { ++ this.keepAlivePending = true; ++ this.keepAlives.add(millis); // currentTime is ID ++ this.send(new ClientboundKeepAlivePacket(millis)); ++ } ++ } ++ } ++ } else ++ // Purpur end - Alternative Keepalive Handling + if (this.checkIfClosed(millis) && !this.processedDisconnect) { + long currTime = System.nanoTime(); + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch new file mode 100644 index 000000000..06fd70e60 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -0,0 +1,233 @@ +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -346,6 +_,20 @@ + } + // Paper end - configuration phase API + ++ // Purpur start - AFK API ++ private final com.google.common.cache.LoadingCache kickPermissionCache = com.google.common.cache.CacheBuilder.newBuilder() ++ .maximumSize(1000) ++ .expireAfterWrite(1, java.util.concurrent.TimeUnit.MINUTES) ++ .build( ++ new com.google.common.cache.CacheLoader<>() { ++ @Override ++ public Boolean load(org.bukkit.craftbukkit.entity.CraftPlayer player) { ++ return player.hasPermission("purpur.bypassIdleKick"); ++ } ++ } ++ ); ++ // Purpur end - AFK API ++ + @Override + public void tick() { + if (this.ackBlockChangesUpTo > -1) { +@@ -363,6 +_,12 @@ + && this.server.playerIdleTimeout() > 0 + && Util.getMillis() - this.player.getLastActionTime() > TimeUnit.MINUTES.toMillis(this.server.playerIdleTimeout()) + && !this.player.wonGame) { ++ // Purpur start - AFK API ++ this.player.setAfk(true); ++ if (!this.player.level().purpurConfig.idleTimeoutKick || (!Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")) && kickPermissionCache.getUnchecked(this.player.getBukkitEntity()))) { ++ return; ++ } ++ // Purpur end - AFK API + this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause + } + } +@@ -682,6 +_,8 @@ + this.lastYaw = to.getYaw(); + this.lastPitch = to.getPitch(); + ++ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur - AFK API ++ + Location oldTo = to.clone(); + PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); + this.cserver.getPluginManager().callEvent(event); +@@ -761,6 +_,7 @@ + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (packet.getId() == this.awaitingTeleport) { + if (this.awaitingPositionFromClient == null) { ++ ServerGamePacketListenerImpl.LOGGER.warn("Disconnected on accept teleport packet. Was not expecting position data from client at this time"); // Purpur - Add more logger output for invalid movement kicks + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause + return; + } +@@ -1301,6 +_,10 @@ + final int maxBookPageSize = pageMax.intValue(); + final double multiplier = Math.clamp(io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier, 0.3D, 1D); + long byteAllowed = maxBookPageSize; ++ // Purpur start - PlayerBookTooLargeEvent ++ int slot = packet.slot(); ++ ItemStack itemstack = Inventory.isHotbarSlot(slot) || slot == Inventory.SLOT_OFFHAND ? this.player.getInventory().getItem(slot) : ItemStack.EMPTY; ++ // Purpur end - PlayerBookTooLargeEvent + for (final String page : pageList) { + final int byteLength = page.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; + byteTotal += byteLength; +@@ -1325,7 +_,8 @@ + } + + if (byteTotal > byteAllowed) { +- ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size()); ++ ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send too large of a book. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size()); ++ org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent event = new org.purpurmc.purpur.event.player.PlayerBookTooLargeEvent(player.getBukkitEntity(), itemstack.asBukkitCopy()); if (event.shouldKickPlayer()) // Purpur - PlayerBookTooLargeEvent + this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect + return; + } +@@ -1344,31 +_,45 @@ + Optional optional = packet.title(); + optional.ifPresent(list::add); + list.addAll(packet.pages()); ++ // Purpur start - Allow color codes in books ++ boolean hasEditPerm = getCraftPlayer().hasPermission("purpur.book.color.edit"); ++ boolean hasSignPerm = hasEditPerm || getCraftPlayer().hasPermission("purpur.book.color.sign"); ++ // Purpur end - Allow color codes in books + Consumer> consumer = optional.isPresent() +- ? texts -> this.signBook(texts.get(0), texts.subList(1, texts.size()), slot) +- : list1 -> this.updateBookContents(list1, slot); ++ ? texts -> this.signBook(texts.get(0), texts.subList(1, texts.size()), slot, hasSignPerm) // Purpur - Allow color codes in books ++ : list1 -> this.updateBookContents(list1, slot, hasEditPerm); // Purpur - Allow color codes in books + this.filterTextPacket(list).thenAcceptAsync(consumer, this.server); + } + } + + private void updateBookContents(List pages, int index) { ++ // Purpur start - Allow color codes in books ++ updateBookContents(pages, index, false); ++ } ++ private void updateBookContents(List pages, int index, boolean hasPerm) { ++ // Purpur end - Allow color codes in books + // CraftBukkit start + ItemStack handItem = this.player.getInventory().getItem(index); + ItemStack item = handItem.copy(); + // CraftBukkit end + if (item.has(DataComponents.WRITABLE_BOOK_CONTENT)) { +- List> list = pages.stream().map(this::filterableFromOutgoing).toList(); ++ List> list = pages.stream().map(filteredText -> filterableFromOutgoing(filteredText).map(s -> color(s, hasPerm))).toList(); // Purpur - Allow color codes in books + item.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list)); + this.player.getInventory().setItem(index, CraftEventFactory.handleEditBookEvent(this.player, index, handItem, item)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) + } + } + + private void signBook(FilteredText title, List pages, int index) { ++ // Purpur start - Allow color codes in books ++ signBook(title, pages, index, false); ++ } ++ private void signBook(FilteredText title, List pages, int index, boolean hasPerm) { ++ // Purpur end - Allow color codes in books + ItemStack item = this.player.getInventory().getItem(index); + if (item.has(DataComponents.WRITABLE_BOOK_CONTENT)) { + ItemStack itemStack = item.transmuteCopy(Items.WRITTEN_BOOK); + itemStack.remove(DataComponents.WRITABLE_BOOK_CONTENT); +- List> list = pages.stream().map(filteredText -> this.filterableFromOutgoing(filteredText).map(Component::literal)).toList(); ++ List> list = pages.stream().map((filteredText) -> this.filterableFromOutgoing(filteredText).map(s -> hexColor(s, hasPerm))).toList(); // Purpur - Allow color codes in books + itemStack.set( + DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getPlainTextName(), 0, list, true) + ); +@@ -1381,6 +_,16 @@ + return this.player.isTextFilteringEnabled() ? Filterable.passThrough(filteredText.filteredOrEmpty()) : Filterable.from(filteredText); + } + ++ // Purpur start - Allow color codes in books ++ private Component hexColor(String str, boolean hasPerm) { ++ return hasPerm ? PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().deserialize(str)) : Component.literal(str); ++ } ++ ++ private String color(String str, boolean hasPerm) { ++ return hasPerm ? org.bukkit.ChatColor.color(str, false) : str; ++ } ++ // Purpur end - Allow color codes in books ++ + @Override + public void handleEntityTagQuery(ServerboundEntityTagQueryPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); +@@ -1420,7 +_,15 @@ + @Override + public void handleMovePlayer(ServerboundMovePlayerPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); +- if (containsInvalidValues(packet.getX(0.0), packet.getY(0.0), packet.getZ(0.0), packet.getYRot(0.0F), packet.getXRot(0.0F))) { ++ // Purpur start - Add more logger output for invalid movement kicks ++ boolean invalidX = Double.isNaN(packet.getX(0.0)); ++ boolean invalidY = Double.isNaN(packet.getY(0.0)); ++ boolean invalidZ = Double.isNaN(packet.getZ(0.0)); ++ boolean invalidYaw = !Floats.isFinite(packet.getYRot(0.0F)); ++ boolean invalidPitch = !Floats.isFinite(packet.getXRot(0.0F)); ++ if (invalidX || invalidY || invalidZ || invalidYaw || invalidPitch) { ++ ServerGamePacketListenerImpl.LOGGER.warn(String.format("Disconnected on move player packet. Invalid data: x=%b, y=%b, z=%b, yaw=%b, pitch=%b", invalidX, invalidY, invalidZ, invalidYaw, invalidPitch)); ++ // Purpur end - Add more logger output for invalid movement kicks + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause + } else { + ServerLevel serverLevel = this.player.level(); +@@ -1603,7 +_,7 @@ + movedWrongly = true; + if (event.getLogWarning()) + // Paper end +- LOGGER.warn("{} moved wrongly!", this.player.getPlainTextName()); ++ LOGGER.warn("{} moved wrongly!, ({})", this.player.getPlainTextName(), verticalDelta); // Purpur - AFK API + } // Paper + } + +@@ -1668,6 +_,8 @@ + this.lastYaw = to.getYaw(); + this.lastPitch = to.getPitch(); + ++ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur - AFK API ++ + Location oldTo = to.clone(); + PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); + this.cserver.getPluginManager().callEvent(event); +@@ -1723,6 +_,13 @@ + this.player.tryResetCurrentImpulseContext(); + } + ++ // Purpur start - Dont run with scissors! ++ if (this.player.level().purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.level().purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.level().purpurConfig.ignoreScissorsInLava && this.player.isInLava()) && (isScissors(this.player.getItemInHand(InteractionHand.MAIN_HAND)) || isScissors(this.player.getItemInHand(InteractionHand.OFF_HAND))) && (int) (Math.random() * 10) == 0) { ++ this.player.hurtServer(this.player.level(), this.player.damageSources().scissors(), (float) this.player.level().purpurConfig.scissorsRunningDamage); ++ if (!org.purpurmc.purpur.PurpurConfig.dontRunWithScissors.isBlank()) this.player.sendActionBarMessage(org.purpurmc.purpur.PurpurConfig.dontRunWithScissors); ++ } ++ // Purpur end - Dont run with scissors! ++ + this.player.checkMovementStatistics(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z); + this.lastGoodX = this.player.getX(); + this.lastGoodY = this.player.getY(); +@@ -1740,6 +_,17 @@ + } + } + ++ // Purpur start - Dont run with scissors! ++ public boolean isScissors(ItemStack stack) { ++ if (!stack.is(Items.SHEARS)) return false; ++ ++ Identifier itemModelReference = stack.get(net.minecraft.core.component.DataComponents.ITEM_MODEL); ++ if (itemModelReference != null && itemModelReference.equals(this.player.level().purpurConfig.dontRunWithScissorsItemModelReference)) return true; ++ ++ return stack.getOrDefault(DataComponents.CUSTOM_MODEL_DATA, net.minecraft.world.item.component.CustomModelData.EMPTY).equals(net.minecraft.world.item.component.CustomModelData.EMPTY); ++ } ++ // Purpur end - Dont run with scissors! ++ + private boolean shouldCheckPlayerMovement(boolean isElytraMovement) { + if (this.isSingleplayerOwner()) { + return false; +@@ -2154,6 +_,7 @@ + + boolean cancelled; + if (hitResult == null || hitResult.getType() != HitResult.Type.BLOCK) { ++ if (this.player.gameMode.shiftClickMended(itemInHand)) return; // Purpur - Shift right click to use exp for mending + org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemInHand, hand); + cancelled = event.useItemInHand() == Event.Result.DENY; + } else { +@@ -2800,6 +_,7 @@ + + AABB boundingBox = target.getBoundingBox(); + if (packet.isWithinRange(this.player, boundingBox, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience value for interact range ++ if (target instanceof net.minecraft.world.entity.Mob mob) mob.ticksSinceLastInteraction = 0; // Purpur - Entity lifespan + packet.dispatch( + new ServerboundInteractPacket.Handler() { + private void performInteraction(InteractionHand hand, ServerGamePacketListenerImpl.EntityInteraction entityInteraction, PlayerInteractEntityEvent event) { // CraftBukkit +@@ -3545,7 +_,7 @@ + @Override + public void handleChangeGameMode(ServerboundChangeGameModePacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); +- if (!GameModeCommand.PERMISSION_CHECK.check(this.player.permissions())) { ++ if (!GameModeCommand.PERMISSION_CHECK.check(this.player.permissions()) && !player.getBukkitEntity().hasPermission("purpur.debug.f3n")) { // Purpur - Add permission for F3+N debug + LOGGER.warn( + "Player {} tried to change game mode to {} without required permissions", + this.player.getGameProfile().name(), diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch new file mode 100644 index 000000000..ec161a6f4 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -278,7 +_,7 @@ + ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); + ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(string1)); // Spigot + } else { +- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); ++ ServerLoginPacketListenerImpl.this.disconnect(org.purpurmc.purpur.PurpurConfig.unverifiedUsername.equals("default") ? Component.translatable("multiplayer.disconnect.unverified_username") : io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.purpurmc.purpur.PurpurConfig.unverifiedUsername))); // Purpur - Config for unverified username message + ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", string1); + } + } catch (AuthenticationUnavailableException var4) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerStatusPacketListenerImpl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerStatusPacketListenerImpl.java.patch new file mode 100644 index 000000000..3fc9d014a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerStatusPacketListenerImpl.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/network/ServerStatusPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerStatusPacketListenerImpl.java +@@ -37,6 +_,7 @@ + } else { + this.hasRequestedStatus = true; + // this.connection.send(new ClientboundStatusResponsePacket(this.status)); // Paper ++ if (net.minecraft.server.MinecraftServer.getServer().getStatus().version().isEmpty()) return; // Purpur - Fix 'outdated server' showing in ping before server fully boots - do not respond to pings before we know the protocol version + com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(net.minecraft.server.MinecraftServer.getServer(), this.connection); // Paper - handle status request + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch new file mode 100644 index 000000000..4b9176bcd --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -0,0 +1,56 @@ +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -311,6 +_,7 @@ + scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); + } + // Paper end - Configurable player collision ++ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur - Implement TPSBar + // CraftBukkit start - moved down + LOGGER.info( + "{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", // CraftBukkit - add world name +@@ -431,6 +_,7 @@ + } + public net.kyori.adventure.text.@Nullable Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) { + // Paper end - Fix kick event leave message not being sent ++ org.purpurmc.purpur.task.BossBarTask.removeFromAll(player.getBukkitEntity()); // Purpur - Implement TPSBar + ServerLevel serverLevel = player.level(); + player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it +@@ -770,6 +_,20 @@ + } + } + ++ // Purpur start - Component related conveniences ++ public void broadcastMiniMessage(@Nullable String message, boolean overlay) { ++ if (message != null && !message.isEmpty()) { ++ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay); ++ } ++ } ++ ++ public void broadcastMessage(net.kyori.adventure.text.@Nullable Component message, boolean overlay) { ++ if (message != null) { ++ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay); ++ } ++ } ++ // Purpur end - Component related conveniences ++ + public void broadcastAll(Packet packet, ResourceKey dimension) { + for (ServerPlayer serverPlayer : this.players) { + if (serverPlayer.level().dimension() == dimension) { +@@ -863,6 +_,7 @@ + case ADMINS -> EntityEvent.PERMISSION_LEVEL_ADMINS; + case OWNERS -> EntityEvent.PERMISSION_LEVEL_OWNERS; + }; ++ if (b < EntityEvent.PERMISSION_LEVEL_OWNERS && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b = EntityEvent.PERMISSION_LEVEL_OWNERS; // Purpur - Add permission for F3+N debug + player.connection.send(new ClientboundEntityEventPacket(player, b)); + } + +@@ -874,7 +_,7 @@ + + // Paper start - whitelist verify event / login event + public LoginResult canBypassFullServerLogin(final NameAndId nameAndId, final LoginResult currentResult) { +- final boolean shouldKick = this.players.size() >= this.getMaxPlayers() && !this.canBypassPlayerLimit(nameAndId); ++ final boolean shouldKick = this.players.size() >= this.getMaxPlayers() && !(/*player.hasPermission("purpur.joinfullserver") || */this.canBypassPlayerLimit(nameAndId)); // Purpur - Allow player join full server by permission TODO: this hasn't worked for a while, so comment it out until we can reliably check perms of the player joining + final io.papermc.paper.event.player.PlayerServerFullCheckEvent fullCheckEvent = new io.papermc.paper.event.player.PlayerServerFullCheckEvent( + new com.destroystokyo.paper.profile.CraftPlayerProfile(nameAndId), + io.papermc.paper.adventure.PaperAdventure.asAdventure(currentResult.message), diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/server/players/SleepStatus.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/SleepStatus.java.patch new file mode 100644 index 000000000..998d2f057 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/players/SleepStatus.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/server/players/SleepStatus.java ++++ b/net/minecraft/server/players/SleepStatus.java +@@ -15,7 +_,7 @@ + + public boolean areEnoughDeepSleeping(int requiredSleepPercentage, List sleepingPlayers) { + // CraftBukkit start +- int i = (int) sleepingPlayers.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping).count(); ++ int i = (int) sleepingPlayers.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping || (player.level().purpurConfig.idleTimeoutCountAsSleeping && player.isAfk())).count(); // Purpur - AFK API + boolean anyDeepSleep = sleepingPlayers.stream().anyMatch(Player::isSleepingLongEnough); + return anyDeepSleep && i >= this.sleepersNeeded(requiredSleepPercentage); + // CraftBukkit end +@@ -43,7 +_,7 @@ + for (ServerPlayer serverPlayer : players) { + if (!serverPlayer.isSpectator()) { + this.activePlayers++; +- if (serverPlayer.isSleeping() || serverPlayer.fauxSleeping) { // CraftBukkit ++ if (serverPlayer.isSleeping() || serverPlayer.fauxSleeping || (serverPlayer.level().purpurConfig.idleTimeoutCountAsSleeping && serverPlayer.isAfk())) { // CraftBukkit // Purpur - AFK API + this.sleepingPlayers++; + } + // CraftBukkit start diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch new file mode 100644 index 000000000..a9e87d51d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/stats/ServerRecipeBook.java ++++ b/net/minecraft/stats/ServerRecipeBook.java +@@ -106,6 +_,7 @@ + private void loadRecipes(List>> recipes, Consumer>> output, Predicate>> isRecognized) { + for (ResourceKey> resourceKey : recipes) { + if (!isRecognized.test(resourceKey)) { ++ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressUnrecognizedRecipeErrors) // Purpur - Logger settings (suppressing pointless logs) + LOGGER.error("Tried to load unrecognized recipe: {} removed now.", resourceKey); + } else { + output.accept(resourceKey); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/util/StringUtil.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/util/StringUtil.java.patch new file mode 100644 index 000000000..b8d231182 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/util/StringUtil.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/util/StringUtil.java ++++ b/net/minecraft/util/StringUtil.java +@@ -87,6 +_,7 @@ + + // Paper start - Username validation + public static boolean isReasonablePlayerName(final String name) { ++ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches(); // Purpur - Configurable valid characters for usernames + if (name.isEmpty() || name.length() > 16) { + return false; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatRules.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatRules.java.patch new file mode 100644 index 000000000..45e676e4b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatRules.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/damagesource/CombatRules.java ++++ b/net/minecraft/world/damagesource/CombatRules.java +@@ -15,7 +_,7 @@ + + public static float getDamageAfterAbsorb(LivingEntity entity, float damageAmount, DamageSource damageSource, float armorValue, float armorToughness) { + float f = 2.0F + armorToughness / 4.0F; +- float f1 = Mth.clamp(armorValue - damageAmount / f, armorValue * 0.2F, 20.0F); ++ float f1 = Mth.clamp(armorValue - damageAmount / f, armorValue * 0.2F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur - Add attribute clamping and armor limit config + float f2 = f1 / 25.0F; + ItemStack weaponItem = damageSource.getWeaponItem(); + float f3; +@@ -30,7 +_,7 @@ + } + + public static float getDamageAfterMagicAbsorb(float damageAmount, float enchantModifiers) { +- float f = Mth.clamp(enchantModifiers, 0.0F, 20.0F); ++ float f = Mth.clamp(enchantModifiers, 0.0F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur - Add attribute clamping and armor limit config + return damageAmount * (1.0F - f / 25.0F); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch new file mode 100644 index 000000000..c39efefcb --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/damagesource/CombatTracker.java ++++ b/net/minecraft/world/damagesource/CombatTracker.java +@@ -64,7 +_,7 @@ + + private Component getMessageForAssistedFall(Entity entity, Component entityDisplayName, String hasWeaponTranslationKey, String noWeaponTranslationKey) { + ItemStack itemStack = entity instanceof LivingEntity livingEntity ? livingEntity.getMainHandItem() : ItemStack.EMPTY; +- return !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME) ++ return !itemStack.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemStack.has(DataComponents.CUSTOM_NAME)) // Purpur - always show item in player death messages + ? Component.translatable(hasWeaponTranslationKey, this.mob.getDisplayName(), entityDisplayName, itemStack.getDisplayName()) + : Component.translatable(noWeaponTranslationKey, this.mob.getDisplayName(), entityDisplayName); + } +@@ -107,6 +_,15 @@ + Component component = ComponentUtils.wrapInSquareBrackets(Component.translatable(string + ".link")).withStyle(INTENTIONAL_GAME_DESIGN_STYLE); + return Component.translatable(string + ".message", this.mob.getDisplayName(), component); + } else { ++ // Purpur start - Dont run with scissors! ++ if (damageSource.isScissors()) { ++ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgRunWithScissors, this.mob); ++ // Purpur start - Stonecutter damage ++ } else if (damageSource.isStonecutter()) { ++ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgStonecutter, this.mob); ++ // Purpur end - Stonecutter damage ++ } ++ // Purpur end - Dont run with scissors! + return damageSource.getLocalizedDeathMessage(this.mob); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch new file mode 100644 index 000000000..f244d3350 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch @@ -0,0 +1,73 @@ +--- a/net/minecraft/world/damagesource/DamageSource.java ++++ b/net/minecraft/world/damagesource/DamageSource.java +@@ -24,6 +_,8 @@ + private org.bukkit.block.@Nullable Block eventBlockDamager; // Relevant block set. damageSourcePosition is only used for bad respawn point explosion or custom damage + private org.bukkit.block.@Nullable BlockState fromBlockSnapshot; // Captured block snapshot when the eventBlockDamager is not relevant (e.g. for bad respawn point explosions the block is already removed) + private boolean critical; // Supports arrows and sweeping damage ++ private boolean scissors = false; // Purpur - Dont run with scissors! ++ private boolean stonecutter = false; // Purpur - Stonecutter damage + + public DamageSource knownCause(final org.bukkit.event.entity.EntityDamageEvent.DamageCause cause) { + final DamageSource damageSource = this.copy(); +@@ -35,6 +_,30 @@ + return this.knownCause; + } + ++ // Purpur start - Dont run with scissors! ++ public DamageSource scissors() { ++ this.knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.SUICIDE); ++ this.scissors = true; ++ return this; ++ } ++ ++ public boolean isScissors() { ++ return this.scissors; ++ } ++ // Purpur end - Dont run with scissors! ++ ++ // Purpur start - - Stonecutter damage ++ public DamageSource stonecutter() { ++ this.knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.CONTACT); ++ this.stonecutter = true; ++ return this; ++ } ++ ++ public boolean isStonecutter() { ++ return this.stonecutter; ++ } ++ // Purpur end - Stonecutter damage ++ + @Nullable + public Entity eventEntityDamager() { + return this.eventEntityDamager; +@@ -94,6 +_,8 @@ + damageSource.eventBlockDamager = this.eventBlockDamager; + damageSource.fromBlockSnapshot = this.fromBlockSnapshot; + damageSource.critical = this.critical; ++ damageSource.scissors = this.isScissors(); // Purpur - Dont run with scissors! ++ damageSource.stonecutter = this.isStonecutter(); // Purpur - Stonecutter damage + return damageSource; + } + // CraftBukkit end +@@ -157,11 +_,20 @@ + } else { + Component component = this.causingEntity == null ? this.directEntity.getDisplayName() : this.causingEntity.getDisplayName(); + ItemStack itemStack = this.causingEntity instanceof LivingEntity livingEntity1 ? livingEntity1.getMainHandItem() : ItemStack.EMPTY; +- return !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME) ++ return !itemStack.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemStack.has(DataComponents.CUSTOM_NAME)) // Purpur - always show item in player death messages + ? Component.translatable(string + ".item", livingEntity.getDisplayName(), component, itemStack.getDisplayName()) + : Component.translatable(string, livingEntity.getDisplayName(), component); + } + } ++ ++ // Purpur start - Component related conveniences ++ public Component getLocalizedDeathMessage(String str, LivingEntity entity) { ++ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName()); ++ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name); ++ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template); ++ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component); ++ } ++ // Purpur end - Component related conveniences + + public String getMsgId() { + return this.type().msgId(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch new file mode 100644 index 000000000..29006dbaf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch @@ -0,0 +1,39 @@ +--- a/net/minecraft/world/damagesource/DamageSources.java ++++ b/net/minecraft/world/damagesource/DamageSources.java +@@ -42,6 +_,8 @@ + private final DamageSource stalagmite; + private final DamageSource outsideBorder; + private final DamageSource genericKill; ++ private final DamageSource scissors; // Purpur - Dont run with scissors! ++ private final DamageSource stonecutter; // Purpur - Stonecutter damage + + public DamageSources(RegistryAccess registry) { + this.damageTypes = registry.lookupOrThrow(Registries.DAMAGE_TYPE); +@@ -70,6 +_,8 @@ + this.stalagmite = this.source(DamageTypes.STALAGMITE); + this.outsideBorder = this.source(DamageTypes.OUTSIDE_BORDER); + this.genericKill = this.source(DamageTypes.GENERIC_KILL); ++ this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur - Dont run with scissors! ++ this.stonecutter = this.source(DamageTypes.MAGIC).stonecutter(); // Purpur - Stonecutter damage + } + + private DamageSource source(ResourceKey damageTypeKey) { +@@ -83,6 +_,18 @@ + private DamageSource source(ResourceKey damageTypeKey, @Nullable Entity causingEntity, @Nullable Entity directEntity) { + return new DamageSource(this.damageTypes.getOrThrow(damageTypeKey), causingEntity, directEntity); + } ++ ++ // Purpur start - Dont run with scissor ++ public DamageSource scissors() { ++ return this.scissors; ++ } ++ // Purpur end - Dont run with scissors! ++ ++ // Purpur start - Stonecutter damage ++ public DamageSource stonecutter() { ++ return this.stonecutter; ++ } ++ // Purpur end - Stonecutter damage + + public DamageSource inFire() { + return this.inFire; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch new file mode 100644 index 000000000..ea284de7b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/effect/HungerMobEffect.java ++++ b/net/minecraft/world/effect/HungerMobEffect.java +@@ -12,7 +_,7 @@ + @Override + public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { + if (entity instanceof Player player) { +- player.causeFoodExhaustion(0.005F * (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent ++ player.causeFoodExhaustion(entity.level().purpurConfig.humanHungerExhaustionAmount * (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent // Purpur - Config MobEffect by world + } + + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch new file mode 100644 index 000000000..0e8d7e3f7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/effect/PoisonMobEffect.java ++++ b/net/minecraft/world/effect/PoisonMobEffect.java +@@ -12,8 +_,8 @@ + + @Override + public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { +- if (entity.getHealth() > 1.0F) { +- entity.hurtServer(level, entity.damageSources().magic().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.POISON), 1.0F); // CraftBukkit ++ if (entity.getHealth() > entity.level().purpurConfig.entityMinimalHealthPoison) { // Purpur ++ entity.hurtServer(level, entity.damageSources().magic().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.POISON), entity.level().purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit // Purpur - Config MobEffect by world + } + + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch new file mode 100644 index 000000000..f19f319af --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/effect/RegenerationMobEffect.java ++++ b/net/minecraft/world/effect/RegenerationMobEffect.java +@@ -11,7 +_,7 @@ + @Override + public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { + if (entity.getHealth() < entity.getMaxHealth()) { +- entity.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit ++ entity.heal(entity.level().purpurConfig.entityHealthRegenAmount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit // Purpur - Config MobEffect by world + } + + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch new file mode 100644 index 000000000..f2d21d380 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/effect/SaturationMobEffect.java ++++ b/net/minecraft/world/effect/SaturationMobEffect.java +@@ -16,7 +_,8 @@ + int oldFoodLevel = player.getFoodData().foodLevel; + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, amplifier + 1 + oldFoodLevel); + if (!event.isCancelled()) { +- player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); ++ if (player.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) player.burpDelay = player.level().purpurConfig.playerBurpDelay; // Purpur - Burp delay ++ player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level().purpurConfig.humanSaturationRegenAmount); // Purpur - Config MobEffect by world + } + + ((org.bukkit.craftbukkit.entity.CraftPlayer) player.getBukkitEntity()).sendHealthUpdate(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/WitherMobEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/WitherMobEffect.java.patch new file mode 100644 index 000000000..767637659 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/effect/WitherMobEffect.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/effect/WitherMobEffect.java ++++ b/net/minecraft/world/effect/WitherMobEffect.java +@@ -12,7 +_,7 @@ + + @Override + public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { +- entity.hurtServer(level, entity.damageSources().wither(), 1.0F); ++ entity.hurtServer(level, entity.damageSources().wither(), entity.level().purpurConfig.entityWitherDegenerationAmount); // Purpur - Config MobEffect by world + return true; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch new file mode 100644 index 000000000..77a30ef9a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -0,0 +1,169 @@ +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -153,6 +_,7 @@ + import org.slf4j.Logger; + + public abstract class Entity implements SyncedDataHolder, DebugValueSource, Nameable, ItemOwner, SlotProvider, EntityAccess, ScoreHolder, DataComponentGetter, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity { // Paper - rewrite chunk system // Paper - optimise entity tracker ++ public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur - Configurable entity base attributes + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + static boolean isLevelAtLeast(ValueInput input, int level) { +@@ -282,8 +_,9 @@ + public double xOld; + public double yOld; + public double zOld; ++ public float maxUpStep; // Purpur - Add option to set armorstand step height + public boolean noPhysics; +- public final RandomSource random = SHARED_RANDOM; // Paper - Share random for entities to make them more random ++ public final RandomSource random; // Paper - Share random for entities to make them more random // Add toggle for RNG manipulation + public int tickCount; + private int remainingFireTicks; + public boolean wasTouchingWater; +@@ -316,8 +_,8 @@ + public @Nullable PortalProcessor portalProcess; + public int portalCooldown; + private boolean invulnerable; +- protected UUID uuid = Mth.createInsecureUUID(this.random); +- protected String stringUUID = this.uuid.toString(); ++ protected UUID uuid; // Purpur - Add toggle for RNG manipulation ++ protected String stringUUID; // Purpur - Add toggle for RNG manipulation + private boolean hasGlowingTag; + private final Set tags = new io.papermc.paper.util.SizeLimitedSet<>(new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(), MAX_ENTITY_TAG_COUNT); // Paper - fully limit tag size - replace set impl + private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0}; +@@ -373,6 +_,7 @@ + public long activatedTick = Integer.MIN_VALUE; + public boolean isTemporarilyActive; + public long activatedImmunityTick = Integer.MIN_VALUE; ++ public @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API + + public void inactiveTick() { + } +@@ -535,10 +_,21 @@ + } + // Paper end - optimise entity tracker + ++ // Purpur start - Add canSaveToDisk to Entity ++ public boolean canSaveToDisk() { ++ return true; ++ } ++ // Purpur end - Add canSaveToDisk to Entity ++ + public Entity(EntityType type, Level level) { + this.type = type; + this.level = level; + this.dimensions = type.getDimensions(); ++ // Purpur start - Add toggle for RNG manipulation ++ this.random = level == null || level.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create(); ++ this.uuid = Mth.createInsecureUUID(this.random); ++ this.stringUUID = this.uuid.toString(); ++ // Purpur end - Add toggle for RNG manipulation + this.position = Vec3.ZERO; + this.blockPosition = BlockPos.ZERO; + this.chunkPosition = ChunkPos.ZERO; +@@ -931,6 +_,7 @@ + && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v) + && (!(this instanceof Player player) || !player.getAbilities().invulnerable))) { + // Paper end - Configurable nether ceiling damage ++ if (this.level.purpurConfig.teleportOnNetherCeilingDamage && this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this instanceof ServerPlayer player) player.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.level.levelData.getRespawnData().pos(), this.level)); else // Purpur - Add option to teleport to spawn on nether ceiling damage + this.onBelowWorld(); + } + } +@@ -1960,7 +_,7 @@ + } + + public boolean fireImmune() { +- return this.getType().fireImmune(); ++ return this.immuneToFire != null ? immuneToFire : this.getType().fireImmune(); // Purpur - add fire immune API + } + + public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) { +@@ -2658,6 +_,11 @@ + output.putBoolean("Paper.FreezeLock", true); + } + // Paper end ++ // Purpur start - Fire immune API ++ if (immuneToFire != null) { ++ output.putBoolean("Purpur.FireImmune", immuneToFire); ++ } ++ // Purpur end - Fire immune API + } catch (Throwable var7) { + CrashReport crashReport = CrashReport.forThrowable(var7, "Saving entity NBT"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being saved"); +@@ -2778,6 +_,9 @@ + } + freezeLocked = input.getBooleanOr("Paper.FreezeLock", false); + // Paper end ++ ++ immuneToFire = input.read("Purpur.FireImmune", com.mojang.serialization.Codec.BOOL).orElse(null); // Purpur - Fire immune API ++ + } catch (Throwable var7) { + CrashReport crashReport = CrashReport.forThrowable(var7, "Loading entity NBT"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded"); +@@ -3041,6 +_,7 @@ + if (this.isAlive() && this instanceof Leashable leashable2) { + if (leashable2.getLeashHolder() == player) { + if (!this.level().isClientSide()) { ++ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.villager.AbstractVillager) return InteractionResult.CONSUME; // Purpur - Allow leashing villagers + // Paper start - EntityUnleashEvent + if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( + leashable2, player, hand, !player.hasInfiniteMaterials(), true +@@ -3471,15 +_,18 @@ + return Vec3.directionFromRotation(this.getRotationVector()); + } + ++ public BlockPos portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals + public void setAsInsidePortal(Portal portal, BlockPos pos) { + if (this.isOnPortalCooldown()) { ++ if (!(level().purpurConfig.playerFixStuckPortal && this instanceof Player && !pos.equals(this.portalPos))) // Purpur - Fix stuck in portals + this.setPortalCooldown(); +- } else { ++ } else if (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - Entities can use portals + if (this.portalProcess == null || !this.portalProcess.isSamePortal(portal)) { + this.portalProcess = new PortalProcessor(portal, pos.immutable()); + } else if (!this.portalProcess.isInsidePortalThisTick()) { + this.portalProcess.updateEntryPosition(pos.immutable()); + this.portalProcess.setAsInsidePortalThisTick(true); ++ this.portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals + } + } + } +@@ -3685,7 +_,7 @@ + } + + public int getMaxAirSupply() { +- return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() ++ return this.level == null? this.maxAirTicks : this.level().purpurConfig.drowningAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir() // Purpur - Drowning Settings + } + + public int getAirSupply() { +@@ -4220,7 +_,7 @@ + } + + public boolean canUsePortal(boolean allowPassengers) { +- return (allowPassengers || !this.isPassenger()) && this.isAlive(); ++ return (allowPassengers || !this.isPassenger()) && this.isAlive() && (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Purpur - Entities can use portals + } + + public boolean canTeleport(Level fromLevel, Level toLevel) { +@@ -4739,6 +_,12 @@ + return Mth.lerp(partialTick, this.yRotO, this.yRot); + } + ++ // Purpur start - Stop squids floating on top of water ++ public AABB getAxisForFluidCheck() { ++ return this.getBoundingBox().deflate(0.001D); ++ } ++ // Purpur end - Stop squids floating on top of water ++ + // Paper start - optimise collisions + public boolean updateFluidHeightAndDoFluidPushing(final TagKey fluid, final double flowScale) { + if (this.touchingUnloadedChunk()) { +@@ -5159,7 +_,7 @@ + } + + public float maxUpStep() { +- return 0.0F; ++ return maxUpStep; // Purpur - Add option to set armorstand step height + } + + public void onExplosionHit(@Nullable Entity entity) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntitySelector.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntitySelector.java.patch new file mode 100644 index 000000000..ff1e039a1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntitySelector.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/EntitySelector.java ++++ b/net/minecraft/world/entity/EntitySelector.java +@@ -28,6 +_,8 @@ + return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks; + }; + // Paper end - Ability to control player's insomnia and phantoms ++ public static Predicate notAfk = (player) -> !player.isAfk(); // Purpur - AFK API ++ + // Paper start - Affects Spawning API + public static final Predicate PLAYER_AFFECTS_SPAWNING = (entity) -> { + return !entity.isSpectator() && entity.isAlive() && entity instanceof Player player && player.affectsSpawning; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntityType.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntityType.java.patch new file mode 100644 index 000000000..f33120729 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntityType.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/world/entity/EntityType.java ++++ b/net/minecraft/world/entity/EntityType.java +@@ -1240,6 +_,16 @@ + return register(vanillaEntityId(key), builder); + } + ++ // Purpur start - PlayerSetSpawnerTypeWithEggEvent ++ public static EntityType getFromBukkitType(org.bukkit.entity.EntityType bukkitType) { ++ return getFromKey(Identifier.parse(bukkitType.getKey().toString())); ++ } ++ ++ public static EntityType getFromKey(Identifier location) { ++ return BuiltInRegistries.ENTITY_TYPE.getValue(location); ++ } ++ // Purpur end - PlayerSetSpawnerTypeWithEggEvent ++ + public static Identifier getKey(EntityType entityType) { + return BuiltInRegistries.ENTITY_TYPE.getKey(entityType); + } +@@ -1467,6 +_,16 @@ + return this.category; + } + ++ // Purpur start - PlayerSetSpawnerTypeWithEggEvent ++ public String getName() { ++ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath(); ++ } ++ ++ public String getTranslatedName() { ++ return getDescription().getString(); ++ } ++ // Purpur end - PlayerSetSpawnerTypeWithEggEvent ++ + public String getDescriptionId() { + return this.descriptionId; + } +@@ -1528,6 +_,7 @@ + // Paper start - Add logging for debugging entity tags with invalid ids + () -> { + LOGGER.warn("Skipping Entity with id {}", input.getStringOr("id", "[invalid]")); ++ LOGGER.warn("Location: {} {}", level.getWorld().getName(), input.read("Pos", net.minecraft.world.phys.Vec3.CODEC).orElse(net.minecraft.world.phys.Vec3.ZERO)); // Purpur - log skipped entity's position + if ((DEBUG_ENTITIES_WITH_INVALID_IDS || level.getCraftServer().getServer().isDebugging()) && input instanceof TagValueInput tagInput) { + LOGGER.warn("Skipped entity tag: {}", tagInput.input); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch new file mode 100644 index 000000000..76b98442d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/entity/ExperienceOrb.java ++++ b/net/minecraft/world/entity/ExperienceOrb.java +@@ -355,7 +_,7 @@ + public void playerTouch(Player entity) { + if (entity instanceof ServerPlayer serverPlayer) { + if (entity.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent +- entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, 2, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; ++ entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, this.level().purpurConfig.playerExpPickupDelay, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; // Purpur - Configurable player pickup exp delay + entity.take(this, 1); + int i = this.repairPlayerItems(serverPlayer, this.getValue()); + if (i > 0) { +@@ -371,7 +_,7 @@ + } + + private int repairPlayerItems(ServerPlayer player, int value) { +- Optional randomItemWith = EnchantmentHelper.getRandomItemWith( ++ Optional randomItemWith = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith( // Purpur - Add option to mend the most damaged equipment first + EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged + ); + if (randomItemWith.isPresent()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch new file mode 100644 index 000000000..a99629671 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -0,0 +1,188 @@ +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -447,6 +_,12 @@ + if (d < 0.0) { + double damagePerBlock = serverLevel1.getWorldBorder().getDamagePerBlock(); + if (damagePerBlock > 0.0) { ++ // Purpur start - Add option to teleport to spawn if outside world border ++ if (this.level().purpurConfig.teleportIfOutsideBorder && this instanceof ServerPlayer serverPlayer) { ++ serverPlayer.teleport(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.level().levelData.getRespawnData().pos(), this.level())); ++ return; ++ } ++ // Purpur end - Add option to teleport to spawn if outside world border + this.hurtServer(serverLevel1, this.damageSources().outOfBorder(), Math.max(1, Mth.floor(-d * damagePerBlock))); + } + } +@@ -462,7 +_,7 @@ + if (this.shouldTakeDrowningDamage()) { + this.setAirSupply(0); + serverLevel1.broadcastEntityEvent(this, EntityEvent.DROWN_PARTICLES); +- this.hurtServer(serverLevel1, this.damageSources().drown(), 2.0F); ++ this.hurtServer(serverLevel1, this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur - Drowning Settings + } + } else if (this.getAirSupply() < this.getMaxAirSupply() && MobEffectUtil.shouldEffectsRefillAirsupply(this)) { + this.setAirSupply(this.increaseAirSupply(this.getAirSupply())); +@@ -522,7 +_,7 @@ + } + + protected boolean shouldTakeDrowningDamage() { +- return this.getAirSupply() <= -20; ++ return this.getAirSupply() <= -this.level().purpurConfig.drowningDamageInterval; // Purpur - Drowning Settings + } + + @Override +@@ -1050,14 +_,32 @@ + if (lookingEntity != null) { + ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD); + EntityType type = lookingEntity.getType(); +- if (type == EntityType.SKELETON && itemBySlot.is(Items.SKELETON_SKULL) +- || type == EntityType.ZOMBIE && itemBySlot.is(Items.ZOMBIE_HEAD) +- || type == EntityType.PIGLIN && itemBySlot.is(Items.PIGLIN_HEAD) +- || type == EntityType.PIGLIN_BRUTE && itemBySlot.is(Items.PIGLIN_HEAD) +- || type == EntityType.CREEPER && itemBySlot.is(Items.CREEPER_HEAD)) { +- d *= 0.5; +- } +- } ++ // Purpur start - Mob head visibility percent ++ if (type == EntityType.SKELETON && itemBySlot.is(Items.SKELETON_SKULL)) { ++ d *= lookingEntity.level().purpurConfig.skeletonHeadVisibilityPercent; ++ } ++ else if (type == EntityType.ZOMBIE && itemBySlot.is(Items.ZOMBIE_HEAD)) { ++ d *= lookingEntity.level().purpurConfig.zombieHeadVisibilityPercent; ++ } ++ else if ((type == EntityType.PIGLIN || type == EntityType.PIGLIN_BRUTE) && itemBySlot.is(Items.PIGLIN_HEAD)) { ++ d *= lookingEntity.level().purpurConfig.piglinHeadVisibilityPercent; ++ } ++ else if (type == EntityType.CREEPER && itemBySlot.is(Items.CREEPER_HEAD)) { ++ d *= lookingEntity.level().purpurConfig.creeperHeadVisibilityPercent; ++ } ++ // Purpur end - Mob head visibility percent ++ } ++ ++ // Purpur start - Configurable mob blindness ++ if (lookingEntity instanceof LivingEntity entityliving) { ++ if (entityliving.hasEffect(MobEffects.BLINDNESS)) { ++ int amplifier = entityliving.getEffect(MobEffects.BLINDNESS).getAmplifier(); ++ for (int i = 0; i < amplifier; i++) { ++ d *= this.level().purpurConfig.mobsBlindnessMultiplier; ++ } ++ } ++ } ++ // Purpur end - Configurable mob blindness + + return d; + } +@@ -1104,6 +_,7 @@ + Iterator iterator = this.activeEffects.values().iterator(); + while (iterator.hasNext()) { + MobEffectInstance effect = iterator.next(); ++ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level().purpurConfig.milkClearsBeneficialEffects && effect.getEffect().value().isBeneficial()) continue; // Purpur - Milk Keeps Beneficial Effects + EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED); + if (event.isCancelled()) { + continue; +@@ -1424,6 +_,24 @@ + this.stopSleeping(); + } + ++ // Purpur start - One Punch Man! ++ if (damageSource.getEntity() instanceof net.minecraft.world.entity.player.Player player && damageSource.getEntity().level().purpurConfig.creativeOnePunch && !damageSource.is(DamageTypeTags.IS_PROJECTILE)) { ++ if (player.isCreative()) { ++ org.apache.commons.lang3.mutable.MutableDouble attackDamage = new org.apache.commons.lang3.mutable.MutableDouble(); ++ player.getMainHandItem().forEachModifier(EquipmentSlot.MAINHAND, (attributeHolder, attributeModifier) -> { ++ if (attributeModifier.operation() == AttributeModifier.Operation.ADD_VALUE) { ++ attackDamage.addAndGet(attributeModifier.amount()); ++ } ++ }); ++ ++ if (attackDamage.doubleValue() == 0.0D) { ++ // One punch! ++ amount = 9999F; ++ } ++ } ++ } ++ // Purpur end - One Punch Man! ++ + this.noActionTime = 0; + if (amount < 0.0F) { + amount = 0.0F; +@@ -1685,10 +_,10 @@ + protected @Nullable Player resolvePlayerResponsibleForDamage(DamageSource damageSource) { + Entity entity = damageSource.getEntity(); + if (entity instanceof Player player) { +- this.setLastHurtByPlayer(player, 100); ++ this.setLastHurtByPlayer(player, this.level().purpurConfig.mobLastHurtByPlayerTime); // Purpur - Config for mob last hurt by player time + } else if (entity instanceof Wolf wolf && wolf.isTame()) { + if (wolf.getOwnerReference() != null) { +- this.setLastHurtByPlayer(wolf.getOwnerReference().getUUID(), 100); ++ this.setLastHurtByPlayer(wolf.getOwnerReference().getUUID(), this.level().purpurConfig.mobLastHurtByPlayerTime); // Purpur - Config for mob last hurt by player time + } else { + this.lastHurtByPlayer = null; + this.lastHurtByPlayerMemoryTime = 0; +@@ -1739,6 +_,30 @@ + } + } + ++ // Purpur start - Totems work in inventory ++ if (level().purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemStack == null || itemStack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) { ++ for (ItemStack item : player.getInventory().getNonEquipmentItems()) { ++ if (item.getItem() == Items.TOTEM_OF_UNDYING) { ++ itemInHand = item; ++ itemStack = item.copy(); ++ break; ++ } ++ } ++ } ++ // Purpur end - Totems work in inventory ++ ++ // Purpur start - Totems work in inventory ++ if (level().purpurConfig.totemOfUndyingWorksInInventory && this instanceof ServerPlayer player && (itemStack == null || itemStack.getItem() != Items.TOTEM_OF_UNDYING) && player.getBukkitEntity().hasPermission("purpur.inventory_totem")) { ++ for (ItemStack item : player.getInventory().getNonEquipmentItems()) { ++ if (item.getItem() == Items.TOTEM_OF_UNDYING) { ++ itemInHand = item; ++ itemStack = item.copy(); ++ break; ++ } ++ } ++ } ++ // Purpur end - Totems work in inventory ++ + final org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null; + final EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot); + event.setCancelled(itemStack == null); +@@ -1920,6 +_,7 @@ + boolean flag = this.lastHurtByPlayerMemoryTime > 0; + this.dropEquipment(level); // CraftBukkit - from below + if (this.shouldDropLoot(level)) { ++ if (!(damageSource.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level().purpurConfig.disableDropsOnCrammingDeath)) { // Purpur - Disable loot drops on death by cramming + this.dropFromLootTable(level, damageSource, flag); + // Paper start + final boolean prev = this.clearEquipmentSlots; +@@ -1928,6 +_,7 @@ + // Paper end + this.dropCustomDeathLoot(level, damageSource, flag); + this.clearEquipmentSlots = prev; // Paper ++ } // Purpur - Disable loot drops on death by cramming + } + + // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment +@@ -3202,6 +_,7 @@ + float f = (float)(d * 10.0 - 3.0); + if (f > 0.0F) { + this.playSound(this.getFallDamageSound((int)f), 1.0F, 1.0F); ++ if (level().purpurConfig.elytraKineticDamage) // Purpur - Toggle for kinetic damage + this.hurt(this.damageSources().flyIntoWall(), f); + } + } +@@ -4666,6 +_,12 @@ + ? slot == EquipmentSlot.MAINHAND && this.canUseSlot(EquipmentSlot.MAINHAND) + : slot == equippable.slot() && this.canUseSlot(equippable.slot()) && equippable.canBeEquippedBy(this.getType()); + } ++ ++ // Purpur start - Dispenser curse of binding protection ++ public @Nullable EquipmentSlot getEquipmentSlotForDispenserItem(ItemStack itemstack) { ++ return EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.BINDING_CURSE, itemstack) > 0 ? null : this.getEquipmentSlotForItem(itemstack); ++ } ++ // Purpur end - Dispenser curse of binding protection + + private static SlotAccess createEquipmentSlotAccess(LivingEntity entity, EquipmentSlot slot) { + return slot != EquipmentSlot.HEAD && slot != EquipmentSlot.MAINHAND && slot != EquipmentSlot.OFFHAND diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Mob.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Mob.java.patch new file mode 100644 index 000000000..35009fa56 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Mob.java.patch @@ -0,0 +1,80 @@ +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -150,6 +_,7 @@ + private int homeRadius = -1; + public boolean aware = true; // CraftBukkit + public net.kyori.adventure.util.TriState despawnInPeacefulOverride = net.kyori.adventure.util.TriState.NOT_SET; // Paper - allow changing despawnInPeaceful ++ public int ticksSinceLastInteraction; // Purpur - Entity lifespan + + protected Mob(EntityType type, Level level) { + super(type, level); +@@ -292,6 +_,7 @@ + target = null; + } + } ++ if (target instanceof net.minecraft.server.level.ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur - Entity lifespan + this.target = target; + return true; + // CraftBukkit end +@@ -335,7 +_,27 @@ + } + + profilerFiller.pop(); +- } ++ incrementTicksSinceLastInteraction(); // Purpur - Entity lifespan ++ } ++ ++ // Purpur start - Entity lifespan ++ private void incrementTicksSinceLastInteraction() { ++ ++this.ticksSinceLastInteraction; ++ if (getRider() != null) { ++ this.ticksSinceLastInteraction = 0; ++ return; ++ } ++ if (this.level().purpurConfig.entityLifeSpan <= 0) { ++ return; // feature disabled ++ } ++ if (!this.removeWhenFarAway(0) || isPersistenceRequired() || requiresCustomPersistence() || hasCustomName()) { ++ return; // mob persistent ++ } ++ if (this.ticksSinceLastInteraction > this.level().purpurConfig.entityLifeSpan) { ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ } ++ } ++ // Purpur end - Entity lifespan + + @Override + protected void playHurtSound(DamageSource damageSource) { +@@ -439,6 +_,7 @@ + output.putString("Paper.DespawnInPeacefulOverride", this.despawnInPeacefulOverride.name()); + } + // Paper end - allow changing despawnInPeaceful ++ output.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur - Entity lifespan + } + + @Override +@@ -466,6 +_,7 @@ + this.lootTableSeed = input.getLongOr("DeathLootTableSeed", 0L); + this.setNoAi(input.getBooleanOr("NoAI", false)); + this.aware = input.getBooleanOr("Bukkit.Aware", true); // CraftBukkit ++ this.ticksSinceLastInteraction = input.getIntOr("Purpur.ticksSinceLastInteraction", 0); // Purpur- Entity lifespan + // Paper start - allow changing despawnInPeaceful + this.despawnInPeacefulOverride = readDespawnInPeacefulOverride(input); + } +@@ -1246,7 +_,7 @@ + ); + } + +- this.setLeftHanded(random.nextFloat() < 0.05F); ++ this.setLeftHanded(random.nextFloat() < level.getLevel().purpurConfig.entityLeftHandedChance); // Purpur - Changeable Mob Left Handed Chance + return spawnGroupData; + } + +@@ -1597,6 +_,7 @@ + } + + this.lungeForwardMaybe(); ++ if (target instanceof net.minecraft.server.level.ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur - Entity lifespan + return flag; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/attributes/RangedAttribute.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/attributes/RangedAttribute.java.patch new file mode 100644 index 000000000..4cd99d012 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/attributes/RangedAttribute.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/ai/attributes/RangedAttribute.java ++++ b/net/minecraft/world/entity/ai/attributes/RangedAttribute.java +@@ -29,6 +_,7 @@ + + @Override + public double sanitizeValue(double value) { ++ if (!org.purpurmc.purpur.PurpurConfig.clampAttributes) return Double.isNaN(value) ? this.minValue : value; // Purpur - Add attribute clamping and armor limit config + return Double.isNaN(value) ? this.minValue : Mth.clamp(value, this.minValue, this.maxValue); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch new file mode 100644 index 000000000..f499d70f9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -85,7 +_,7 @@ + }; + // Paper start - optimise POI access + final java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); +- io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, acquirablePois, predicate1, mob.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); ++ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, acquirablePois, predicate1, mob.blockPosition(), level.purpurConfig.villagerAcquirePoiSearchRadius, level.purpurConfig.villagerAcquirePoiSearchRadius*level.purpurConfig.villagerAcquirePoiSearchRadius, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); // Purpur - Configurable villager search radius + final Set, BlockPos>> set = new java.util.HashSet<>(poiposes.size()); + for (final Pair, BlockPos> poiPose : poiposes) { + if (predicate.test(level, poiPose.getSecond())) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch new file mode 100644 index 000000000..f90e63ebd --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java ++++ b/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java +@@ -55,7 +_,7 @@ + Node nextNode = path.getNextNode(); + BlockPos blockPos = previousNode.asBlockPos(); + BlockState blockState = level.getBlockState(blockPos); +- if (blockState.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)) { ++ if (blockState.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)&& !DoorBlock.requiresRedstone(entity.level(), blockState, blockPos)) { // Purpur - Option to make doors require redstone + DoorBlock doorBlock = (DoorBlock)blockState.getBlock(); + if (!doorBlock.isOpen(blockState)) { + // CraftBukkit start - entities opening doors +@@ -72,7 +_,7 @@ + + BlockPos blockPos1 = nextNode.asBlockPos(); + BlockState blockState1 = level.getBlockState(blockPos1); +- if (blockState1.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)) { ++ if (blockState1.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock) && !DoorBlock.requiresRedstone(entity.level(), blockState1, blockPos1)) { // Purpur - Option to make doors require redstone + DoorBlock doorBlock1 = (DoorBlock)blockState1.getBlock(); + if (!doorBlock1.isOpen(blockState1)) { + // CraftBukkit start - entities opening doors +@@ -118,7 +_,7 @@ + iterator.remove(); + } else { + BlockState blockState = level.getBlockState(blockPos); +- if (!blockState.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)) { ++ if (!blockState.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock) || DoorBlock.requiresRedstone(entity.level(), blockState, blockPos)) { // Purpur - Option to make doors require redstone + iterator.remove(); + } else { + DoorBlock doorBlock = (DoorBlock)blockState.getBlock(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java.patch new file mode 100644 index 000000000..694422e8b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java ++++ b/net/minecraft/world/entity/ai/behavior/ShowTradesToPlayer.java +@@ -45,6 +_,7 @@ + + @Override + public boolean canStillUse(ServerLevel level, Villager entity, long gameTime) { ++ if (!entity.level().purpurConfig.villagerDisplayTradeItem) return false; // Purpur - Option for villager display trade item + return this.checkExtraStartConditions(level, entity) + && this.lookTime > 0 + && entity.getBrain().getMemory(MemoryModuleType.INTERACTION_TARGET).isPresent(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch new file mode 100644 index 000000000..3ff26e6d1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java ++++ b/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java +@@ -285,7 +_,7 @@ + LevelChunk chunkNow = level.getChunkSource().getChunkNow(chunkPos.x, chunkPos.z); + if (chunkNow != null) { + for (BlockEntity blockEntity : chunkNow.getBlockEntities().values()) { +- if (blockEntity instanceof ChestBlockEntity chestBlockEntity) { ++ if (blockEntity instanceof net.minecraft.world.level.block.entity.BaseContainerBlockEntity chestBlockEntity) { // Purpur - copper golem can place items in barrels or shulkers option + double d1 = chestBlockEntity.getBlockPos().distToCenterSqr(mob.position()); + if (d1 < d) { + TransportItemsBetweenContainers.TransportItemTarget transportItemTarget1 = this.isTargetValidToPick( +@@ -369,7 +_,11 @@ + } + + private boolean isTargetBlocked(Level level, TransportItemsBetweenContainers.TransportItemTarget target) { +- return ChestBlock.isChestBlockedAt(level, target.pos); ++ // Purpur start - copper golem can place items in barrels or shulkers option ++ boolean isBarrelBlocked = !level.purpurConfig.copperGolemCanOpenBarrel && target.state.is(net.minecraft.world.level.block.Blocks.BARREL); ++ boolean isShulkerBlocked = !level.purpurConfig.copperGolemCanOpenShulker && target.blockEntity instanceof net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity shulkerBoxBlockEntity && !net.minecraft.world.level.block.ShulkerBoxBlock.canOpen(target.state, level, target.pos, shulkerBoxBlockEntity); ++ return target.state.is(net.minecraft.world.level.block.Blocks.BARREL) ? isBarrelBlocked : isShulkerBlocked || net.minecraft.world.level.block.ChestBlock.isChestBlockedAt(level, target.pos); ++ // Purpur end - copper golem can place items in barrels or shulkers option + } + + private boolean targetHasNotChanged(Level level, TransportItemsBetweenContainers.TransportItemTarget target) { +@@ -446,7 +_,7 @@ + } + + private boolean isWantedBlock(PathfinderMob mob, BlockState state) { +- return isPickingUpItems(mob) ? this.sourceBlockType.test(state) : this.destinationBlockType.test(state); ++ return isPickingUpItems(mob) ? this.sourceBlockType.test(state) : (mob.level().purpurConfig.copperGolemCanOpenBarrel && state.is(net.minecraft.world.level.block.Blocks.BARREL)) || (mob.level().purpurConfig.copperGolemCanOpenShulker && state.is(net.minecraft.tags.BlockTags.SHULKER_BOXES)) || this.destinationBlockType.test(state); // Purpur - copper golem can place items in barrels or shulkers option + } + + private static double getInteractionRange(PathfinderMob mob) { +@@ -525,7 +_,7 @@ + int i = 0; + + for (ItemStack itemStack : container) { +- if (!itemStack.isEmpty()) { ++ if (!itemStack.isEmpty() && (!(container instanceof net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity) || !itemStack.is(net.minecraft.tags.ItemTags.SHULKER_BOXES))) { // Purpur - copper golem can place items in barrels or shulkers option + int min = Math.min(itemStack.getCount(), 16); + return container.removeItem(i, min); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java.patch new file mode 100644 index 000000000..6fbb7bf98 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java ++++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java +@@ -22,6 +_,7 @@ + + @Override + public boolean canUse() { ++ if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur - Llama API // Purpur - Config to disable Llama caravans + if (!this.llama.isLeashed() && !this.llama.inCaravan()) { + List entities = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0, 4.0, 9.0), entity1 -> { + EntityType type = entity1.getType(); +@@ -71,6 +_,7 @@ + + @Override + public boolean canContinueToUse() { ++ if (!this.llama.shouldJoinCaravan) return false; // Purpur - Llama API + if (this.llama.inCaravan() && this.llama.getCaravanHead().isAlive() && this.firstIsLeashed(this.llama, 0)) { + double d = this.llama.distanceToSqr(this.llama.getCaravanHead()); + if (d > 676.0) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java.patch new file mode 100644 index 000000000..f623c03a9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java ++++ b/net/minecraft/world/entity/ai/goal/RangedBowAttackGoal.java +@@ -116,9 +_,9 @@ + } + + this.mob.lookAt(target, 30.0F, 30.0F); +- } else { ++ } //else { // Purpur - MC-121706 - Fix mobs not looking up and down when strafing + this.mob.getLookControl().setLookAt(target, 30.0F, 30.0F); +- } ++ //} // Purpur - MC-121706 - Fix mobs not looking up and down when strafing + + if (this.mob.isUsingItem()) { + if (!hasLineOfSight && this.seeTime < -60) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch new file mode 100644 index 000000000..dbec726c4 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java ++++ b/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java +@@ -59,7 +_,7 @@ + if (firstPassenger instanceof Player player) { + int temper = this.horse.getTemper(); + int maxTemper = this.horse.getMaxTemper(); +- if (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent ++ if (((this.horse.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper)) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent // Purpur - Config to always tame in Creative + this.horse.tameWithName(player); + return; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch new file mode 100644 index 000000000..6600c8fdd --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/entity/ai/goal/SwellGoal.java ++++ b/net/minecraft/world/entity/ai/goal/SwellGoal.java +@@ -54,6 +_,14 @@ + this.creeper.setSwellDir(-1); + } else { + this.creeper.setSwellDir(1); ++ // Purpur start - option to allow creeper to encircle target when fusing ++ if (this.creeper.level().purpurConfig.creeperEncircleTarget) { ++ net.minecraft.world.phys.Vec3 relative = this.creeper.position().subtract(this.target.position()); ++ relative = relative.yRot((float) Math.PI / 3).normalize().multiply(2, 2, 2); ++ net.minecraft.world.phys.Vec3 destination = this.target.position().add(relative); ++ this.creeper.getNavigation().moveTo(destination.x, destination.y, destination.z, 1); ++ } ++ // Purpur end - option to allow creeper to encircle target when fusing + } + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java.patch new file mode 100644 index 000000000..7e2366a32 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +@@ -56,7 +_,7 @@ + // Paper start - optimise POI access + java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); + // don't ask me why it's unbounded. ask mojang. +- io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); ++ io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), level.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur - Configurable villager search radius + Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); + // Paper end - optimise POI access + if (path != null && path.canReach()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/targeting/TargetingConditions.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/targeting/TargetingConditions.java.patch new file mode 100644 index 000000000..f090e4c9a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/targeting/TargetingConditions.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/entity/ai/targeting/TargetingConditions.java ++++ b/net/minecraft/world/entity/ai/targeting/TargetingConditions.java +@@ -63,6 +_,10 @@ + return false; + } else if (this.selector != null && !this.selector.test(target, level)) { + return false; ++ // Purpur start - AFK API ++ } else if (!level.purpurConfig.idleTimeoutTargetPlayer && target instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) { ++ return false; ++ // Purpur end - AFK API + } else { + if (entity == null) { + if (this.isCombat && (!target.canBeSeenAsEnemy() || level.getDifficulty() == Difficulty.PEACEFUL)) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch new file mode 100644 index 000000000..c6916a32a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch @@ -0,0 +1,34 @@ +--- a/net/minecraft/world/entity/animal/Animal.java ++++ b/net/minecraft/world/entity/animal/Animal.java +@@ -146,7 +_,7 @@ + ItemStack itemInHand = player.getItemInHand(hand); + if (this.isFood(itemInHand)) { + int age = this.getAge(); +- if (player instanceof ServerPlayer serverPlayer && age == 0 && this.canFallInLove()) { ++ if (player instanceof ServerPlayer serverPlayer && age == 0 && this.canFallInLove() && (this.level().purpurConfig.animalBreedingCooldownSeconds <= 0 || !this.level().hasBreedingCooldown(player.getUUID(), this.getClass()))) { // Purpur - Add adjustable breeding cooldown to config + final ItemStack breedCopy = itemInHand.copy(); // Paper - Fix EntityBreedEvent copying + this.usePlayerItem(player, hand, itemInHand); + this.setInLove(serverPlayer, breedCopy); // Paper - Fix EntityBreedEvent copying +@@ -227,10 +_,20 @@ + public void spawnChildFromBreeding(ServerLevel level, Animal partner) { + AgeableMob breedOffspring = this.getBreedOffspring(level, partner); + if (breedOffspring != null) { +- breedOffspring.setBaby(true); +- breedOffspring.snapTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); ++ //breedOffspring.setBaby(true); // Purpur - Add adjustable breeding cooldown to config - moved down ++ //breedOffspring.snapTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); // Purpur - Add adjustable breeding cooldown to config - moved down + // CraftBukkit start - Call EntityBreedEvent + ServerPlayer breeder = Optional.ofNullable(this.getLoveCause()).or(() -> Optional.ofNullable(partner.getLoveCause())).orElse(null); ++ // Purpur start - Add adjustable breeding cooldown to config ++ if (breeder != null && level.purpurConfig.animalBreedingCooldownSeconds > 0) { ++ if (level.hasBreedingCooldown(breeder.getUUID(), this.getClass())) { ++ return; ++ } ++ level.addBreedingCooldown(breeder.getUUID(), this.getClass()); ++ } ++ breedOffspring.setBaby(true); ++ breedOffspring.snapTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); ++ // Purpur end - Add adjustable breeding cooldown to config + int experience = this.getRandom().nextInt(7) + 1; + org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(breedOffspring, this, partner, breeder, this.breedItem, experience); + if (entityBreedEvent.isCancelled()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch new file mode 100644 index 000000000..536d9a92f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch @@ -0,0 +1,73 @@ +--- a/net/minecraft/world/entity/animal/bee/Bee.java ++++ b/net/minecraft/world/entity/animal/bee/Bee.java +@@ -171,7 +_,7 @@ + // Paper end - Fix MC-167279 + this.lookControl = new Bee.BeeLookControl(this); + this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F); +- this.setPathfindingMalus(PathType.WATER, -1.0F); ++ if (this.level().purpurConfig.beeCanInstantlyStartDrowning) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - bee can instantly start drowning in water option + this.setPathfindingMalus(PathType.WATER_BORDER, 16.0F); + this.setPathfindingMalus(PathType.COCOA, -1.0F); + this.setPathfindingMalus(PathType.FENCE, -1.0F); +@@ -360,13 +_,19 @@ + if (this.stayOutOfHiveCountdown <= 0 && !this.beePollinateGoal.isPollinating() && !this.hasStung() && this.getTarget() == null) { + boolean flag = this.hasNectar() + || this.isTiredOfLookingForNectar() +- || this.level().environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, this.position()); ++ || this.level().environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, this.position()) || isNightOrRaining(this.level()); // Purpur - Bee can work when raining or at night + return flag && !this.isHiveNearFire(); + } else { + return false; + } + } + ++ // Purpur start - Bee can work when raining or at night ++ public static boolean isNightOrRaining(Level level) { ++ return level.dimensionType().hasSkyLight() && (level.isDarkOutside() && !level.purpurConfig.beeCanWorkAtNight || level.isRaining() && !level.purpurConfig.beeCanWorkInRain); // Purpur - Bee can work when raining or at night ++ } ++ // Purpur end - Bee can work when raining or at night ++ + public void setStayOutOfHiveCountdown(int stayOutOfHiveCountdown) { + this.stayOutOfHiveCountdown = stayOutOfHiveCountdown; + } +@@ -387,7 +_,7 @@ + @Override + protected void customServerAiStep(ServerLevel level) { + boolean hasStung = this.hasStung(); +- if (this.isInWater()) { ++ if (this.level().purpurConfig.beeCanInstantlyStartDrowning && this.isInWater()) { // Purpur - bee can instantly start drowning in water option + this.underWaterTicks++; + } else { + this.underWaterTicks = 0; +@@ -397,6 +_,7 @@ + this.hurtServer(level, this.damageSources().drown(), 1.0F); + } + ++ if (hasStung && !this.level().purpurConfig.beeDiesAfterSting) setHasStung(false); else // Purpur - Stop bees from dying after stinging + if (hasStung) { + this.timeSinceSting++; + if (this.timeSinceSting % 5 == 0 && this.random.nextInt(Mth.clamp(1200 - this.timeSinceSting, 1, 1200)) == 0) { +@@ -1130,6 +_,7 @@ + Bee.this.savedFlowerPos = optional.get(); + Bee.this.navigation + .moveTo(Bee.this.savedFlowerPos.getX() + 0.5, Bee.this.savedFlowerPos.getY() + 0.5, Bee.this.savedFlowerPos.getZ() + 0.5, 1.2F); ++ new org.purpurmc.purpur.event.entity.BeeFoundFlowerEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level())).callEvent(); // Purpur - Bee API + return true; + } else { + Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60); +@@ -1176,6 +_,7 @@ + this.pollinating = false; + Bee.this.navigation.stop(); + Bee.this.remainingCooldownBeforeLocatingNewFlower = 200; ++ new org.purpurmc.purpur.event.entity.BeeStopPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), Bee.this.savedFlowerPos == null ? null : org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level()), Bee.this.hasNectar()).callEvent(); // Purpur - Bee API + } + + @Override +@@ -1222,6 +_,7 @@ + this.setWantedPos(); + } + ++ if (this.successfulPollinatingTicks == 0) new org.purpurmc.purpur.event.entity.BeeStartedPollinatingEvent((org.bukkit.entity.Bee) Bee.this.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(Bee.this.savedFlowerPos, Bee.this.level())).callEvent(); // Purpur - Bee API + this.successfulPollinatingTicks++; + if (Bee.this.random.nextFloat() < 0.05F && this.successfulPollinatingTicks > this.lastSoundPlayedTick + 60) { + this.lastSoundPlayedTick = this.successfulPollinatingTicks; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch new file mode 100644 index 000000000..6ab713663 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch @@ -0,0 +1,90 @@ +--- a/net/minecraft/world/entity/animal/cow/AbstractCow.java ++++ b/net/minecraft/world/entity/animal/cow/AbstractCow.java +@@ -40,7 +_,7 @@ + this.goalSelector.addGoal(0, new FloatGoal(this)); + this.goalSelector.addGoal(1, new PanicGoal(this, 2.0)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0)); +- this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, itemStack -> itemStack.is(ItemTags.COW_FOOD), false)); ++ this.goalSelector.addGoal(3, new TemptGoal(this, 1.25, itemStack -> level().purpurConfig.cowFeedMushrooms > 0 && (itemStack.is(net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) || itemStack.is(net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem())) || itemStack.is(ItemTags.COW_FOOD), false)); // Purpur - Cows eat mushrooms + this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); +@@ -96,6 +_,10 @@ + ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit + player.setItemInHand(hand, itemStack); + return InteractionResult.SUCCESS; ++ // Purpur start - Cows eat mushrooms - feed mushroom to change to mooshroom ++ } else if (level().purpurConfig.cowFeedMushrooms > 0 && this.getType() != EntityType.MOOSHROOM && isMushroom(itemInHand)) { ++ return this.feedMushroom(player, itemInHand); ++ // Purpur end - Cows eat mushrooms + } else { + return super.mobInteract(player, hand); + } +@@ -105,4 +_,67 @@ + public EntityDimensions getDefaultDimensions(Pose pose) { + return this.isBaby() ? BABY_DIMENSIONS : super.getDefaultDimensions(pose); + } ++ ++ // Purpur start - Cows eat mushrooms - feed mushroom to change to mooshroom ++ private int redMushroomsFed = 0; ++ private int brownMushroomsFed = 0; ++ ++ private boolean isMushroom(ItemStack stack) { ++ return stack.getItem() == net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem() || stack.getItem() == net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem(); ++ } ++ ++ private int incrementFeedCount(ItemStack stack) { ++ if (stack.getItem() == net.minecraft.world.level.block.Blocks.RED_MUSHROOM.asItem()) { ++ return ++redMushroomsFed; ++ } else { ++ return ++brownMushroomsFed; ++ } ++ } ++ ++ private InteractionResult feedMushroom(Player player, ItemStack stack) { ++ level().broadcastEntityEvent(this, (byte) 18); // hearts ++ playSound(SoundEvents.COW_MILK, 1.0F, 1.0F); ++ if (incrementFeedCount(stack) < level().purpurConfig.cowFeedMushrooms) { ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ return InteractionResult.CONSUME; // require 5 mushrooms to transform (prevents mushroom duping) ++ } ++ MushroomCow mooshroom = EntityType.MOOSHROOM.create(level(), net.minecraft.world.entity.EntitySpawnReason.CONVERSION); ++ if (mooshroom == null) { ++ return InteractionResult.PASS; ++ } ++ if (stack.getItem() == net.minecraft.world.level.block.Blocks.BROWN_MUSHROOM.asItem()) { ++ mooshroom.setVariant(MushroomCow.Variant.BROWN); ++ } else { ++ mooshroom.setVariant(MushroomCow.Variant.RED); ++ } ++ mooshroom.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); ++ mooshroom.setHealth(this.getHealth()); ++ mooshroom.setAge(getAge()); ++ mooshroom.copyPosition(this); ++ mooshroom.setYBodyRot(this.yBodyRot); ++ mooshroom.setYHeadRot(this.getYHeadRot()); ++ mooshroom.yRotO = this.yRotO; ++ mooshroom.xRotO = this.xRotO; ++ if (this.hasCustomName()) { ++ mooshroom.setCustomName(this.getCustomName()); ++ } ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ this.level().addFreshEntity(mooshroom); ++ this.remove(RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ for (int i = 0; i < 15; ++i) { ++ ((net.minecraft.server.level.ServerLevel) level()).sendParticlesSource(((net.minecraft.server.level.ServerLevel) level()).players(), null, net.minecraft.core.particles.ParticleTypes.HAPPY_VILLAGER, ++ false, true, ++ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1, ++ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0); ++ } ++ return InteractionResult.SUCCESS; ++ } ++ // Purpur end - Cows eat mushrooms + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch new file mode 100644 index 000000000..c042bfe98 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/animal/cow/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java +@@ -199,6 +_,13 @@ + level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, source, 1.0F, 1.0F); + this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), cow -> { + level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); ++ // Purpur start - Fix cow rotation when shearing mooshroom ++ cow.copyPosition(this); ++ cow.yBodyRot = this.yBodyRot; ++ cow.setYHeadRot(this.getYHeadRot()); ++ cow.yRotO = this.yRotO; ++ cow.xRotO = this.xRotO; ++ // Purpur end - Fix cow rotation when shearing mooshroom + // Paper start - custom shear drops; moved drop generation to separate method + drops.forEach(drop -> { + this.spawnAtLocation(level, new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), drop)); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch new file mode 100644 index 000000000..4cfc72671 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch @@ -0,0 +1,47 @@ +--- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java ++++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java +@@ -75,6 +_,7 @@ + public static final float BABY_SCALE = 0.65F; + private static final boolean DEFAULT_GOT_FISH = false; + @Nullable public BlockPos treasurePos; ++ private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance + + public Dolphin(EntityType type, Level level) { + super(type, level); +@@ -90,6 +_,7 @@ + this.setAirSupply(this.getMaxAirSupply()); + this.setXRot(0.0F); + SpawnGroupData spawnGroupData1 = Objects.requireNonNullElseGet(spawnGroupData, () -> new AgeableMob.AgeableMobGroupData(0.1F)); ++ this.isNaturallyAggressiveToPlayers = level.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= level.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance; // Purpur - Dolphins naturally aggressive to players chance + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData1); + } + +@@ -155,17 +_,19 @@ + protected void registerGoals() { + this.goalSelector.addGoal(0, new BreathAirGoal(this)); + this.goalSelector.addGoal(0, new TryFindWaterGoal(this)); ++ this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - Dolphins naturally aggressive to players chance + this.goalSelector.addGoal(1, new Dolphin.DolphinSwimToTreasureGoal(this)); + this.goalSelector.addGoal(2, new Dolphin.DolphinSwimWithPlayerGoal(this, 4.0)); + this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0, 10)); + this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(5, new DolphinJumpGoal(this, 10)); +- this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2F, true)); ++ //this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2F, true)); // Purpur - moved up - Dolphins naturally aggressive to players chance + this.goalSelector.addGoal(8, new Dolphin.PlayWithItemsGoal()); + this.goalSelector.addGoal(8, new FollowBoatGoal(this)); + this.goalSelector.addGoal(9, new AvoidEntityGoal<>(this, Guardian.class, 8.0F, 1.0, 1.0)); + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Guardian.class).setAlertOthers()); ++ this.targetSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Dolphins naturally aggressive to players chance + } + + public static AttributeSupplier.Builder createAttributes() { +@@ -392,6 +_,7 @@ + + @Override + public boolean canUse() { ++ if (this.dolphin.level().purpurConfig.dolphinDisableTreasureSearching) return false; // Purpur - Add option to disable dolphin treasure searching + return this.dolphin.gotFish() && this.dolphin.getAirSupply() >= 100; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch new file mode 100644 index 000000000..f5a863392 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/entity/animal/equine/Llama.java ++++ b/net/minecraft/world/entity/animal/equine/Llama.java +@@ -76,6 +_,7 @@ + boolean didSpit; + private @Nullable Llama caravanHead; + public @Nullable Llama caravanTail; // Paper - public ++ public boolean shouldJoinCaravan = true; // Purpur - Llama API + + public Llama(EntityType type, Level level) { + super(type, level); +@@ -105,6 +_,7 @@ + super.addAdditionalSaveData(output); + output.store("Variant", Llama.Variant.LEGACY_CODEC, this.getVariant()); + output.putInt("Strength", this.getStrength()); ++ output.putBoolean("Purpur.ShouldJoinCaravan", shouldJoinCaravan); // Purpur - Llama API + } + + @Override +@@ -112,6 +_,7 @@ + this.setStrength(input.getIntOr("Strength", 0)); + super.readAdditionalSaveData(input); + this.setVariant(input.read("Variant", Llama.Variant.LEGACY_CODEC).orElse(Llama.Variant.DEFAULT)); ++ this.shouldJoinCaravan = input.getBooleanOr("Purpur.ShouldJoinCaravan", true); // Purpur - Llama API + } + + @Override +@@ -388,6 +_,7 @@ + + public void leaveCaravan() { + if (this.caravanHead != null) { ++ new org.purpurmc.purpur.event.entity.LlamaLeaveCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity()).callEvent(); // Purpur - Llama API + this.caravanHead.caravanTail = null; + } + +@@ -395,6 +_,7 @@ + } + + public void joinCaravan(Llama caravanHead) { ++ if (!this.level().purpurConfig.llamaJoinCaravans || !shouldJoinCaravan || !new org.purpurmc.purpur.event.entity.LlamaJoinCaravanEvent((org.bukkit.entity.Llama) getBukkitEntity(), (org.bukkit.entity.Llama) caravanHead.getBukkitEntity()).callEvent()) return; // Purpur - Llama API // Purpur - Config to disable Llama caravans + this.caravanHead = caravanHead; + this.caravanHead.caravanTail = this; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch new file mode 100644 index 000000000..ff93d1657 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/entity/animal/feline/Cat.java ++++ b/net/minecraft/world/entity/animal/feline/Cat.java +@@ -354,6 +_,14 @@ + return this.isTame() && otherAnimal instanceof Cat cat && cat.isTame() && super.canMate(otherAnimal); + } + ++ // Purpur start - Configurable default collar color ++ @Override ++ public void tame(Player player) { ++ setCollarColor(level().purpurConfig.catDefaultCollarColor); ++ super.tame(player); ++ } ++ // Purpur end - Configurable default collar color ++ + @Override + public @Nullable SpawnGroupData finalizeSpawn( + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData +@@ -451,7 +_,7 @@ + } + + private void tryToTame(Player player) { +- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit ++ if (((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(3) == 0) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit // Purpur - Config to always tame in Creative + this.tame(player); + this.setOrderedToSit(true); + this.level().broadcastEntityEvent(this, EntityEvent.TAMING_SUCCEEDED); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch new file mode 100644 index 000000000..21c0a7907 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/animal/feline/Ocelot.java ++++ b/net/minecraft/world/entity/animal/feline/Ocelot.java +@@ -234,7 +_,7 @@ + public boolean checkSpawnObstruction(LevelReader level) { + if (level.isUnobstructed(this) && !level.containsAnyLiquid(this.getBoundingBox())) { + BlockPos blockPos = this.blockPosition(); +- if (blockPos.getY() < level.getSeaLevel()) { ++ if (!level().purpurConfig.ocelotSpawnUnderSeaLevel && blockPos.getY() < level.getSeaLevel()) { // Purpur - Option Ocelot Spawn Under Sea Level + return false; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch new file mode 100644 index 000000000..a572fa81f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/entity/animal/fish/WaterAnimal.java ++++ b/net/minecraft/world/entity/animal/fish/WaterAnimal.java +@@ -76,8 +_,7 @@ + seaLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(seaLevel); + i = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(i); + // Paper end - Make water animal spawn height configurable +- return pos.getY() >= i +- && pos.getY() <= seaLevel ++ return ((spawnReason == EntitySpawnReason.SPAWNER && level.getMinecraftWorld().purpurConfig.spawnerFixMC238526) || (pos.getY() >= i && pos.getY() <= seaLevel)) // Purpur - MC-238526 - Fix spawner not spawning water animals correctly + && level.getFluidState(pos.below()).is(FluidTags.WATER) + && level.getBlockState(pos.above()).is(Blocks.WATER); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch new file mode 100644 index 000000000..f6fdc8513 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/world/entity/animal/fox/Fox.java ++++ b/net/minecraft/world/entity/animal/fox/Fox.java +@@ -357,6 +_,11 @@ + } + + private void setTargetGoals() { ++ // Purpur start - Tulips change fox type - do not add duplicate goals ++ this.targetSelector.removeGoal(this.landTargetGoal); ++ this.targetSelector.removeGoal(this.turtleEggTargetGoal); ++ this.targetSelector.removeGoal(this.fishTargetGoal); ++ // Purpur end - Tulips change fox type + if (this.getVariant() == Fox.Variant.RED) { + this.targetSelector.addGoal(4, this.landTargetGoal); + this.targetSelector.addGoal(4, this.turtleEggTargetGoal); +@@ -384,6 +_,7 @@ + + public void setVariant(Fox.Variant variant) { + this.entityData.set(DATA_TYPE_ID, variant.getId()); ++ this.setTargetGoals(); // Purpur - Tulips change fox type - fix API bug not updating pathfinders on type change + } + + @Override +@@ -709,6 +_,29 @@ + return slot == EquipmentSlot.MAINHAND; + } + // Paper end ++ ++ // Purpur start - Tulips change fox type ++ @Override ++ public net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) { ++ if (level().purpurConfig.foxTypeChangesWithTulips) { ++ ItemStack itemstack = player.getItemInHand(hand); ++ if (getVariant() == Variant.RED && itemstack.getItem() == Items.WHITE_TULIP) { ++ setVariant(Variant.SNOW); ++ if (!player.getAbilities().instabuild) { ++ itemstack.shrink(1); ++ } ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } else if (getVariant() == Variant.SNOW && itemstack.getItem() == Items.ORANGE_TULIP) { ++ setVariant(Variant.RED); ++ if (!player.getAbilities().instabuild) { ++ itemstack.shrink(1); ++ } ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } ++ } ++ return super.mobInteract(player, hand); ++ } ++ // Purpur end - Tulips change fox type + + @Override + // Paper start - Cancellable death event diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch new file mode 100644 index 000000000..d854787d7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -389,6 +_,7 @@ + + // Paper start - Goat ram API + public void ram(net.minecraft.world.entity.LivingEntity entity) { ++ if(!new org.purpurmc.purpur.event.entity.GoatRamEntityEvent((org.bukkit.entity.Goat) getBukkitEntity(), entity.getBukkitLivingEntity()).callEvent()) return; // Purpur - Added goat ram event + Brain brain = this.getBrain(); + brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position()); + brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch new file mode 100644 index 000000000..6ce83f8b0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/world/entity/animal/golem/CopperGolem.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java +@@ -83,6 +_,7 @@ + private final AnimationState interactionDropItemAnimationState = new AnimationState(); + private final AnimationState interactionDropNoItemAnimationState = new AnimationState(); + public static final EquipmentSlot EQUIPMENT_SLOT_ANTENNA = EquipmentSlot.SADDLE; ++ @Nullable private UUID summoner; // Purpur - Summoner API + + public CopperGolem(EntityType type, Level level) { + super(type, level); +@@ -96,6 +_,17 @@ + this.getBrain().setMemory(MemoryModuleType.TRANSPORT_ITEMS_COOLDOWN_TICKS, this.getRandom().nextInt(60, 100)); + } + ++ // Purpur start - Summoner API ++ @Nullable ++ public UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(@Nullable UUID summoner) { ++ this.summoner = summoner; ++ } ++ // Purpur end - Summoner API ++ + public static AttributeSupplier.Builder createAttributes() { + return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.STEP_HEIGHT, 1.0).add(Attributes.MAX_HEALTH, 12.0); + } +@@ -171,6 +_,7 @@ + super.addAdditionalSaveData(output); + output.putLong("next_weather_age", this.nextWeatheringTick); + output.store("weather_state", WeatheringCopper.WeatherState.CODEC, this.getWeatherState()); ++ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API + } + + @Override +@@ -178,6 +_,7 @@ + super.readAdditionalSaveData(input); + this.nextWeatheringTick = input.getLongOr("next_weather_age", -1L); + this.setWeatherState(input.read("weather_state", WeatheringCopper.WeatherState.CODEC).orElse(WeatheringCopper.WeatherState.UNAFFECTED)); ++ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolemAi.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolemAi.java.patch new file mode 100644 index 000000000..81805e39c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/CopperGolemAi.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/animal/golem/CopperGolemAi.java ++++ b/net/minecraft/world/entity/animal/golem/CopperGolemAi.java +@@ -43,7 +_,7 @@ + private static final int TICK_TO_START_ON_REACHED_INTERACTION = 1; + private static final int TICK_TO_PLAY_ON_REACHED_SOUND = 9; + private static final Predicate TRANSPORT_ITEM_SOURCE_BLOCK = state -> state.is(BlockTags.COPPER_CHESTS); +- private static final Predicate TRANSPORT_ITEM_DESTINATION_BLOCK = state -> state.is(Blocks.CHEST) || state.is(Blocks.TRAPPED_CHEST); ++ private static final Predicate TRANSPORT_ITEM_DESTINATION_BLOCK = state -> state.is(Blocks.CHEST) || state.is(Blocks.TRAPPED_CHEST); // Purpur - copper golem can place items in barrels or shulkers option - diff on change + private static final ImmutableList>> SENSOR_TYPES = ImmutableList.of( + SensorType.NEAREST_LIVING_ENTITIES, SensorType.HURT_BY + ); +@@ -158,6 +_,11 @@ + } + + if (integer == 60) { ++ // Purpur start - copper golem can place items in barrels or shulkers option ++ if (container instanceof net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity shulkerBoxBlockEntity && shulkerBoxBlockEntity.openCount > 0) { ++ container.stopOpen(copperGolem); ++ } ++ // Purpur end - copper golem can place items in barrels or shulkers option + if (container.getEntitiesWithContainerOpen().contains(pathfinderMob)) { + container.stopOpen(copperGolem); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch new file mode 100644 index 000000000..9d2f00982 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/world/entity/animal/golem/IronGolem.java ++++ b/net/minecraft/world/entity/animal/golem/IronGolem.java +@@ -58,13 +_,25 @@ + private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39); + private long persistentAngerEndTime; + private @Nullable EntityReference persistentAngerTarget; ++ private java.util.@Nullable UUID summoner; // Purpur - Summoner API + + public IronGolem(EntityType type, Level level) { + super(type, level); + } + ++ // Purpur start - Summoner API ++ public java.util.@Nullable UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(java.util.@Nullable UUID summoner) { ++ this.summoner = summoner; ++ } ++ // Purpur end - Summoner API ++ + @Override + protected void registerGoals() { ++ if (this.level().purpurConfig.ironGolemPoppyCalm) this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.ReceiveFlower(this)); // Purpur - Iron golem calm anger options + this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true)); + this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9, 32.0F)); + this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6, false)); +@@ -142,6 +_,7 @@ + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); + output.putBoolean("PlayerCreated", this.isPlayerCreated()); ++ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API + this.addPersistentAngerSaveData(output); + } + +@@ -149,6 +_,7 @@ + protected void readAdditionalSaveData(ValueInput input) { + super.readAdditionalSaveData(input); + this.setPlayerCreated(input.getBooleanOr("PlayerCreated", false)); ++ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API + this.readPersistentAngerSaveData(this.level(), input); + } + +@@ -267,6 +_,7 @@ + float f = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F; + this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0F, f); + itemInHand.consume(1, player); ++ if (this.level().purpurConfig.ironGolemHealCalm && isAngry() && getHealth() == getMaxHealth()) stopBeingAngry(); // Purpur - Iron golem calm anger options + return InteractionResult.SUCCESS; + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch new file mode 100644 index 000000000..8e017028e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch @@ -0,0 +1,61 @@ +--- a/net/minecraft/world/entity/animal/golem/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java +@@ -46,15 +_,26 @@ + private static final EntityDataAccessor DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE); + private static final byte PUMPKIN_FLAG = 16; + private static final boolean DEFAULT_PUMPKIN = true; ++ private java.util.@Nullable UUID summoner; // Purpur - Summoner API + + public SnowGolem(EntityType type, Level level) { + super(type, level); + } + ++ // Purpur start - Summoner API ++ public java.util.@Nullable UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(java.util.@Nullable UUID summoner) { ++ this.summoner = summoner; ++ } ++ // Purpur end - Summoner API ++ + @Override + protected void registerGoals() { +- this.goalSelector.addGoal(1, new RangedAttackGoal(this, 1.25, 20, 10.0F)); +- this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0, 1.0000001E-5F)); ++ this.goalSelector.addGoal(1, new RangedAttackGoal(this, level().purpurConfig.snowGolemAttackDistance, level().purpurConfig.snowGolemSnowBallMin, level().purpurConfig.snowGolemSnowBallMax, level().purpurConfig.snowGolemSnowBallModifier)); // Purpur - Snow Golem rate of fire config ++ this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D, 1.0000001E-5F)); + this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F)); + this.goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entity, level) -> entity instanceof Enemy)); +@@ -74,12 +_,14 @@ + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); + output.putBoolean("Pumpkin", this.hasPumpkin()); ++ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API + } + + @Override + protected void readAdditionalSaveData(ValueInput input) { + super.readAdditionalSaveData(input); + this.setPumpkin(input.getBooleanOr("Pumpkin", true)); ++ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API + } + + @Override +@@ -153,6 +_,14 @@ + } + + return InteractionResult.SUCCESS; ++ // Purpur start - Snowman drop and put back pumpkin ++ } else if (level().purpurConfig.snowGolemPutPumpkinBack && !hasPumpkin() && itemInHand.getItem() == Blocks.CARVED_PUMPKIN.asItem()) { ++ setPumpkin(true); ++ if (!player.getAbilities().instabuild) { ++ itemInHand.shrink(1); ++ } ++ return InteractionResult.SUCCESS; ++ // Purpur end - Snowman drop and put back pumpkin + } else { + return InteractionResult.PASS; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch new file mode 100644 index 000000000..66096c247 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch @@ -0,0 +1,51 @@ +--- a/net/minecraft/world/entity/animal/parrot/Parrot.java ++++ b/net/minecraft/world/entity/animal/parrot/Parrot.java +@@ -164,6 +_,7 @@ + protected void registerGoals() { + this.goalSelector.addGoal(0, new TamableAnimal.TamableAnimalPanicGoal(1.25)); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ if (this.level().purpurConfig.parrotBreedable) this.goalSelector.addGoal(1, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); // Purpur - Breedable parrots + this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F)); + this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(2, new FollowOwnerGoal(this, 1.0, 5.0F, 1.0F)); +@@ -269,7 +_,7 @@ + } + + if (!this.level().isClientSide()) { +- if (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit ++ if (((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(10) == 0) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit // Purpur - Config to always tame in Creative + this.tame(player); + this.level().broadcastEntityEvent(this, EntityEvent.TAMING_SUCCEEDED); + } else { +@@ -277,6 +_,7 @@ + } + } + ++ if (this.level().purpurConfig.parrotBreedable) return super.mobInteract(player, hand); // Purpur - Breedable parrots + return InteractionResult.SUCCESS; + } else if (!itemInHand.is(ItemTags.PARROT_POISONOUS_FOOD)) { + if (!this.isFlying() && this.isTame() && this.isOwnedBy(player)) { +@@ -301,7 +_,7 @@ + + @Override + public boolean isFood(ItemStack stack) { +- return false; ++ return this.level().purpurConfig.parrotBreedable && stack.is(ItemTags.PARROT_FOOD); // Purpur - Breedable parrots + } + + public static boolean checkParrotSpawnRules( +@@ -316,12 +_,12 @@ + + @Override + public boolean canMate(Animal otherAnimal) { +- return false; ++ return super.canMate(otherAnimal); // Purpur - Breedable parrots + } + + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { +- return null; ++ return level.purpurConfig.parrotBreedable ? EntityType.PARROT.create(level, EntitySpawnReason.BREEDING) : null; // Purpur - Breedable parrots + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch new file mode 100644 index 000000000..3b45f4fdc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/world/entity/animal/pig/Pig.java ++++ b/net/minecraft/world/entity/animal/pig/Pig.java +@@ -138,6 +_,19 @@ + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { + boolean isFood = this.isFood(player.getItemInHand(hand)); ++ // Purpur start - Pigs give saddle back ++ if (level().purpurConfig.pigGiveSaddleBack && player.isSecondaryUseActive() && !isFood && isSaddled() && !isVehicle()) { ++ this.setItemSlot(EquipmentSlot.SADDLE, ItemStack.EMPTY); ++ if (!player.getAbilities().instabuild) { ++ ItemStack saddle = new ItemStack(Items.SADDLE); ++ if (!player.getInventory().add(saddle)) { ++ player.drop(saddle, false); ++ } ++ } ++ return InteractionResult.SUCCESS; ++ } ++ // Purpur end - Pigs give saddle back ++ + if (!isFood && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) { + if (!this.level().isClientSide()) { + player.startRiding(this); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/polarbear/PolarBear.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/polarbear/PolarBear.java.patch new file mode 100644 index 000000000..06b4c45f4 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/polarbear/PolarBear.java.patch @@ -0,0 +1,54 @@ +--- a/net/minecraft/world/entity/animal/polarbear/PolarBear.java ++++ b/net/minecraft/world/entity/animal/polarbear/PolarBear.java +@@ -66,6 +_,29 @@ + super(type, level); + } + ++ // Purpur start - Breedable Polar Bears ++ public boolean canMate(Animal other) { ++ if (other == this) { ++ return false; ++ } else if (this.isStanding()) { ++ return false; ++ } else if (this.getTarget() != null) { ++ return false; ++ } else if (!(other instanceof PolarBear)) { ++ return false; ++ } else { ++ PolarBear bear = (PolarBear) other; ++ if (bear.isStanding()) { ++ return false; ++ } ++ if (bear.getTarget() != null) { ++ return false; ++ } ++ return this.isInLove() && bear.isInLove(); ++ } ++ } ++ // Purpur end - Breedable Polar Bears ++ + @Override + public @Nullable AgeableMob getBreedOffspring(ServerLevel level, AgeableMob partner) { + return EntityType.POLAR_BEAR.create(level, EntitySpawnReason.BREEDING); +@@ -73,7 +_,7 @@ + + @Override + public boolean isFood(ItemStack stack) { +- return false; ++ return level().purpurConfig.polarBearBreedableItem != null && stack.getItem() == level().purpurConfig.polarBearBreedableItem; // Purpur - Breedable Polar Bears + } + + @Override +@@ -82,6 +_,12 @@ + this.goalSelector.addGoal(0, new FloatGoal(this)); + this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal()); + this.goalSelector.addGoal(1, new PanicGoal(this, 2.0, mob -> mob.isBaby() ? DamageTypeTags.PANIC_CAUSES : DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES)); ++ // Purpur start - Breedable Polar Bears ++ if (level().purpurConfig.polarBearBreedableItem != null) { ++ this.goalSelector.addGoal(2, new net.minecraft.world.entity.ai.goal.BreedGoal(this, 1.0D)); ++ this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.TemptGoal(this, 1.0D, net.minecraft.world.item.crafting.Ingredient.of(level().purpurConfig.polarBearBreedableItem), false)); ++ } ++ // Purpur end - Breedable Polar Bears + this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25)); + this.goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch new file mode 100644 index 000000000..d378a0a96 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java ++++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java +@@ -404,10 +_,23 @@ + } + + this.setVariant(randomRabbitVariant); ++ ++ // Purpur start - Special mobs naturally spawn ++ if (randomRabbitVariant != Variant.EVIL && level.getLevel().purpurConfig.rabbitNaturalToast > 0D && random.nextDouble() <= level.getLevel().purpurConfig.rabbitNaturalToast) { ++ setCustomName(Component.translatable("Toast")); ++ } ++ // Purpur end - Special mobs naturally spawn ++ + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); + } + + private static Rabbit.Variant getRandomRabbitVariant(LevelAccessor level, BlockPos pos) { ++ // Purpur start - Special mobs naturally spawn ++ Level world = level.getMinecraftWorld(); ++ if (world.purpurConfig.rabbitNaturalKiller > 0D && world.getRandom().nextDouble() <= world.purpurConfig.rabbitNaturalKiller) { ++ return Rabbit.Variant.EVIL; ++ } ++ // Purpur end - Special mobs naturally spawn + Holder biome = level.getBiome(pos); + int randomInt = level.getRandom().nextInt(100); + if (biome.is(BiomeTags.SPAWNS_WHITE_RABBITS)) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/GlowSquid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/GlowSquid.java.patch new file mode 100644 index 000000000..00859fa4c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/GlowSquid.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/animal/squid/GlowSquid.java ++++ b/net/minecraft/world/entity/animal/squid/GlowSquid.java +@@ -30,6 +_,13 @@ + super(type, level); + } + ++ // Purpur start - Flying squids! Oh my! ++ @Override ++ public boolean canFly() { ++ return this.level().purpurConfig.glowSquidsCanFly; ++ } ++ // Purpur end - Flying squids! Oh my! ++ + @Override + protected ParticleOptions getInkParticle() { + return ParticleTypes.GLOW_SQUID_INK; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch new file mode 100644 index 000000000..9ca8b5607 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/world/entity/animal/squid/Squid.java ++++ b/net/minecraft/world/entity/animal/squid/Squid.java +@@ -48,10 +_,29 @@ + + public Squid(EntityType type, Level level) { + super(type, level); +- // this.random.setSeed(this.getId()); // Paper - Share random for entities to make them more random ++ if (!level.purpurConfig.entitySharedRandom) this.random.setSeed(this.getId()); // Paper - Share random for entities to make them more random // Purpur - Add toggle for RNG manipulation + this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; + } + ++ // Purpur start - Stop squids floating on top of water ++ @Override ++ public net.minecraft.world.phys.AABB getAxisForFluidCheck() { ++ // Stops squids from floating just over the water ++ return super.getAxisForFluidCheck().offsetY(level().purpurConfig.squidOffsetWaterCheck); ++ } ++ // Purpur end - Stop squids floating on top of water ++ ++ // Purpur start - Flying squids! Oh my! ++ public boolean canFly() { ++ return this.level().purpurConfig.squidsCanFly; ++ } ++ ++ @Override ++ public boolean isInWater() { ++ return this.wasTouchingWater || canFly(); ++ } ++ // Purpur end - Flying squids! Oh my! ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); +@@ -128,6 +_,7 @@ + } + + if (this.isInWater()) { ++ if (canFly()) setNoGravity(!wasTouchingWater); // Purpur - Flying squids! Oh my! + if (this.tentacleMovement < (float) Math.PI) { + float f = this.tentacleMovement / (float) Math.PI; + this.tentacleAngle = Mth.sin(f * f * (float) Math.PI) * (float) Math.PI * 0.25F; +@@ -308,7 +_,7 @@ + int noActionTime = this.squid.getNoActionTime(); + if (noActionTime > 100) { + this.squid.movementVector = Vec3.ZERO; +- } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) { ++ } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.isInWater() || !this.squid.hasMovementVector()) { // Purpur - Flying squids! Oh my! + float f = this.squid.getRandom().nextFloat() * (float) (Math.PI * 2); + this.squid.movementVector = new Vec3(Mth.cos(f) * 0.2F, -0.1F + this.squid.getRandom().nextFloat() * 0.2F, Mth.sin(f) * 0.2F); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch new file mode 100644 index 000000000..e7ed8a723 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch @@ -0,0 +1,168 @@ +--- a/net/minecraft/world/entity/animal/wolf/Wolf.java ++++ b/net/minecraft/world/entity/animal/wolf/Wolf.java +@@ -100,6 +_,37 @@ + EntityType type = entity.getType(); + return type == EntityType.SHEEP || type == EntityType.RABBIT || type == EntityType.FOX; + }; ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ private boolean isRabid = false; ++ private static final TargetingConditions.Selector RABID_PREDICATE = (entity, ignored) -> entity instanceof net.minecraft.server.level.ServerPlayer || entity instanceof net.minecraft.world.entity.Mob; ++ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_VANILLA = new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR); ++ private final net.minecraft.world.entity.ai.goal.Goal PATHFINDER_RABID = new NonTameRandomTargetGoal<>(this, LivingEntity.class, false, RABID_PREDICATE); ++ private static final class AvoidRabidWolfGoal extends AvoidEntityGoal { ++ private final Wolf wolf; ++ ++ public AvoidRabidWolfGoal(Wolf wolf, float distance, double minSpeed, double maxSpeed) { ++ super(wolf, Wolf.class, distance, minSpeed, maxSpeed); ++ this.wolf = wolf; ++ } ++ ++ @Override ++ public boolean canUse() { ++ return super.canUse() && !this.wolf.isRabid() && this.toAvoid != null && this.toAvoid.isRabid(); // wolves which are not rabid run away from rabid wolves ++ } ++ ++ @Override ++ public void start() { ++ this.wolf.setTarget(null); ++ super.start(); ++ } ++ ++ @Override ++ public void tick() { ++ this.wolf.setTarget(null); ++ super.tick(); ++ } ++ } ++ // Purpur end - Configurable chance for wolves to spawn rabid + private static final float START_HEALTH = 8.0F; + private static final float TAME_HEALTH = 40.0F; + private static final float ARMOR_REPAIR_UNIT = 0.125F; +@@ -121,12 +_,47 @@ + this.setPathfindingMalus(PathType.DANGER_POWDER_SNOW, -1.0F); + } + ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ public boolean isRabid() { ++ return this.isRabid; ++ } ++ ++ public void setRabid(boolean isRabid) { ++ this.isRabid = isRabid; ++ updatePathfinders(true); ++ } ++ ++ public void updatePathfinders(boolean modifyEffects) { ++ this.targetSelector.removeGoal(PATHFINDER_VANILLA); ++ this.targetSelector.removeGoal(PATHFINDER_RABID); ++ if (this.isRabid) { ++ this.setOwnerReference(null); ++ setTame(false, true); ++ this.targetSelector.addGoal(5, PATHFINDER_RABID); ++ if (modifyEffects) this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.NAUSEA, 1200)); ++ } else { ++ this.targetSelector.addGoal(5, PATHFINDER_VANILLA); ++ this.stopBeingAngry(); ++ if (modifyEffects) this.removeEffect(net.minecraft.world.effect.MobEffects.NAUSEA); ++ } ++ } ++ // Purpur end - Configurable chance for wolves to spawn rabid ++ ++ // Purpur start - Configurable default collar color ++ @Override ++ public void tame(Player player) { ++ setCollarColor(level().purpurConfig.wolfDefaultCollarColor); ++ super.tame(player); ++ } ++ // Purpur end - Configurable default collar color ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); + this.goalSelector.addGoal(1, new TamableAnimal.TamableAnimalPanicGoal(1.5, DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES)); + this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); + this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5, 1.5)); ++ this.goalSelector.addGoal(3, new AvoidRabidWolfGoal(this, 24.0F, 1.5D, 1.5D)); // Purpur - Configurable chance for wolves to spawn rabid + this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F)); + this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, true)); + this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0, 10.0F, 2.0F)); +@@ -139,7 +_,7 @@ + this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); + this.targetSelector.addGoal(3, new HurtByTargetGoal(this).setAlertOthers()); + this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); +- this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR)); ++ //this.targetSelector.addGoal(5, new NonTameRandomTargetGoal<>(this, Animal.class, false, PREY_SELECTOR)); // Purpur - Configurable chance for wolves to spawn rabid - moved to updatePathfinders() + this.targetSelector.addGoal(6, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); + this.targetSelector.addGoal(7, new NearestAttackableTargetGoal<>(this, AbstractSkeleton.class, false)); + this.targetSelector.addGoal(8, new ResetUniversalAngerTargetGoal<>(this, true)); +@@ -229,6 +_,7 @@ + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); + output.store("CollarColor", DyeColor.LEGACY_ID_CODEC, this.getCollarColor()); ++ output.putBoolean("Purpur.IsRabid", this.isRabid); // Purpur - Configurable chance for wolves to spawn rabid + VariantUtils.writeVariant(output, this.getVariant()); + this.addPersistentAngerSaveData(output); + this.getSoundVariant() +@@ -243,6 +_,10 @@ + super.readAdditionalSaveData(input); + VariantUtils.readVariant(input, Registries.WOLF_VARIANT).ifPresent(this::setVariant); + this.setCollarColor(input.read("CollarColor", DyeColor.LEGACY_ID_CODEC).orElse(DEFAULT_COLLAR_COLOR)); ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ this.isRabid = input.getBooleanOr("Purpur.IsRabid", false); ++ this.updatePathfinders(false); ++ // Purpur end - Configurable chance for wolves to spawn rabid + this.readPersistentAngerSaveData(this.level(), input); + input.read("sound_variant", ResourceKey.codec(Registries.WOLF_SOUND_VARIANT)) + .flatMap(resourceKey -> this.registryAccess().lookupOrThrow(Registries.WOLF_SOUND_VARIANT).get((ResourceKey)resourceKey)) +@@ -266,6 +_,10 @@ + } + + this.setSoundVariant(WolfSoundVariants.pickRandomSoundVariant(this.registryAccess(), level.getRandom())); ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ this.isRabid = level.getLevel().purpurConfig.wolfNaturalRabid > 0.0D && random.nextDouble() <= level.getLevel().purpurConfig.wolfNaturalRabid; ++ this.updatePathfinders(false); ++ // Purpur end - Configurable chance for wolves to spawn rabid + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); + } + +@@ -316,6 +_,11 @@ + public void tick() { + super.tick(); + if (this.isAlive()) { ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ if (this.age % 300 == 0 && this.isRabid()) { ++ this.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.NAUSEA, 400)); ++ } ++ // Purpur end - Configurable chance for wolves to spawn rabid + this.interestedAngleO = this.interestedAngle; + if (this.isInterested()) { + this.interestedAngle = this.interestedAngle + (1.0F - this.interestedAngle) * 0.4F; +@@ -517,13 +_,27 @@ + itemInHand.consume(1, player); + this.tryToTame(player); + return InteractionResult.SUCCESS_SERVER; ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ } else if (this.level().purpurConfig.wolfMilkCuresRabies && itemInHand.getItem() == Items.MILK_BUCKET && this.isRabid()) { ++ if (!player.isCreative()) { ++ player.setItemInHand(hand, new ItemStack(Items.BUCKET)); ++ } ++ this.setRabid(false); ++ for (int i = 0; i < 10; ++i) { ++ ((ServerLevel) level()).sendParticlesSource(((ServerLevel) level()).players(), null, ParticleTypes.HAPPY_VILLAGER, ++ false, true, ++ getX() + random.nextFloat(), getY() + (random.nextFloat() * 1.5), getZ() + random.nextFloat(), 1, ++ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0); ++ } ++ return InteractionResult.SUCCESS_SERVER; ++ // Purpur end - Configurable chance for wolves to spawn rabid + } + + return super.mobInteract(player, hand); + } + + private void tryToTame(Player player) { +- if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call ++ if (((this.level().purpurConfig.alwaysTameInCreative && player.hasInfiniteMaterials()) || this.random.nextInt(3) == 0) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call // Purpur - Config to always tame in Creative + this.tame(player); + this.navigation.stop(); + this.setTarget(null); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch new file mode 100644 index 000000000..b58f13a85 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch @@ -0,0 +1,56 @@ +--- a/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +@@ -39,6 +_,24 @@ + this.setPos(x, y, z); + } + ++ // Purpur start - End crystal explosion options ++ public boolean shouldExplode() { ++ return showsBottom() ? level().purpurConfig.basedEndCrystalExplode : level().purpurConfig.baselessEndCrystalExplode; ++ } ++ ++ public float getExplosionPower() { ++ return (float) (showsBottom() ? level().purpurConfig.basedEndCrystalExplosionPower : level().purpurConfig.baselessEndCrystalExplosionPower); ++ } ++ ++ public boolean hasExplosionFire() { ++ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionFire : level().purpurConfig.baselessEndCrystalExplosionFire; ++ } ++ ++ public Level.ExplosionInteraction getExplosionEffect() { ++ return showsBottom() ? level().purpurConfig.basedEndCrystalExplosionEffect : level().purpurConfig.baselessEndCrystalExplosionEffect; ++ } ++ // Purpur end - End crystal explosion options ++ + @Override + protected Entity.MovementEmission getMovementEmission() { + return Entity.MovementEmission.NONE; +@@ -75,6 +_,8 @@ + } + } + // Paper end - Fix invulnerable end crystals ++ if (this.level().purpurConfig.endCrystalCramming > 0 && this.level().getEntitiesOfClass(EndCrystal.class, getBoundingBox()).size() > this.level().purpurConfig.endCrystalCramming) this.hurt(this.damageSources().cramming(), 6.0F); // Purpur - End Crystal Cramming ++ + } + + @Override +@@ -115,15 +_,17 @@ + } + // CraftBukkit end + if (!damageSource.is(DamageTypeTags.IS_EXPLOSION)) { ++ if (shouldExplode()) {// Purpur - End crystal explosion options + DamageSource damageSource1 = damageSource.getEntity() != null ? this.damageSources().explosion(this, damageSource.getEntity()) : null; + // CraftBukkit start +- org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, 6.0F, false); ++ org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, getExplosionPower(), hasExplosionFire()); // Purpur - End crystal explosion options + if (event.isCancelled()) { + return false; + } + + this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // Paper - add Bukkit remove cause +- level.explode(this, damageSource1, null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK); ++ level.explode(this, damageSource1, null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), getExplosionEffect()); // Purpur - End crystal explosion options ++ } else this.unsetRemoved(); // Purpur - End crystal explosion options + } else { + this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // Paper - add Bukkit remove cause + // CraftBukkit end diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch new file mode 100644 index 000000000..ca3634fcb --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -957,6 +_,7 @@ + + @Override + protected boolean canRide(Entity entity) { ++ if (this.level().purpurConfig.enderDragonCanRideVehicles) return this.boardingCooldown <= 0; // Purpur - Configs for if Wither/Ender Dragon can ride vehicles + return false; + } + +@@ -992,7 +_,7 @@ + boolean flag = level.getGameRules().get(GameRules.MOB_DROPS); + int i = 500; + +- if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) { ++ if (this.dragonFight != null && (level().purpurConfig.enderDragonAlwaysDropsFullExp || !this.dragonFight.hasPreviouslyKilledDragon())) { // Purpur - Ender dragon always drop full exp + i = 12000; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch new file mode 100644 index 000000000..e59a055c2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch @@ -0,0 +1,73 @@ +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -80,6 +_,7 @@ + private static final TargetingConditions.Selector LIVING_ENTITY_SELECTOR = (entity, level) -> !entity.getType().is(EntityTypeTags.WITHER_FRIENDS) + && entity.attackable(); + private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0).selector(LIVING_ENTITY_SELECTOR); ++ private java.util.@Nullable UUID summoner; // Purpur - Summoner API + + public WitherBoss(EntityType type, Level level) { + super(type, level); +@@ -88,6 +_,16 @@ + this.xpReward = 50; + } + ++ // Purpur start - Summoner API ++ public java.util.@Nullable UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(java.util.@Nullable UUID summoner) { ++ this.summoner = summoner; ++ } ++ // Purpur end - Summoner API ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +@@ -120,6 +_,7 @@ + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); + output.putInt("Invul", this.getInvulnerableTicks()); ++ output.storeNullable("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC, getSummoner()); // Purpur - Summoner API + } + + @Override +@@ -129,6 +_,7 @@ + if (this.hasCustomName()) { + this.bossEvent.setName(this.getDisplayName()); + } ++ this.setSummoner(input.read("Purpur.Summoner", net.minecraft.core.UUIDUtil.CODEC).orElse(null)); // Purpur - Summoner API + } + + @Override +@@ -272,7 +_,7 @@ + level.explode(this, this.getX(), this.getEyeY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); + } + // CraftBukkit end +- if (!this.isSilent()) { ++ if (!this.isSilent() && level.purpurConfig.witherPlaySpawnSound) { // Purpur - Toggle for Wither's spawn sound + // CraftBukkit start - Use relative location for far away sounds + // level.globalLevelEvent(1023, this.blockPosition(), 0); + int viewDistance = level.getCraftServer().getViewDistance() * 16; +@@ -379,8 +_,10 @@ + } + } + +- if (this.tickCount % 20 == 0) { +- this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit ++ // Purpur start - Customizable wither health and healing - customizable heal rate and amount ++ if (this.tickCount % level().purpurConfig.witherHealthRegenDelay == 0) { ++ this.heal(level().purpurConfig.witherHealthRegenAmount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit ++ // Purpur end - Customizable wither health and healing + } + + this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); +@@ -577,6 +_,7 @@ + + @Override + protected boolean canRide(Entity entity) { ++ if (this.level().purpurConfig.witherCanRideVehicles) return this.boardingCooldown <= 0; // Purpur - Configs for if Wither/Ender Dragon can ride vehicles + return false; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch new file mode 100644 index 000000000..035025719 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch @@ -0,0 +1,43 @@ +--- a/net/minecraft/world/entity/decoration/ArmorStand.java ++++ b/net/minecraft/world/entity/decoration/ArmorStand.java +@@ -92,10 +_,13 @@ + public boolean canTickSetByAPI = false; + private boolean noTickEquipmentDirty = false; + // Paper end - Allow ArmorStands not to tick ++ public boolean canMovementTick = true; // Purpur - Movement options for armor stands + + public ArmorStand(EntityType type, Level level) { + super(type, level); + if (level != null) this.canTick = level.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick ++ if (level != null) this.canMovementTick = level.purpurConfig.armorstandMovement; // Purpur - Movement options for armor stands ++ this.setShowArms(level != null && level.purpurConfig.armorstandPlaceWithArms); // Purpur - Config to show Armor Stand arms on spawn + } + + public ArmorStand(Level level, double x, double y, double z) { +@@ -522,6 +_,7 @@ + // Paper start - Allow ArmorStands not to tick + @Override + public void tick() { ++ maxUpStep = level().purpurConfig.armorstandStepHeight; // Purpur - Add option to set armorstand step height + if (!this.canTick) { + if (this.noTickEquipmentDirty) { + this.noTickEquipmentDirty = false; +@@ -810,4 +_,18 @@ + } + } + // Paper end ++ ++ // Purpur start - Movement options for armor stands ++ @Override ++ public void updateInWaterStateAndDoWaterCurrentPushing() { ++ if (this.level().purpurConfig.armorstandWaterMovement && ++ (this.level().purpurConfig.armorstandWaterFence || !(level().getBlockState(blockPosition().below()).getBlock() instanceof net.minecraft.world.level.block.FenceBlock))) ++ super.updateInWaterStateAndDoWaterCurrentPushing(); ++ } ++ ++ @Override ++ public void aiStep() { ++ if (this.canMovementTick && this.canMove) super.aiStep(); ++ } ++ // Purpur end - Movement options for armor stands + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch new file mode 100644 index 000000000..9a7d5bb73 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/world/entity/item/ItemEntity.java ++++ b/net/minecraft/world/entity/item/ItemEntity.java +@@ -54,6 +_,12 @@ + public boolean canMobPickup = true; // Paper - Item#canEntityPickup + private int despawnRate = -1; // Paper - Alternative item-despawn-rate + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API ++ // Purpur start - Item entity immunities ++ public boolean immuneToCactus = false; ++ public boolean immuneToExplosion = false; ++ public boolean immuneToFire = false; ++ public boolean immuneToLightning = false; ++ // Purpur end - Item entity immunities + + public ItemEntity(EntityType type, Level level) { + super(type, level); +@@ -330,7 +_,16 @@ + + @Override + public final boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { +- if (this.isInvulnerableToBase(damageSource)) { ++ // Purpur start - Item entity immunities ++ if ( ++ (immuneToCactus && damageSource.is(net.minecraft.world.damagesource.DamageTypes.CACTUS)) || ++ (immuneToFire && (damageSource.is(net.minecraft.tags.DamageTypeTags.IS_FIRE) || damageSource.is(net.minecraft.world.damagesource.DamageTypes.ON_FIRE) || damageSource.is(net.minecraft.world.damagesource.DamageTypes.IN_FIRE))) || ++ (immuneToLightning && damageSource.is(net.minecraft.world.damagesource.DamageTypes.LIGHTNING_BOLT)) || ++ (immuneToExplosion && damageSource.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION)) ++ ) { ++ return false; ++ } else if (this.isInvulnerableToBase(damageSource)) { ++ // Purpur end - Item entity immunities + return false; + } else if (!level.getGameRules().get(GameRules.MOB_GRIEFING) && damageSource.getEntity() instanceof Mob) { + return false; +@@ -508,6 +_,12 @@ + public void setItem(ItemStack stack) { + this.getEntityData().set(DATA_ITEM, stack); + this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate ++ // Purpur start - Item entity immunities ++ if (level().purpurConfig.itemImmuneToCactus.contains(stack.getItem())) immuneToCactus = true; ++ if (level().purpurConfig.itemImmuneToExplosion.contains(stack.getItem())) immuneToExplosion = true; ++ if (level().purpurConfig.itemImmuneToFire.contains(stack.getItem())) immuneToFire = true; ++ if (level().purpurConfig.itemImmuneToLightning.contains(stack.getItem())) immuneToLightning = true; ++ // level end - Item entity immunities + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch new file mode 100644 index 000000000..a3308c373 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch @@ -0,0 +1,35 @@ +--- a/net/minecraft/world/entity/item/PrimedTnt.java ++++ b/net/minecraft/world/entity/item/PrimedTnt.java +@@ -235,4 +_,32 @@ + return !this.level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid(); + } + // Paper end - Option to prevent TNT from moving in water ++ ++ // Purpur start - Shears can defuse TNT ++ @Override ++ public net.minecraft.world.InteractionResult interact(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { ++ Level world = this.level(); ++ ++ if (world instanceof ServerLevel serverWorld && level().purpurConfig.shearsCanDefuseTnt) { ++ final net.minecraft.world.item.ItemStack inHand = player.getItemInHand(hand); ++ ++ if (!inHand.is(net.minecraft.world.item.Items.SHEARS) || !player.getBukkitEntity().hasPermission("purpur.tnt.defuse") || ++ serverWorld.random.nextFloat() > serverWorld.purpurConfig.shearsCanDefuseTntChance) return net.minecraft.world.InteractionResult.PASS; ++ ++ net.minecraft.world.entity.item.ItemEntity tntItem = new net.minecraft.world.entity.item.ItemEntity(serverWorld, getX(), getY(), getZ(), ++ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.TNT)); ++ tntItem.setPickUpDelay(10); ++ ++ inHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); ++ serverWorld.addFreshEntity(tntItem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CUSTOM); ++ ++ this.playSound(net.minecraft.sounds.SoundEvents.SHEEP_SHEAR); ++ ++ this.kill(serverWorld); ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } ++ ++ return super.interact(player, hand); ++ } ++ // Purpur end - Shears can defuse TNT + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch new file mode 100644 index 000000000..d27c4e8c7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch @@ -0,0 +1,65 @@ +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -56,6 +_,7 @@ + public int explosionRadius = 3; + public boolean droppedSkulls; + public @Nullable Entity entityIgniter; // CraftBukkit ++ private boolean exploding = false; // Purpur - Config to make Creepers explode on death + + public Creeper(EntityType type, Level level) { + super(type, level); +@@ -159,6 +_,27 @@ + return false; // CraftBukkit + } + ++ // Purpur start - Special mobs naturally spawn ++ @Override ++ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, net.minecraft.world.entity.@Nullable SpawnGroupData entityData) { ++ double chance = world.getLevel().purpurConfig.creeperChargedChance; ++ if (chance > 0D && random.nextDouble() <= chance) { ++ setPowered(true); ++ } ++ return super.finalizeSpawn(world, difficulty, spawnReason, entityData); ++ } ++ // Purpur end - Special mobs naturally spawn ++ ++ // Purpur start - Config to make Creepers explode on death ++ @Override ++ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel world, DamageSource damageSource) { ++ if (!this.exploding && this.level().purpurConfig.creeperExplodeWhenKilled && damageSource.getEntity() instanceof net.minecraft.server.level.ServerPlayer) { ++ this.explodeCreeper(); ++ } ++ return super.dropAllDeathLoot(world, damageSource); ++ } ++ // Purpur end - Config to make Creepers explode on death ++ + @Override + public SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.CREEPER_HURT; +@@ -243,14 +_,16 @@ + } + + public void explodeCreeper() { ++ this.exploding = true; // Purpur - Config to make Creepers explode on death + if (this.level() instanceof ServerLevel serverLevel) { + float f = this.isPowered() ? 2.0F : 1.0F; ++ float multiplier = serverLevel.purpurConfig.creeperHealthRadius ? this.getHealth() / this.getMaxHealth() : 1; // Purpur - Config for health to impact Creeper explosion radius + // CraftBukkit start +- org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * f, false); ++ org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, (this.explosionRadius * f) * multiplier, false); // Purpur - Config for health to impact Creeper explosion radius + if (!event.isCancelled()) { + // CraftBukkit end + this.dead = true; +- serverLevel.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this) ++ serverLevel.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), serverLevel.getGameRules().get(net.minecraft.world.level.gamerules.GameRules.MOB_GRIEFING) && level().purpurConfig.creeperAllowGriefing ? Level.ExplosionInteraction.MOB : Level.ExplosionInteraction.NONE); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this) // Purpur - Add enderman and creeper griefing controls + this.spawnLingeringCloud(); + this.triggerOnDeathMobEffects(serverLevel, Entity.RemovalReason.KILLED); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause +@@ -261,6 +_,7 @@ + } + // CraftBukkit end + } ++ this.exploding = false; // Purpur - Config to make Creepers explode on death + } + + private void spawnLingeringCloud() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch new file mode 100644 index 000000000..7479b47dc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch @@ -0,0 +1,61 @@ +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -101,7 +_,7 @@ + this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this)); + this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt)); + this.targetSelector.addGoal(2, new HurtByTargetGoal(this)); +- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false)); ++ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, 10, true, false, (entityliving, ignored) -> entityliving.level().purpurConfig.endermanAggroEndermites && entityliving instanceof Endermite endermite && (!entityliving.level().purpurConfig.endermanAggroEndermitesOnlyIfPlayerSpawned || endermite.isPlayerSpawned()))); // Purpur + this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false)); + } + +@@ -220,7 +_,7 @@ + + boolean isBeingStaredBy(Player player) { + // Paper start - EndermanAttackPlayerEvent +- final boolean shouldAttack = this.isBeingStaredBy0(player); ++ final boolean shouldAttack = !this.level().purpurConfig.endermanDisableStareAggro && this.isBeingStaredBy0(player); // Purpur - Config to ignore Dragon Head wearers and stare aggro + final com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) player.getBukkitEntity()); + event.setCancelled(!shouldAttack); + return event.callEvent(); +@@ -375,6 +_,7 @@ + public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + if (this.isInvulnerableTo(level, damageSource)) { + return false; ++ } else if (org.purpurmc.purpur.PurpurConfig.endermanShortHeight && damageSource.is(net.minecraft.world.damagesource.DamageTypes.IN_WALL)) { return false; // Purpur - no suffocation damage if short height - Short enderman height + } else { + AbstractThrownPotion abstractThrownPotion1 = damageSource.getDirectEntity() instanceof AbstractThrownPotion abstractThrownPotion + ? abstractThrownPotion +@@ -391,6 +_,7 @@ + } else { + boolean flag = abstractThrownPotion1 != null && this.hurtWithCleanWater(level, damageSource, abstractThrownPotion1, amount); + ++ if (!flag && level.purpurConfig.endermanIgnoreProjectiles) return super.hurtServer(level, damageSource, amount); // Purpur - Config to disable Enderman teleport on projectile hit + if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent + for (int i = 0; i < 64; i++) { + if (this.teleport()) { +@@ -434,7 +_,7 @@ + + @Override + public boolean requiresCustomPersistence() { +- return super.requiresCustomPersistence() || this.getCarriedBlock() != null; ++ return super.requiresCustomPersistence() || (!this.level().purpurConfig.endermanDespawnEvenWithBlock && this.getCarriedBlock() != null); // Purpur - Add config for allowing Endermen to despawn even while holding a block + } + + static class EndermanFreezeWhenLookedAt extends Goal { +@@ -477,6 +_,7 @@ + + @Override + public boolean canUse() { ++ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls + return this.enderman.getCarriedBlock() != null + && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING) + && this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; +@@ -625,6 +_,7 @@ + + @Override + public boolean canUse() { ++ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls + return this.enderman.getCarriedBlock() == null + && getServerLevel(this.enderman).getGameRules().get(GameRules.MOB_GRIEFING) + && this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Endermite.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Endermite.java.patch new file mode 100644 index 000000000..a399138fa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Endermite.java.patch @@ -0,0 +1,41 @@ +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -30,12 +_,23 @@ + private static final int MAX_LIFE = 2400; + private static final int DEFAULT_LIFE = 0; + public int life = 0; ++ private boolean isPlayerSpawned; // Purpur - Add back player spawned endermite API + + public Endermite(EntityType type, Level level) { + super(type, level); + this.xpReward = 3; + } + ++ // Purpur start - Add back player spawned endermite API ++ public boolean isPlayerSpawned() { ++ return this.isPlayerSpawned; ++ } ++ ++ public void setPlayerSpawned(boolean playerSpawned) { ++ this.isPlayerSpawned = playerSpawned; ++ } ++ // Purpur end - Add back player spawned endermite API ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new FloatGoal(this)); +@@ -81,12 +_,14 @@ + protected void readAdditionalSaveData(ValueInput input) { + super.readAdditionalSaveData(input); + this.life = input.getIntOr("Lifetime", 0); ++ this.isPlayerSpawned = input.getBooleanOr("PlayerSpawned", false); // Purpur - Add back player spawned endermite API + } + + @Override + protected void addAdditionalSaveData(ValueOutput output) { + super.addAdditionalSaveData(output); + output.putInt("Lifetime", this.life); ++ output.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur - Add back player spawned endermite API + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch new file mode 100644 index 000000000..c05e83d84 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -156,6 +_,11 @@ + public static boolean checkGhastSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return level.getDifficulty() != Difficulty.PEACEFUL && random.nextInt(20) == 0 && checkMobSpawnRules(entityType, level, spawnReason, pos, random); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch new file mode 100644 index 000000000..c85899c80 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -312,6 +_,11 @@ + public static boolean checkGuardianSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return (random.nextInt(20) == 0 || !level.canSeeSkyFromBelowWater(pos)) + && level.getDifficulty() != Difficulty.PEACEFUL + && (EntitySpawnReason.isSpawner(spawnReason) || level.getFluidState(pos).is(FluidTags.WATER)) diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/MagmaCube.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/MagmaCube.java.patch new file mode 100644 index 000000000..358fa1924 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/MagmaCube.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/MagmaCube.java ++++ b/net/minecraft/world/entity/monster/MagmaCube.java +@@ -31,6 +_,11 @@ + public static boolean checkMagmaCubeSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return level.getDifficulty() != Difficulty.PEACEFUL; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch new file mode 100644 index 000000000..6db59e5bc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch @@ -0,0 +1,39 @@ +--- a/net/minecraft/world/entity/monster/Monster.java ++++ b/net/minecraft/world/entity/monster/Monster.java +@@ -84,6 +_,11 @@ + } + + public static boolean isDarkEnoughToSpawn(ServerLevelAccessor level, BlockPos pos, RandomSource random) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + if (level.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) { + return false; + } else { +@@ -109,6 +_,11 @@ + public static boolean checkAnyLightMonsterSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return level.getDifficulty() != Difficulty.PEACEFUL && checkMobSpawnRules(entityType, level, spawnReason, pos, random); + } + +@@ -146,4 +_,12 @@ + return ItemStack.EMPTY; + } + } ++ ++ // Purpur start - Config to disable hostile mob spawn on ice ++ public static boolean canSpawnInBlueAndPackedIce(LevelAccessor level, BlockPos pos) { ++ net.minecraft.world.level.block.state.BlockState spawnBlock = level.getBlockState(pos.below()); ++ ++ return (!level.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.PACKED_ICE)) || (!level.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce && spawnBlock.is(net.minecraft.world.level.block.Blocks.BLUE_ICE)); ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch new file mode 100644 index 000000000..9f60b3712 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -166,7 +_,11 @@ + ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData + ) { + this.anchorPoint = this.blockPosition().above(5); +- this.setPhantomSize(0); ++ // Purpur start - Configurable phantom size ++ int min = level.getLevel().purpurConfig.phantomMinSize; ++ int max = level.getLevel().purpurConfig.phantomMaxSize; ++ this.setPhantomSize(min == max ? min : level.getRandom().nextInt(max + 1 - min) + min); ++ // Purpur end - Configurable phantom size + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch new file mode 100644 index 000000000..7173a96a0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -76,6 +_,7 @@ + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new FloatGoal(this)); ++ if (level().purpurConfig.ravagerAvoidRabbits) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.AvoidEntityGoal<>(this, net.minecraft.world.entity.animal.rabbit.Rabbit.class, 6.0F, 1.0D, 1.2D)); // Purpur - option to make ravagers afraid of rabbits + this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true)); + this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4)); + this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F)); +@@ -154,7 +_,7 @@ + )) { + BlockState blockState = serverLevel.getBlockState(blockPos); + Block block = blockState.getBlock(); +- if (block instanceof LeavesBlock) { ++ if (this.level().purpurConfig.ravagerGriefableBlocks.contains(block)) { // Purpur - Configurable ravager griefable blocks list + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, blockState.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + continue; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch new file mode 100644 index 000000000..21c6840c6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch @@ -0,0 +1,59 @@ +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -93,6 +_,21 @@ + this.lookControl = new Shulker.ShulkerLookControl(this); + } + ++ // Purpur start - Shulker change color with dye ++ @Override ++ protected net.minecraft.world.InteractionResult mobInteract(Player player, net.minecraft.world.InteractionHand hand) { ++ net.minecraft.world.item.ItemStack itemstack = player.getItemInHand(hand); ++ if (player.level().purpurConfig.shulkerChangeColorWithDye && itemstack.getItem() instanceof net.minecraft.world.item.DyeItem dye && dye.getDyeColor() != this.getColor()) { ++ this.setVariant(Optional.of(dye.getDyeColor())); ++ if (!player.getAbilities().instabuild) { ++ itemstack.shrink(1); ++ } ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } ++ return super.mobInteract(player, hand); ++ } ++ // Purpur end - Shulker change color with dye ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(1, new LookAtPlayerGoal(this, Player.class, 8.0F, 0.02F, true)); +@@ -454,11 +_,21 @@ + private void hitByShulkerBullet() { + Vec3 vec3 = this.position(); + AABB boundingBox = this.getBoundingBox(); +- if (!this.isClosed() && this.teleportSomewhere()) { +- int size = this.level().getEntities(EntityType.SHULKER, boundingBox.inflate(8.0), Entity::isAlive).size(); +- float f = (size - 1) / 5.0F; +- if (!(this.level().random.nextFloat() < f)) { ++ // Purpur start - Shulker spawn from bullet options ++ if ((!this.level().purpurConfig.shulkerSpawnFromBulletRequireOpenLid || !this.isClosed()) && this.teleportSomewhere()) { ++ float chance = this.level().purpurConfig.shulkerSpawnFromBulletBaseChance; ++ if (!this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation.isBlank()) { ++ int nearby = this.level().getEntities((net.minecraft.world.level.entity.EntityTypeTest) EntityType.SHULKER, boundingBox.inflate(this.level().purpurConfig.shulkerSpawnFromBulletNearbyRange), Entity::isAlive).size(); ++ try { ++ chance -= ((Number) scriptEngine.eval("let nearby = " + nearby + "; " + this.level().purpurConfig.shulkerSpawnFromBulletNearbyEquation)).floatValue(); ++ } catch (javax.script.ScriptException e) { ++ e.printStackTrace(); ++ chance -= (nearby - 1) / 5.0F; ++ } ++ } ++ if (this.level().random.nextFloat() <= chance) { + Shulker shulker = EntityType.SHULKER.create(this.level(), EntitySpawnReason.BREEDING); ++ // Purpur end - Shulker spawn from bullet options + if (shulker != null) { + shulker.setVariant(this.getVariant()); + shulker.snapTo(vec3); +@@ -565,7 +_,7 @@ + } + + public Optional getVariant() { +- return Optional.ofNullable(this.getColor()); ++ return Optional.ofNullable(this.level().purpurConfig.shulkerSpawnFromBulletRandomColor ? DyeColor.random(this.level().random) : this.getColor()); // Purpur - Shulker spawn from bullet options + } + + public @Nullable DyeColor getColor() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Slime.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Slime.java.patch new file mode 100644 index 000000000..7963b593d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Slime.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -302,6 +_,11 @@ + public static boolean checkSlimeSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + if (level.getDifficulty() != Difficulty.PEACEFUL) { + if (EntitySpawnReason.isSpawner(spawnReason)) { + return checkMobSpawnRules(entityType, level, spawnReason, pos, random); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Strider.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Strider.java.patch new file mode 100644 index 000000000..86f2a97ff --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Strider.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -390,6 +_,18 @@ + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { + boolean isFood = this.isFood(player.getItemInHand(hand)); ++ // Purpur start ++ if (level().purpurConfig.striderGiveSaddleBack && player.isSecondaryUseActive() && !isFood && isSaddled() && !isVehicle()) { ++ this.setItemSlot(EquipmentSlot.SADDLE, ItemStack.EMPTY); ++ if (!player.getAbilities().instabuild) { ++ ItemStack saddle = new ItemStack(Items.SADDLE); ++ if (!player.getInventory().add(saddle)) { ++ player.drop(saddle, false); ++ } ++ } ++ return InteractionResult.SUCCESS; ++ } ++ // Purpur end + if (!isFood && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) { + if (!this.level().isClientSide()) { + player.startRiding(this); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch new file mode 100644 index 000000000..0034248ca --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -202,6 +_,11 @@ + public static boolean checkHoglinSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (net.minecraft.world.entity.monster.Monster.canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch new file mode 100644 index 000000000..a79df0bf1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/illager/Vindicator.java ++++ b/net/minecraft/world/entity/monster/illager/Vindicator.java +@@ -131,6 +_,11 @@ + RandomSource random = level.getRandom(); + this.populateDefaultEquipmentSlots(random, difficulty); + this.populateDefaultEquipmentEnchantments(level, random, difficulty); ++ // Purpur start - Special mobs naturally spawn ++ if (level().purpurConfig.vindicatorJohnnySpawnChance > 0D && random.nextDouble() <= level().purpurConfig.vindicatorJohnnySpawnChance) { ++ setCustomName(Component.translatable("Johnny")); ++ } ++ // Purpur end - Special mobs naturally spawn + return spawnGroupData1; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch new file mode 100644 index 000000000..b8e4bf183 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -208,6 +_,11 @@ + public static boolean checkPiglinSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch new file mode 100644 index 000000000..931363770 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/entity/monster/piglin/PiglinAi.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinAi.java +@@ -667,13 +_,23 @@ + + public static boolean isWearingSafeArmor(LivingEntity entity) { + for (EquipmentSlot equipmentSlot : EquipmentSlotGroup.ARMOR) { +- if (entity.getItemBySlot(equipmentSlot).is(ItemTags.PIGLIN_SAFE_ARMOR)) { ++ // Purpur start - piglins ignore gold-trimmed armor ++ net.minecraft.world.item.ItemStack itemStack = entity.getItemBySlot(equipmentSlot); ++ if (itemStack.is(ItemTags.PIGLIN_SAFE_ARMOR) || (entity.level().purpurConfig.piglinIgnoresArmorWithGoldTrim && isWearingGoldTrim(itemStack))) { ++ // Purpur end - piglins ignore gold-trimmed armor + return true; + } + } + + return false; + } ++ ++ // Purpur start - piglins ignore gold-trimmed armor ++ private static boolean isWearingGoldTrim(net.minecraft.world.item.ItemStack itemstack) { ++ net.minecraft.world.item.equipment.trim.ArmorTrim armorTrim = itemstack.getComponents().get(net.minecraft.core.component.DataComponents.TRIM); ++ return armorTrim != null && armorTrim.material().is(net.minecraft.world.item.equipment.trim.TrimMaterials.GOLD); ++ } ++ // Purpur end - piglins ignore gold-trimmed armor + + private static void stopWalking(Piglin piglin) { + piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch new file mode 100644 index 000000000..e5dcc11bf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +@@ -137,7 +_,7 @@ + this.populateDefaultEquipmentEnchantments(level, random, difficulty); + this.reassessWeaponGoal(); + this.setCanPickUpLoot(level.getLevel().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || random.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper - Add world settings for mobs picking up loot +- if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && SpecialDates.isHalloween() && random.nextFloat() < 0.25F) { ++ if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && (level.getLevel().purpurConfig.forceHalloweenSeason || SpecialDates.isHalloween()) && random.nextFloat() < this.level().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur - Halloween options and optimizations + this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); + this.setDropChance(EquipmentSlot.HEAD, 0.0F); + } +@@ -184,7 +_,7 @@ + double squareRoot = Math.sqrt(d * d + d2 * d2); + if (this.level() instanceof ServerLevel serverLevel) { + Projectile.Delayed delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed +- arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 ++ arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, serverLevel.purpurConfig.skeletonBowAccuracyMap.getOrDefault(serverLevel.getDifficulty().getId(), (float) (14 - serverLevel.getDifficulty().getId() * 4)) // Purpur - skeleton bow accuracy option + ); + + // Paper start - call EntityShootBowEvent diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch new file mode 100644 index 000000000..5982e0484 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/world/entity/monster/skeleton/Skeleton.java ++++ b/net/minecraft/world/entity/monster/skeleton/Skeleton.java +@@ -130,4 +_,64 @@ + SoundEvent getStepSound() { + return SoundEvents.SKELETON_STEP; + } ++ ++ // Purpur start - Skeletons eat wither roses ++ private int witherRosesFed = 0; ++ ++ @Override ++ public net.minecraft.world.InteractionResult mobInteract(net.minecraft.world.entity.player.Player player, net.minecraft.world.InteractionHand hand) { ++ net.minecraft.world.item.ItemStack stack = player.getItemInHand(hand); ++ ++ if (level().purpurConfig.skeletonFeedWitherRoses > 0 && this.getType() != EntityType.WITHER_SKELETON && stack.getItem() == net.minecraft.world.level.block.Blocks.WITHER_ROSE.asItem()) { ++ return this.feedWitherRose(player, stack); ++ } ++ ++ return super.mobInteract(player, hand); ++ } ++ ++ private net.minecraft.world.InteractionResult feedWitherRose(net.minecraft.world.entity.player.Player player, net.minecraft.world.item.ItemStack stack) { ++ if (++witherRosesFed < level().purpurConfig.skeletonFeedWitherRoses) { ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ ++ WitherSkeleton skeleton = EntityType.WITHER_SKELETON.create(level(), net.minecraft.world.entity.EntitySpawnReason.CONVERSION); ++ if (skeleton == null) { ++ return net.minecraft.world.InteractionResult.PASS; ++ } ++ ++ skeleton.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); ++ skeleton.setHealth(this.getHealth()); ++ skeleton.setAggressive(this.isAggressive()); ++ skeleton.copyPosition(this); ++ skeleton.setYBodyRot(this.yBodyRot); ++ skeleton.setYHeadRot(this.getYHeadRot()); ++ skeleton.yRotO = this.yRotO; ++ skeleton.xRotO = this.xRotO; ++ ++ if (this.hasCustomName()) { ++ skeleton.setCustomName(this.getCustomName()); ++ } ++ ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, skeleton, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { ++ return net.minecraft.world.InteractionResult.PASS; ++ } ++ ++ this.level().addFreshEntity(skeleton); ++ this.remove(RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(1); ++ } ++ ++ for (int i = 0; i < 15; ++i) { ++ ((net.minecraft.server.level.ServerLevel) level()).sendParticlesSource(((net.minecraft.server.level.ServerLevel) level()).players(), null, net.minecraft.core.particles.ParticleTypes.HAPPY_VILLAGER, ++ false, true, ++ getX() + random.nextFloat(), getY() + (random.nextFloat() * 2), getZ() + random.nextFloat(), 1, ++ random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, random.nextGaussian() * 0.05D, 0); ++ } ++ return net.minecraft.world.InteractionResult.SUCCESS; ++ } ++ // Purpur end - Skeletons eat wither roses + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/warden/WardenAi.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/warden/WardenAi.java.patch new file mode 100644 index 000000000..6c45d2405 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/warden/WardenAi.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/entity/monster/warden/WardenAi.java ++++ b/net/minecraft/world/entity/monster/warden/WardenAi.java +@@ -175,15 +_,16 @@ + brain.addActivityAndRemoveMemoryWhenStopped( + Activity.FIGHT, + 10, +- ImmutableList.of( ++ ImmutableList.copyOf(java.util.stream.Stream.>of( // Purpur - configurable warden sonic boom + DIG_COOLDOWN_SETTER, + StopAttackingIfTargetInvalid.create( + (level, entity) -> !warden.getAngerLevel().isAngry() || !warden.canTargetEntity(entity), WardenAi::onTargetInvalid, false + ), + SetEntityLookTarget.create(entity -> isTarget(warden, entity), (float)warden.getAttributeValue(Attributes.FOLLOW_RANGE)), + SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.2F), +- new SonicBoom(), ++ warden.level().purpurConfig.wardenCanUseSonicBoom ? new SonicBoom() : null, // Purpur - configurable warden sonic boom + MeleeAttack.create(18) ++ ).filter(java.util.Objects::nonNull).toList() // Purpur - configurable warden sonic boom + ), + MemoryModuleType.ATTACK_TARGET + ); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch new file mode 100644 index 000000000..2f3581f1e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/entity/monster/zombie/Drowned.java ++++ b/net/minecraft/world/entity/monster/zombie/Drowned.java +@@ -86,10 +_,23 @@ + this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0, false)); + this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0)); + this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0, this.level().getSeaLevel())); ++ if (level().purpurConfig.drownedBreakDoors) this.goalSelector.addGoal(6, new net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors)); // Purpur - Option to make drowned break doors + this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0)); + this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Drowned.class).setAlertOthers(ZombifiedPiglin.class)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entity, level) -> this.okTarget(entity))); +- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper - Check drowned for villager aggression config ++ // Purpur start - Add option to disable zombie aggressiveness towards villagers ++ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Paper - Check drowned for villager aggression config ++ @Override ++ public boolean canUse() { ++ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse(); ++ } ++ ++ @Override ++ public boolean canContinueToUse() { ++ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse(); ++ } ++ }); ++ // Purpur end - Add option to disable zombie aggressiveness towards villagers + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false)); + this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch new file mode 100644 index 000000000..375b49003 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/entity/monster/zombie/Zombie.java ++++ b/net/minecraft/world/entity/monster/zombie/Zombie.java +@@ -118,7 +_,19 @@ + this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0)); + this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class)); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); +- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Spigot ++ // Purpur start - Add option to disable zombie aggressiveness towards villagers ++ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false) { // Spigot ++ @Override ++ public boolean canUse() { ++ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canUse(); ++ } ++ ++ @Override ++ public boolean canContinueToUse() { ++ return (level().purpurConfig.zombieAggressiveTowardsVillagerWhenLagging || !level().getServer().server.isLagging()) && super.canContinueToUse(); ++ } ++ }); ++ // Purpur end - Add option to disable zombie aggressiveness towards villagers + this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); + this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); + } +@@ -524,7 +_,7 @@ + } + } + +- if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && SpecialDates.isHalloween() && random.nextFloat() < 0.25F) { ++ if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && (level.getLevel().purpurConfig.forceHalloweenSeason || SpecialDates.isHalloween()) && random.nextFloat() < level.getLevel().purpurConfig.chanceHeadHalloweenOnEntity) { // Purpur - Halloween options and optimizations + this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); + this.setDropChance(EquipmentSlot.HEAD, 0.0F); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch new file mode 100644 index 000000000..fd9bd006c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +@@ -159,10 +_,10 @@ + public InteractionResult mobInteract(Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + if (itemInHand.is(Items.GOLDEN_APPLE)) { +- if (this.hasEffect(MobEffects.WEAKNESS)) { ++ if (this.hasEffect(MobEffects.WEAKNESS) && level().purpurConfig.zombieVillagerCureEnabled) { // Purpur - Add option to disable zombie villagers cure + itemInHand.consume(1, player); + if (!this.level().isClientSide()) { +- this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600); ++ this.startConverting(player.getUUID(), this.random.nextInt(level().purpurConfig.zombieVillagerCuringTimeMax - level().purpurConfig.zombieVillagerCuringTimeMin + 1) + level().purpurConfig.zombieVillagerCuringTimeMin); // Purpur - Customizable Zombie Villager curing times + } + + return InteractionResult.SUCCESS_SERVER; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch new file mode 100644 index 000000000..0dae756c2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch @@ -0,0 +1,40 @@ +--- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +@@ -109,6 +_,12 @@ + this.maybeAlertOthers(); + } + ++ // Purpur start - Toggle for Zombified Piglin death always counting as player kill when angry ++ if (this.isAngry() && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { ++ this.lastHurtByPlayerMemoryTime = this.tickCount; ++ } ++ // Purpur end - Toggle for Zombified Piglin death always counting as player kill when angry ++ + super.customServerAiStep(level); + } + +@@ -156,6 +_,12 @@ + this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random); + } + ++ // Purpur start - Toggle for Zombified Piglin death always counting as player kill when angry ++ if (target instanceof Player player && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { ++ this.setLastHurtByPlayer(player, this.tickCount); ++ } ++ // Purpur end - Toggle for Zombified Piglin death always counting as player kill when angry ++ + return super.setTarget(target, reason); // CraftBukkit + } + +@@ -176,6 +_,11 @@ + public static boolean checkZombifiedPiglinSpawnRules( + EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (canSpawnInBlueAndPackedIce(level, pos)) { ++ return false; ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + return level.getDifficulty() != Difficulty.PEACEFUL && !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch new file mode 100644 index 000000000..6d573d3e2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch @@ -0,0 +1,41 @@ +--- a/net/minecraft/world/entity/npc/CatSpawner.java ++++ b/net/minecraft/world/entity/npc/CatSpawner.java +@@ -23,7 +_,7 @@ + public void tick(ServerLevel level, boolean spawnEnemies) { + this.nextTick--; + if (this.nextTick <= 0) { +- this.nextTick = 1200; ++ this.nextTick = level.purpurConfig.catSpawnDelay; // Purpur - Cat spawning options + Player randomPlayer = level.getRandomPlayer(); + if (randomPlayer != null) { + RandomSource randomSource = level.random; +@@ -45,9 +_,12 @@ + } + + private void spawnInVillage(ServerLevel level, BlockPos pos) { +- int i = 48; +- if (level.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { +- List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(48.0, 8.0, 48.0)); ++ // Purpur start - Cat spawning options ++ int range = level.purpurConfig.catSpawnVillageScanRange; ++ if (range <= 0) return; ++ if (level.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { ++ List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + if (entitiesOfClass.size() < 5) { + this.spawnCat(pos, level, false); + } +@@ -55,8 +_,11 @@ + } + + private void spawnInHut(ServerLevel level, BlockPos pos) { +- int i = 16; +- List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(16.0, 8.0, 16.0)); ++ // Purpur start - Cat spawning options ++ int range = level.purpurConfig.catSpawnSwampHutScanRange; ++ if (range <= 0) return; ++ List entitiesOfClass = level.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + if (entitiesOfClass.isEmpty()) { + this.spawnCat(pos, level, true); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch new file mode 100644 index 000000000..c3dd42b60 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch @@ -0,0 +1,141 @@ +--- a/net/minecraft/world/entity/npc/villager/Villager.java ++++ b/net/minecraft/world/entity/npc/villager/Villager.java +@@ -179,6 +_,8 @@ + MemoryModuleType.MEETING_POINT, + (villager, poiType) -> poiType.is(PoiTypes.MEETING) + ); ++ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur - Lobotomize stuck villagers ++ private int notLobotomizedCount = 0; // Purpur - Lobotomize stuck villagers + + public Villager(EntityType type, Level level) { + this(type, level, VillagerType.PLAINS); +@@ -197,6 +_,57 @@ + this.setVillagerData(this.getVillagerData().withType(villagerType).withProfession(level.registryAccess(), VillagerProfession.NONE)); + } + ++ // Purpur start - Allow leashing villagers ++ @Override ++ public boolean canBeLeashed() { ++ return level().purpurConfig.villagerCanBeLeashed; ++ } ++ // Purpur end - Allow leashing villagers ++ ++ // Purpur start - Lobotomize stuck villagers ++ private boolean checkLobotomized() { ++ int interval = this.level().purpurConfig.villagerLobotomizeCheckInterval; ++ boolean shouldCheckForTradeLocked = this.level().purpurConfig.villagerLobotomizeWaitUntilTradeLocked; ++ if (this.notLobotomizedCount > 3) { ++ // check half as often if not lobotomized for the last 3+ consecutive checks ++ interval *= 2; ++ } ++ if (this.level().getGameTime() % interval == 0) { ++ // offset Y for short blocks like dirt_path/farmland ++ this.isLobotomized = !(shouldCheckForTradeLocked && this.getVillagerXp() == 0) && !canTravelFrom(BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z)); ++ ++ if (this.isLobotomized) { ++ this.notLobotomizedCount = 0; ++ } else { ++ this.notLobotomizedCount++; ++ } ++ } ++ return this.isLobotomized; ++ } ++ ++ private boolean canTravelFrom(BlockPos pos) { ++ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south()); ++ } ++ ++ private boolean canTravelTo(BlockPos pos) { ++ net.minecraft.world.level.block.state.BlockState state = this.level().getBlockStateIfLoaded(pos); ++ if (state == null) { ++ // chunk not loaded ++ return false; ++ } ++ net.minecraft.world.level.block.Block bottom = state.getBlock(); ++ if (bottom instanceof net.minecraft.world.level.block.FenceBlock || ++ bottom instanceof net.minecraft.world.level.block.FenceGateBlock || ++ bottom instanceof net.minecraft.world.level.block.WallBlock) { ++ // bottom block is too tall to get over ++ return false; ++ } ++ net.minecraft.world.level.block.Block top = level().getBlockState(pos.above()).getBlock(); ++ // only if both blocks have no collision ++ return !bottom.hasCollision && !top.hasCollision; ++ } ++ // Purpur end - Lobotomize stuck villagers ++ + @Override + public Brain getBrain() { + return (Brain)super.getBrain(); +@@ -293,11 +_,22 @@ + // Paper start - EAR 2 + this.customServerAiStep(level, false); + } +- protected void customServerAiStep(ServerLevel level, final boolean inactive) { ++ protected void customServerAiStep(ServerLevel level, boolean inactive) { // Purpur - Lobotomize stuck villagers - not final + // Paper end - EAR 2 + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("villagerBrain"); +- if (!inactive) this.getBrain().tick(level, this); // Paper - EAR 2 ++ // Purpur start - Lobotomize stuck villagers ++ if (this.level().purpurConfig.villagerLobotomizeEnabled) { ++ // treat as inactive if lobotomized ++ inactive = inactive || checkLobotomized(); ++ } else { ++ this.isLobotomized = false; ++ } ++ if (!inactive) { ++ this.getBrain().tick(level, this); // Paper - EAR 2 ++ } ++ else if (this.isLobotomized && shouldRestock(level)) restock(); ++ // Purpur end - Lobotomize stuck villagers + profilerFiller.pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +@@ -369,6 +_,7 @@ + return InteractionResult.CONSUME; + } + ++ if (this.level().purpurConfig.villagerAllowTrading) // Purpur - Add config for villager trading + this.startTrading(player); + } + +@@ -500,7 +_,7 @@ + + public void updateDemand() { + for (MerchantOffer merchantOffer : this.getOffers()) { +- merchantOffer.updateDemand(); ++ merchantOffer.updateDemand(this.level().purpurConfig.villagerMinimumDemand); // Purpur - Configurable minimum demand for trades + } + } + +@@ -692,7 +_,7 @@ + + @Override + public boolean canBreed() { +- return this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; ++ return this.level().purpurConfig.villagerCanBreed && this.foodLevel + this.countFoodPointsInInventory() >= 12 && !this.isSleeping() && this.getAge() == 0; // Purpur - Configurable villager breeding + } + + private boolean hungry() { +@@ -915,6 +_,7 @@ + } + + public void spawnGolemIfNeeded(ServerLevel level, long gameTime, int minVillagerAmount) { ++ if (level.purpurConfig.villagerSpawnIronGolemRadius > 0 && level.getEntitiesOfClass(net.minecraft.world.entity.animal.golem.IronGolem.class, getBoundingBox().inflate(level.purpurConfig.villagerSpawnIronGolemRadius)).size() > level.purpurConfig.villagerSpawnIronGolemLimit) return; // Purpur - Implement configurable search radius for villagers to spawn iron golems + if (this.wantsToSpawnGolem(gameTime)) { + AABB aabb = this.getBoundingBox().inflate(10.0, 10.0, 10.0); + List entitiesOfClass = level.getEntitiesOfClass(Villager.class, aabb); +@@ -982,6 +_,12 @@ + + @Override + public void startSleeping(BlockPos pos) { ++ // Purpur start - Option for beds to explode on villager sleep ++ if (level().purpurConfig.bedExplodeOnVillagerSleep && this.level().getBlockState(pos).getBlock() instanceof net.minecraft.world.level.block.BedBlock) { ++ this.level().explode(null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (float) this.level().purpurConfig.bedExplosionPower, this.level().purpurConfig.bedExplosionFire, this.level().purpurConfig.bedExplosionEffect); ++ return; ++ } ++ // Purpur end - Option for beds to explode on villager sleep + super.startSleeping(pos); + this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime()); + this.brain.eraseMemory(MemoryModuleType.WALK_TARGET); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch new file mode 100644 index 000000000..373eaf1a9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java +@@ -61,6 +_,13 @@ + super(type, level); + } + ++ // Purpur start - Allow leashing villagers ++ @Override ++ public boolean canBeLeashed() { ++ return level().purpurConfig.wanderingTraderCanBeLeashed; ++ } ++ // Purpur end - Allow leashing villagers ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +@@ -81,7 +_,7 @@ + this, + new ItemStack(Items.MILK_BUCKET), + SoundEvents.WANDERING_TRADER_REAPPEARED, +- wanderingTrader -> this.canDrinkMilk && this.level().isBrightOutside() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API ++ wanderingTrader -> level().purpurConfig.milkClearsBeneficialEffects && this.canDrinkMilk && this.level().isBrightOutside() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API // // Purpur - Milk Keeps Beneficial Effects + ) + ); + this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); +@@ -124,8 +_,10 @@ + return InteractionResult.CONSUME; + } + ++ if (this.level().purpurConfig.wanderingTraderAllowTrading) { // Purpur - Add config for villager trading + this.setTradingPlayer(player); + this.openTradingScreen(player, this.getDisplayName(), 1); ++ } // Purpur - Add config for villager trading + } + + return InteractionResult.SUCCESS; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch new file mode 100644 index 000000000..505d76d91 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java ++++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java +@@ -134,7 +_,17 @@ + int i1 = pos.getX() + this.random.nextInt(maxDistance * 2) - maxDistance; + int i2 = pos.getZ() + this.random.nextInt(maxDistance * 2) - maxDistance; + int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i1, i2); +- BlockPos blockPos1 = new BlockPos(i1, height, i2); ++ // Purpur start - Allow toggling special MobSpawners per world - allow traders to spawn below nether roof ++ BlockPos.MutableBlockPos blockPos1 = new BlockPos.MutableBlockPos(i1, height, i2); ++ if (level.dimensionType().hasCeiling()) { ++ do { ++ blockPos1.relative(net.minecraft.core.Direction.DOWN); ++ } while (!level.getBlockState(blockPos1).isAir()); ++ do { ++ blockPos1.relative(net.minecraft.core.Direction.DOWN); ++ } while (level.getBlockState(blockPos1).isAir() && blockPos1.getY() > 0); ++ } ++ // Purpur end - Allow toggling special MobSpawners per world + if (placementType.isSpawnPositionOk(level, blockPos1, EntityType.WANDERING_TRADER)) { + blockPos = blockPos1; + break; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch new file mode 100644 index 000000000..655474e8b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -0,0 +1,111 @@ +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -179,11 +_,20 @@ + private int currentImpulseContextResetGraceTime = 0; + public boolean affectsSpawning = true; // Paper - Affects Spawning API + public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage ++ public int burpDelay = 0; // Purpur - Burp delay ++ public boolean canPortalInstant = false; // Purpur - Add portal permission bypass + + // CraftBukkit start + public boolean fauxSleeping; + public int oldLevel = -1; + ++ // Purpur start - AFK API ++ public abstract void setAfk(boolean afk); ++ ++ public boolean isAfk() { ++ return false; ++ } ++ // Purpur end - AFK API + @Override + public org.bukkit.craftbukkit.entity.CraftHumanEntity getBukkitEntity() { + return (org.bukkit.craftbukkit.entity.CraftHumanEntity) super.getBukkitEntity(); +@@ -245,6 +_,12 @@ + + @Override + public void tick() { ++ // Purpur start - Burp delay ++ if (this.burpDelay > 0 && --this.burpDelay == 0) { ++ this.level().playSound(null, getX(), getY(), getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 1.0F, this.level().random.nextFloat() * 0.1F + 0.9F); ++ } ++ // Purpur end - Burp delay ++ + this.noPhysics = this.isSpectator(); + if (this.isSpectator() || this.isPassenger()) { + this.setOnGround(false); +@@ -302,6 +_,17 @@ + this.turtleHelmetTick(); + } + ++ // Purpur start - Full netherite armor grants fire resistance ++ if (this.level().purpurConfig.playerNetheriteFireResistanceDuration > 0 && this.level().getGameTime() % 20 == 0) { ++ if (this.getItemBySlot(EquipmentSlot.HEAD).is(Items.NETHERITE_HELMET) ++ && this.getItemBySlot(EquipmentSlot.CHEST).is(Items.NETHERITE_CHESTPLATE) ++ && this.getItemBySlot(EquipmentSlot.LEGS).is(Items.NETHERITE_LEGGINGS) ++ && this.getItemBySlot(EquipmentSlot.FEET).is(Items.NETHERITE_BOOTS)) { ++ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, this.level().purpurConfig.playerNetheriteFireResistanceDuration, this.level().purpurConfig.playerNetheriteFireResistanceAmplifier, this.level().purpurConfig.playerNetheriteFireResistanceAmbient, this.level().purpurConfig.playerNetheriteFireResistanceShowParticles, this.level().purpurConfig.playerNetheriteFireResistanceShowIcon), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NETHERITE_ARMOR); ++ } ++ } ++ // Purpur end - Full netherite armor grants fire resistance ++ + this.cooldowns.tick(); + this.updatePlayerPose(); + if (this.currentImpulseContextResetGraceTime > 0) { +@@ -510,7 +_,7 @@ + List list = Lists.newArrayList(); + + for (Entity entity : entities) { +- if (entity.getType() == EntityType.EXPERIENCE_ORB) { ++ if (entity.getType() == EntityType.EXPERIENCE_ORB && entity.level().purpurConfig.playerExpPickupDelay >= 0) { // Purpur - Configurable player pickup exp delay + list.add(entity); + } else if (!entity.isRemoved()) { + this.touch(entity); +@@ -1052,7 +_,7 @@ + flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits + if (flag2) { + damageSource = damageSource.critical(); // Paper - critical damage API +- f *= 1.5F; ++ f *= (float) this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur - Add config change multiplier critical damage value + } + + float f2 = f + f1; +@@ -1764,7 +_,23 @@ + + @Override + protected int getBaseExperienceReward(ServerLevel level) { +- return !level.getGameRules().get(GameRules.KEEP_INVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0; ++ // Purpur start - Add player death exp control options ++ if (!level.getGameRules().get(GameRules.KEEP_INVENTORY) && !this.isSpectator()) { ++ int toDrop; ++ try { ++ toDrop = Math.round(((Number) scriptEngine.eval("let expLevel = " + experienceLevel + "; " + ++ "let expTotal = " + totalExperience + "; " + ++ "let exp = " + experienceProgress + "; " + ++ level().purpurConfig.playerDeathExpDropEquation)).floatValue()); ++ } catch (javax.script.ScriptException e) { ++ e.printStackTrace(); ++ toDrop = experienceLevel * 7; ++ } ++ return Math.min(toDrop, level().purpurConfig.playerDeathExpDropMax); ++ } else { ++ return 0; ++ } ++ // Purpur end - Add player death exp control options + } + + @Override +@@ -1807,6 +_,13 @@ + public boolean addItem(ItemStack stack) { + return this.inventory.add(stack); + } ++ ++ // Purpur start - Player ridable in water option ++ @Override ++ public boolean dismountsUnderwater() { ++ return !level().purpurConfig.playerRidableInWater; ++ } ++ // Purpur end - Player ridable in water option + + public abstract @Nullable GameType gameMode(); + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch new file mode 100644 index 000000000..165e503d9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java ++++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java +@@ -78,6 +_,7 @@ + private @Nullable List piercedAndKilledEntities; + public ItemStack pickupItemStack = this.getDefaultPickupItem(); + public @Nullable ItemStack firedFromWeapon = null; ++ public net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments = net.minecraft.world.item.enchantment.ItemEnchantments.EMPTY; // Purpur - Add an option to fix MC-3304 projectile looting + + protected AbstractArrow(EntityType type, Level level) { + super(type, level); +@@ -359,7 +_,7 @@ + this.setInGround(false); + Vec3 deltaMovement = this.getDeltaMovement(); + this.setDeltaMovement(deltaMovement.multiply(this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F)); +- this.life = 0; ++ if (this.level().purpurConfig.arrowMovementResetsDespawnCounter) this.life = 0; // Purpur - Arrows should not reset despawn counter + } + + public boolean isInGround() { +@@ -582,6 +_,12 @@ + public @Nullable ItemStack getWeaponItem() { + return this.firedFromWeapon; + } ++ ++ // Purpur start - Add an option to fix MC-3304 projectile looting ++ public void setActualEnchantments(net.minecraft.world.item.enchantment.ItemEnchantments actualEnchantments) { ++ this.actualEnchantments = actualEnchantments; ++ } ++ // Purpur end - Add an option to fix MC-3304 projectile looting + + protected SoundEvent getDefaultHitGroundSoundEvent() { + return SoundEvents.ARROW_HIT; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch new file mode 100644 index 000000000..1eef8d9ac --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java ++++ b/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java +@@ -72,7 +_,7 @@ + + Entity owner = this.getOwner(); + int i = this.entityData.get(ID_LOYALTY); +- if (i > 0 && (this.dealtDamage || this.isNoPhysics()) && owner != null) { ++ if (i > 0 && (this.dealtDamage || this.isNoPhysics() || (level().purpurConfig.tridentLoyaltyVoidReturnHeight < 0.0D && getY() < level().purpurConfig.tridentLoyaltyVoidReturnHeight)) && owner != null) { // Purpur - Add option to allow loyalty on tridents to work in the void + if (!this.isAcceptibleReturnOwner()) { + if (this.level() instanceof ServerLevel serverLevel && this.pickup == AbstractArrow.Pickup.ALLOWED) { + this.spawnAtLocation(serverLevel, this.getPickupItem(), 0.1F); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch new file mode 100644 index 000000000..a1ad3be09 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java ++++ b/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java +@@ -94,7 +_,7 @@ + super.onHit(result); + if (!this.level().isClientSide()) { + // CraftBukkit start +- org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); ++ org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent(this.getBukkitEntity(), this.level().purpurConfig.witherExplosionRadius, false); // Purpur - Config for wither explosion radius + if (event.callEvent()) { + this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); + } +@@ -102,6 +_,13 @@ + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause + } + } ++ ++ // Purpur start - Add canSaveToDisk to Entity ++ @Override ++ public boolean canSaveToDisk() { ++ return false; ++ } ++ // Purpur end - Add canSaveToDisk to Entity + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch new file mode 100644 index 000000000..10badc693 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch @@ -0,0 +1,43 @@ +--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java ++++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java +@@ -53,9 +_,39 @@ + protected void onHitEntity(EntityHitResult result) { + super.onHitEntity(result); + Entity entity = result.getEntity(); +- int i = entity instanceof Blaze ? 3 : 0; ++ int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur - Add configurable snowball damage + entity.hurt(this.damageSources().thrown(this, this.getOwner()), i); + } ++ ++ // Purpur start - options to extinguish fire blocks with snowballs - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire ++ @Override ++ protected void onHitBlock(net.minecraft.world.phys.BlockHitResult blockHitResult) { ++ super.onHitBlock(blockHitResult); ++ ++ if (!this.level().isClientSide()) { ++ net.minecraft.core.BlockPos pos = blockHitResult.getBlockPos(); ++ net.minecraft.core.BlockPos relativePos = pos.relative(blockHitResult.getDirection()); ++ ++ net.minecraft.world.level.block.state.BlockState blockState = this.level().getBlockState(pos); ++ ++ if (this.level().purpurConfig.snowballExtinguishesFire && this.level().getBlockState(relativePos).is(net.minecraft.world.level.block.Blocks.FIRE)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, relativePos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) { ++ this.level().removeBlock(relativePos, false); ++ } ++ } else if (this.level().purpurConfig.snowballExtinguishesCandles && net.minecraft.world.level.block.AbstractCandleBlock.isLit(blockState)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, blockState.setValue(net.minecraft.world.level.block.AbstractCandleBlock.LIT, false))) { ++ net.minecraft.world.level.block.AbstractCandleBlock.extinguish(null, blockState, this.level(), pos); ++ } ++ } else if (this.level().purpurConfig.snowballExtinguishesCampfires && net.minecraft.world.level.block.CampfireBlock.isLitCampfire(blockState)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, blockState.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false))) { ++ this.level().levelEvent(null, 1009, pos, 0); ++ net.minecraft.world.level.block.CampfireBlock.dowse(this.getOwner(), this.level(), pos, blockState); ++ this.level().setBlockAndUpdate(pos, blockState.setValue(net.minecraft.world.level.block.CampfireBlock.LIT, false)); ++ } ++ } ++ } ++ } ++ // Purpur end - options to extinguish fire blocks with snowballs + + @Override + protected void onHit(HitResult result) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch new file mode 100644 index 000000000..da76c1d01 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java ++++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java +@@ -112,9 +_,10 @@ + return; + } + // CraftBukkit end +- if (this.random.nextFloat() < 0.05F && serverLevel.isSpawningMonsters()) { ++ if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.isSpawningMonsters()) { // Purpur - Configurable Ender Pearl RNG + Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); + if (endermite != null) { ++ endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API + endermite.snapTo(preTeleportX, preTeleportY, preTeleportZ, preTeleportYRot, preTeleportXRot); // Paper - spawn endermite at pre teleport position as teleport has been moved up + serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason + } +@@ -134,7 +_,7 @@ + if (serverPlayer1 != null) { + serverPlayer1.resetFallDistance(); + serverPlayer1.resetCurrentImpulseContext(); +- serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API ++ serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage + } + + this.playSound(serverLevel, vec3); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raids.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raids.java.patch new file mode 100644 index 000000000..1bf111404 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raids.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/entity/raid/Raids.java ++++ b/net/minecraft/world/entity/raid/Raids.java +@@ -31,6 +_,7 @@ + + public class Raids extends SavedData { + private static final String RAID_FILE_ID = "raids"; ++ public final java.util.Map playerCooldowns = com.google.common.collect.Maps.newHashMap(); // Purpur - Raid cooldown setting + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + Raids.RaidWithId.CODEC +@@ -82,6 +_,17 @@ + + public void tick(ServerLevel level) { + this.tick++; ++ // Purpur start - Raid cooldown setting ++ if (level.purpurConfig.raidCooldownSeconds != 0 && this.tick % 20 == 0) { ++ com.google.common.collect.ImmutableMap.copyOf(playerCooldowns).forEach((uuid, i) -> { ++ if (i < 1) { ++ playerCooldowns.remove(uuid); ++ } else { ++ playerCooldowns.put(uuid, i - 1); ++ } ++ }); ++ } ++ // Purpur end - Raid cooldown setting + Iterator iterator = this.raidMap.values().iterator(); + + while (iterator.hasNext()) { +@@ -144,11 +_,11 @@ + // } + + if (!raid.isStarted() || (raid.isInProgress() && raid.getRaidOmenLevel() < raid.getMaxRaidOmenLevel())) { // CraftBukkit - fixed a bug with raid: players could add up Bad Omen level even when the raid had finished +- // CraftBukkit start ++ if (serverLevel.purpurConfig.raidCooldownSeconds != 0 && playerCooldowns.containsKey(player.getUUID())) return null; // Purpur - Raid cooldown setting// CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(serverLevel, raid, player)) { + player.removeEffect(net.minecraft.world.effect.MobEffects.RAID_OMEN); + return null; +- } ++ }if (serverLevel.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), serverLevel.purpurConfig.raidCooldownSeconds); // Purpur - Raid cooldown setting + + if (!raid.isStarted() && !this.raidMap.containsValue(raid)) { + this.raidMap.put(this.getUniqueId(), raid); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch new file mode 100644 index 000000000..b14b6c1de --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java ++++ b/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java +@@ -431,6 +_,7 @@ + float groundFriction = this.getGroundFriction(); + if (groundFriction > 0.0F) { + this.landFriction = groundFriction; ++ if (level().purpurConfig.boatEjectPlayersOnLand) ejectPassengers(); // Purpur - Add option for boats to eject players on land + return AbstractBoat.Status.ON_LAND; + } else { + return AbstractBoat.Status.IN_AIR; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch new file mode 100644 index 000000000..a77663c60 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java ++++ b/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java +@@ -391,7 +_,7 @@ + private Vec3 calculateBoostTrackSpeed(Vec3 speed, BlockPos pos, BlockState state) { + if (state.is(Blocks.POWERED_RAIL) && state.getValue(PoweredRailBlock.POWERED)) { + if (speed.length() > 0.01) { +- return speed.normalize().scale(speed.length() + 0.06); ++ return speed.normalize().scale(speed.length() + this.level().purpurConfig.poweredRailBoostModifier); // Purpur - Configurable powered rail boost modifier + } else { + Vec3 redstoneDirection = this.minecart.getRedstoneDirection(pos); + return redstoneDirection.lengthSqr() <= 0.0 ? speed : redstoneDirection.scale(speed.length() + 0.2); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch new file mode 100644 index 000000000..5562ff33c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java ++++ b/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java +@@ -243,8 +_,8 @@ + Vec3 deltaMovement1 = this.getDeltaMovement(); + double d13 = deltaMovement1.horizontalDistance(); + if (d13 > 0.01) { +- double d14 = 0.06; +- this.setDeltaMovement(deltaMovement1.add(deltaMovement1.x / d13 * 0.06, 0.0, deltaMovement1.z / d13 * 0.06)); ++ double d14 = level.purpurConfig.poweredRailBoostModifier; // Purpur - Configurable powered rail boost modifier ++ this.setDeltaMovement(deltaMovement1.add(deltaMovement1.x / d13 * level.purpurConfig.poweredRailBoostModifier, 0.0, deltaMovement1.z / d13 * level.purpurConfig.poweredRailBoostModifier)); // Purpur - Configurable powered rail boost modifier + } else { + Vec3 deltaMovement2 = this.getDeltaMovement(); + double d15 = deltaMovement2.x; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodData.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodData.java.patch new file mode 100644 index 000000000..a44cdd312 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodData.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/food/FoodData.java ++++ b/net/minecraft/world/food/FoodData.java +@@ -39,6 +_,7 @@ + int oldFoodLevel = this.foodLevel; + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(serverPlayer, foodProperties.nutrition() + oldFoodLevel, stack); + if (!event.isCancelled()) { ++ if (serverPlayer.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) serverPlayer.burpDelay = serverPlayer.level().purpurConfig.playerBurpDelay; // Purpur - Burp after eating food fills hunger bar completely + this.add(event.getFoodLevel() - oldFoodLevel, foodProperties.saturation()); + } + serverPlayer.getBukkitEntity().sendHealthUpdate(); +@@ -87,7 +_,7 @@ + this.tickTimer++; + if (this.tickTimer >= this.starvationRate) { // CraftBukkit - add regen rate manipulation + if (player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL) { +- player.hurtServer(serverLevel, player.damageSources().starve(), 1.0F); ++ player.hurtServer(serverLevel, player.damageSources().starve(), player.level().purpurConfig.hungerStarvationDamage); // Purpur - Configurable hunger starvation damage + } + + this.tickTimer = 0; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodProperties.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodProperties.java.patch new file mode 100644 index 000000000..87515c815 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/food/FoodProperties.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/food/FoodProperties.java ++++ b/net/minecraft/world/food/FoodProperties.java +@@ -42,9 +_,11 @@ + level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, random.triangle(1.0F, 0.4F)); + if (entity instanceof Player player) { + player.getFoodData().eat(this, stack, (net.minecraft.server.level.ServerPlayer) player); // CraftBukkit +- level.playSound( +- null, player.getX(), player.getY(), player.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(random, 0.9F, 1.0F) +- ); ++ // Purpur start - Burp delay - moved to Player#tick() ++ //level.playSound( ++ // null, player.getX(), player.getY(), player.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(random, 0.9F, 1.0F) ++ //); ++ // Purpur end - Burp delay - moved to Player#tick() + } + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch new file mode 100644 index 000000000..ef13da00d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/inventory/AbstractContainerMenu.java ++++ b/net/minecraft/world/inventory/AbstractContainerMenu.java +@@ -63,6 +_,7 @@ + private final List containerListeners = Lists.newArrayList(); + private @Nullable ContainerSynchronizer synchronizer; + private boolean suppressRemoteUpdates; ++ @Nullable protected ItemStack activeQuickItem = null; // Purpur - Anvil API + // CraftBukkit start + public boolean checkReachable = true; + public abstract org.bukkit.inventory.InventoryView getBukkitView(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch new file mode 100644 index 000000000..41fe20831 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/inventory/AbstractFurnaceMenu.java ++++ b/net/minecraft/world/inventory/AbstractFurnaceMenu.java +@@ -121,7 +_,13 @@ + } else if (slotIndex != 1 && slotIndex != 0) { + if (this.canSmelt(item)) { + if (!this.moveItemStackTo(item, 0, 1, false)) { +- return ItemStack.EMPTY; ++ // Purpur start - Added the ability to add combustible items ++ if (this.isFuel(item)) { ++ if (!this.moveItemStackTo(item, 1, 2, false)) { ++ return ItemStack.EMPTY; ++ } ++ } ++ // Purpur end - Added the ability to add combustible items + } + } else if (this.isFuel(item)) { + if (!this.moveItemStackTo(item, 1, 2, false)) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch new file mode 100644 index 000000000..34fcab570 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch @@ -0,0 +1,196 @@ +--- a/net/minecraft/world/inventory/AnvilMenu.java ++++ b/net/minecraft/world/inventory/AnvilMenu.java +@@ -23,6 +_,12 @@ + import org.jspecify.annotations.Nullable; + import org.slf4j.Logger; + ++// Purpur start - Anvil API ++import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; ++import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; ++import net.minecraft.server.level.ServerPlayer; ++// Purpur end - Anvil API ++ + public class AnvilMenu extends ItemCombinerMenu { + public static final int INPUT_SLOT = 0; + public static final int ADDITIONAL_SLOT = 1; +@@ -51,6 +_,10 @@ + private org.bukkit.craftbukkit.inventory.view.CraftAnvilView bukkitEntity; + // CraftBukkit end + public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions ++ // Purpur start - Anvil API ++ public boolean bypassCost = false; ++ public boolean canDoUnsafeEnchants = false; ++ // Purpur end - Anvil API + + public AnvilMenu(int containerId, Inventory playerInventory) { + this(containerId, playerInventory, ContainerLevelAccess.NULL); +@@ -76,12 +_,17 @@ + + @Override + protected boolean mayPickup(Player player, boolean hasStack) { +- return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && hasStack; // CraftBukkit - allow cost 0 like a free item ++ return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && (this.bypassCost || this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST) && hasStack; // CraftBukkit - allow cost 0 like a free item // Purpur - Anvil API + } + + @Override + protected void onTake(Player player, ItemStack stack) { ++ // Purpur start - Anvil API ++ ItemStack itemstack = this.activeQuickItem != null ? this.activeQuickItem : stack; ++ if (org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)).callEvent(); ++ // Purpur end - Anvil API + if (!player.hasInfiniteMaterials()) { ++ if (this.bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur - Anvil API + player.giveExperienceLevels(-this.cost.get()); + } + +@@ -134,13 +_,19 @@ + + @Override + public void createResult() { ++ // Purpur start - Anvil API ++ this.bypassCost = false; ++ this.canDoUnsafeEnchants = false; ++ if (org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent.getHandlerList().getRegisteredListeners().length > 0) new org.purpurmc.purpur.event.inventory.AnvilUpdateResultEvent(getBukkitView()).callEvent(); ++ // Purpur end - Anvil API ++ + ItemStack item = this.inputSlots.getItem(0); + this.onlyRenaming = false; + this.cost.set(1); + int i = 0; + long l = 0L; + int i1 = 0; +- if (!item.isEmpty() && EnchantmentHelper.canStoreEnchantments(item)) { ++ if (!item.isEmpty() && this.canDoUnsafeEnchants || EnchantmentHelper.canStoreEnchantments(item)) { // Purpur - Anvil API + ItemStack itemStack = item.copy(); + ItemStack item1 = this.inputSlots.getItem(1); + ItemEnchantments.Mutable mutable = new ItemEnchantments.Mutable(EnchantmentHelper.getEnchantmentsForCrafting(itemStack)); +@@ -198,23 +_,34 @@ + int intValue = entry.getIntValue(); + intValue = level == intValue ? intValue + 1 : Math.max(intValue, level); + Enchantment enchantment = holder.value(); +- boolean canEnchant = enchantment.canEnchant(item); ++ // Purpur start - Config to allow unsafe enchants ++ boolean canEnchant = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || enchantment.canEnchant(item); // whether the enchantment can be applied on specific item type ++ boolean canEnchant1 = true; // whether two incompatible enchantments can be applied on a single item ++ // Purpur end - Config to allow unsafe enchants + if (this.player.hasInfiniteMaterials() || item.is(Items.ENCHANTED_BOOK)) { + canEnchant = true; + } + ++ java.util.Set> removedEnchantments = new java.util.HashSet<>(); // Purpur - Config to allow unsafe enchants + for (Holder holder1 : mutable.keySet()) { + if (!holder1.equals(holder) && !Enchantment.areCompatible(holder, holder1)) { +- canEnchant = false; ++ canEnchant1 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants; // Purpur - Anvil API // Purpur - canEnchant -> canEnchant1 - Config to allow unsafe enchants ++ // Purpur start - Config to allow unsafe enchants ++ if (!canEnchant1 && org.purpurmc.purpur.PurpurConfig.replaceIncompatibleEnchants) { ++ removedEnchantments.add(holder1); ++ canEnchant1 = true; ++ } ++ // Purpur end - Config to allow unsafe enchants + i++; + } + } ++ mutable.removeIf(removedEnchantments::contains); // Purpur - Config to allow unsafe enchants + +- if (!canEnchant) { ++ if (!canEnchant || !canEnchant1) { // Purpur - Config to allow unsafe enchants + flag1 = true; + } else { + flag = true; +- if (intValue > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions ++ if (!org.purpurmc.purpur.PurpurConfig.allowHigherEnchantsLevels && intValue > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions // Purpur - Config to allow unsafe enchants + intValue = enchantment.getMaxLevel(); + } + +@@ -243,6 +_,54 @@ + if (!this.itemName.equals(item.getHoverName().getString())) { + i1 = 1; + i += i1; ++ // Purpur start - Allow anvil colors ++ if (this.player != null) { ++ org.bukkit.craftbukkit.entity.CraftHumanEntity player = this.player.getBukkitEntity(); ++ String name = this.itemName; ++ boolean removeItalics = false; ++ if (player.hasPermission("purpur.anvil.remove_italics")) { ++ if (name.startsWith("&r")) { ++ name = name.substring(2); ++ removeItalics = true; ++ } else if (name.startsWith("")) { ++ name = name.substring(3); ++ removeItalics = true; ++ } else if (name.startsWith("")) { ++ name = name.substring(7); ++ removeItalics = true; ++ } ++ } ++ if (this.player.level().purpurConfig.anvilAllowColors) { ++ if (player.hasPermission("purpur.anvil.color")) { ++ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([0-9a-fr])").matcher(name); ++ while (matcher.find()) { ++ String match = matcher.group(1); ++ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT)); ++ } ++ //name = name.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1"); ++ } ++ if (player.hasPermission("purpur.anvil.format")) { ++ java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("(?i)&([k-or])").matcher(name); ++ while (matcher.find()) { ++ String match = matcher.group(1); ++ name = name.replace("&" + match, "\u00a7" + match.toLowerCase(java.util.Locale.ROOT)); ++ } ++ //name = name.replaceAll("(?i)&([l-or])", "\u00a7$1"); ++ } ++ } ++ net.kyori.adventure.text.Component component; ++ if (this.player.level().purpurConfig.anvilColorsUseMiniMessage && player.hasPermission("purpur.anvil.minimessage")) { ++ component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.bukkit.ChatColor.stripColor(name)); ++ } else { ++ component = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(name); ++ } ++ if (removeItalics) { ++ component = component.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); ++ } ++ itemStack.set(DataComponents.CUSTOM_NAME, io.papermc.paper.adventure.PaperAdventure.asVanilla(component)); ++ } ++ else ++ // Purpur end - Allow anvil colors + itemStack.set(DataComponents.CUSTOM_NAME, Component.literal(this.itemName)); + } + } else if (item.has(DataComponents.CUSTOM_NAME)) { +@@ -267,6 +_,12 @@ + this.onlyRenaming = true; + } + ++ // Purpur start - Anvil API ++ if (this.bypassCost && this.cost.get() >= this.maximumRepairCost) { ++ this.cost.set(this.maximumRepairCost - 1); ++ } ++ // Purpur end - Anvil API ++ + if (this.cost.get() >= this.maximumRepairCost && !this.player.hasInfiniteMaterials()) { // CraftBukkit + itemStack = ItemStack.EMPTY; + } +@@ -287,6 +_,13 @@ + + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemStack); // CraftBukkit + this.broadcastChanges(); ++ ++ // Purpur start - Anvil API ++ if ((this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants) && itemStack != ItemStack.EMPTY) { // Purpur - Config to allow unsafe enchants ++ ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), 2, itemStack)); ++ ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetDataPacket(this.containerId, 0, this.cost.get())); ++ } ++ // Purpur end - Anvil API + } else { + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), ItemStack.EMPTY); // CraftBukkit + this.cost.set(AnvilMenu.DEFAULT_DENIED_COST); // CraftBukkit - use a variable for set a cost for denied item +@@ -295,7 +_,7 @@ + } + + public static int calculateIncreasedRepairCost(int oldRepairCost) { +- return (int)Math.min(oldRepairCost * 2L + 1L, 2147483647L); ++ return org.purpurmc.purpur.PurpurConfig.anvilCumulativeCost ? (int)Math.min(oldRepairCost * 2L + 1L, 2147483647L) : 0; // Purpur - Make anvil cumulative cost configurable + } + + public boolean setItemName(String itemName) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ArmorSlot.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ArmorSlot.java.patch new file mode 100644 index 000000000..d2b8cef83 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ArmorSlot.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/inventory/ArmorSlot.java ++++ b/net/minecraft/world/inventory/ArmorSlot.java +@@ -46,7 +_,7 @@ + @Override + public boolean mayPickup(Player player) { + ItemStack item = this.getItem(); +- return (item.isEmpty() || player.isCreative() || !EnchantmentHelper.has(item, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE)) ++ return (item.isEmpty() || player.isCreative() || (!EnchantmentHelper.has(item, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE) || player.level().purpurConfig.playerRemoveBindingWithWeakness && player.hasEffect(net.minecraft.world.effect.MobEffects.WEAKNESS))) // Purpur - Config to remove curse of binding with weakness + && super.mayPickup(player); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch new file mode 100644 index 000000000..4996e388d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch @@ -0,0 +1,51 @@ +--- a/net/minecraft/world/inventory/EnchantmentMenu.java ++++ b/net/minecraft/world/inventory/EnchantmentMenu.java +@@ -63,6 +_,22 @@ + return access.getLocation(); + } + // CraftBukkit end ++ ++ // Purpur start - Enchantment Table Persists Lapis ++ @Override ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ super.onClose(who); ++ ++ if (who.getHandle().level().purpurConfig.enchantmentTableLapisPersists) { ++ access.execute((level, pos) -> { ++ net.minecraft.world.level.block.entity.BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof net.minecraft.world.level.block.entity.EnchantingTableBlockEntity enchantmentTable) { ++ enchantmentTable.setLapis(this.getItem(1).getCount()); ++ } ++ }); ++ } ++ } ++ // Purpur end - Enchantment Table Persists Lapis + }; + // Paper end - Add missing InventoryHolders + this.access = access; +@@ -83,6 +_,16 @@ + return EnchantmentMenu.EMPTY_SLOT_LAPIS_LAZULI; + } + }); ++ // Purpur start - Enchantment Table Persists Lapis ++ access.execute((level, pos) -> { ++ if (level.purpurConfig.enchantmentTableLapisPersists) { ++ net.minecraft.world.level.block.entity.BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof net.minecraft.world.level.block.entity.EnchantingTableBlockEntity enchantmentTable) { ++ this.getSlot(1).set(new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis())); ++ } ++ } ++ }); ++ // Purpur end - Enchantment Table Persists Lapis + this.addStandardInventorySlots(playerInventory, 8, 84); + this.addDataSlot(DataSlot.shared(this.costs, 0)); + this.addDataSlot(DataSlot.shared(this.costs, 1)); +@@ -299,7 +_,7 @@ + @Override + public void removed(Player player) { + super.removed(player); +- this.access.execute((level, pos) -> this.clearContainer(player, this.enchantSlots)); ++ this.access.execute((level, pos) -> {if (level.purpurConfig.enchantmentTableLapisPersists) this.getSlot(1).set(ItemStack.EMPTY);this.clearContainer(player, this.enchantSlots);}); // Purpur - Enchantment Table Persists Lapis + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch new file mode 100644 index 000000000..f1ba2081a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch @@ -0,0 +1,138 @@ +--- a/net/minecraft/world/inventory/GrindstoneMenu.java ++++ b/net/minecraft/world/inventory/GrindstoneMenu.java +@@ -92,11 +_,13 @@ + @Override + public void onTake(Player player, ItemStack stack) { + access.execute((level, blockPos) -> { ++ ItemStack itemstack = activeQuickItem == null ? stack : activeQuickItem; // Purpur - Grindstone API + if (level instanceof ServerLevel) { + // Paper start - Fire BlockExpEvent on grindstone use + org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), this.getExperienceAmount(level)); + event.callEvent(); +- ExperienceOrb.awardWithDirection((ServerLevel) level, Vec3.atCenterOf(blockPos), Vec3.ZERO, event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null); ++ org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent grindstoneTakeResultEvent = new org.purpurmc.purpur.event.inventory.GrindstoneTakeResultEvent(player.getBukkitEntity(), getBukkitView(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), event.getExpToDrop()); grindstoneTakeResultEvent.callEvent(); // Purpur - Grindstone API ++ ExperienceOrb.awardWithDirection((ServerLevel) level, Vec3.atCenterOf(blockPos), Vec3.ZERO, grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null); // Purpur - Grindstone API + // Paper end - Fire BlockExpEvent on grindstone use + } + +@@ -125,7 +_,7 @@ + for (Entry> entry : enchantmentsForCrafting.entrySet()) { + Holder holder = entry.getKey(); + int intValue = entry.getIntValue(); +- if (!holder.is(EnchantmentTags.CURSE)) { ++ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value())) { // Purpur - Config for grindstones + i += holder.value().getMinCost(intValue); + } + } +@@ -203,15 +_,75 @@ + + for (Entry> entry : enchantmentsForCrafting.entrySet()) { + Holder holder = entry.getKey(); +- if (!holder.is(EnchantmentTags.CURSE) || mutable.getLevel(holder) == 0) { ++ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()) || mutable.getLevel(holder) == 0) { // Purpur - Config for grindstones + mutable.upgrade(holder, entry.getIntValue()); + } + } + }); + } + ++ // Purpur start - Config for grindstones ++ private java.util.List> GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST = java.util.List.of( ++ // DataComponents.MAX_STACK_SIZE, ++ // DataComponents.DAMAGE, ++ // DataComponents.BLOCK_STATE, ++ DataComponents.CUSTOM_DATA, ++ // DataComponents.MAX_DAMAGE, ++ // DataComponents.UNBREAKABLE, ++ // DataComponents.CUSTOM_NAME, ++ // DataComponents.ITEM_NAME, ++ // DataComponents.LORE, ++ // DataComponents.RARITY, ++ // DataComponents.ENCHANTMENTS, ++ // DataComponents.CAN_PLACE_ON, ++ // DataComponents.CAN_BREAK, ++ DataComponents.ATTRIBUTE_MODIFIERS, ++ DataComponents.CUSTOM_MODEL_DATA, ++ // DataComponents.HIDE_ADDITIONAL_TOOLTIP, ++ // DataComponents.HIDE_TOOLTIP, ++ // DataComponents.REPAIR_COST, ++ // DataComponents.CREATIVE_SLOT_LOCK, ++ // DataComponents.ENCHANTMENT_GLINT_OVERRIDE, ++ // DataComponents.INTANGIBLE_PROJECTILE, ++ // DataComponents.FOOD, ++ // DataComponents.FIRE_RESISTANT, ++ // DataComponents.TOOL, ++ // DataComponents.STORED_ENCHANTMENTS, ++ DataComponents.DYED_COLOR, ++ // DataComponents.MAP_COLOR, ++ // DataComponents.MAP_ID, ++ // DataComponents.MAP_DECORATIONS, ++ // DataComponents.MAP_POST_PROCESSING, ++ // DataComponents.CHARGED_PROJECTILES, ++ // DataComponents.BUNDLE_CONTENTS, ++ // DataComponents.POTION_CONTENTS, ++ DataComponents.SUSPICIOUS_STEW_EFFECTS ++ // DataComponents.WRITABLE_BOOK_CONTENT, ++ // DataComponents.WRITTEN_BOOK_CONTENT, ++ // DataComponents.TRIM, ++ // DataComponents.DEBUG_STICK_STATE, ++ // DataComponents.ENTITY_DATA, ++ // DataComponents.BUCKET_ENTITY_DATA, ++ // DataComponents.BLOCK_ENTITY_DATA, ++ // DataComponents.INSTRUMENT, ++ // DataComponents.OMINOUS_BOTTLE_AMPLIFIER, ++ // DataComponents.RECIPES, ++ // DataComponents.LODESTONE_TRACKER, ++ // DataComponents.FIREWORK_EXPLOSION, ++ // DataComponents.FIREWORKS, ++ // DataComponents.PROFILE, ++ // DataComponents.NOTE_BLOCK_SOUND, ++ // DataComponents.BANNER_PATTERNS, ++ // DataComponents.BASE_COLOR, ++ // DataComponents.POT_DECORATIONS, ++ // DataComponents.CONTAINER, ++ // DataComponents.BEES, ++ // DataComponents.LOCK, ++ // DataComponents.CONTAINER_LOOT, ++ ); ++ // Purpur end - Config for grindstones + private ItemStack removeNonCursesFrom(ItemStack item) { +- ItemEnchantments itemEnchantments = EnchantmentHelper.updateEnchantments(item, mutable -> mutable.removeIf(holder -> !holder.is(EnchantmentTags.CURSE))); ++ ItemEnchantments itemEnchantments = EnchantmentHelper.updateEnchantments(item, mutable -> mutable.removeIf(holder -> !org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()))); // Purpur - Config for grindstones + if (item.is(Items.ENCHANTED_BOOK) && itemEnchantments.isEmpty()) { + item = item.transmuteCopy(Items.BOOK); + } +@@ -223,6 +_,23 @@ + } + + item.set(DataComponents.REPAIR_COST, i); ++ ++ // Purpur start - Config for grindstones ++ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder(); ++ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveAttributes) { ++ item.getComponents().forEach(typedDataComponent -> { ++ if (GRINDSTONE_REMOVE_ATTRIBUTES_REMOVAL_LIST.contains(typedDataComponent.type())) { ++ builder.remove(typedDataComponent.type()); ++ } ++ }); ++ } ++ if (org.purpurmc.purpur.PurpurConfig.grindstoneRemoveDisplay) { ++ builder.remove(DataComponents.CUSTOM_NAME); ++ builder.remove(DataComponents.LORE); ++ } ++ item.applyComponents(builder.build()); ++ // Purpur end - Config for grindstones ++ + return item; + } + +@@ -279,7 +_,9 @@ + return ItemStack.EMPTY; + } + ++ this.activeQuickItem = itemStack; // Purpur - Grindstone API + slot.onTake(player, item); ++ this.activeQuickItem = null; // Purpur - Grindstone API + } + + return itemStack; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch new file mode 100644 index 000000000..9899e30a5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/inventory/ItemCombinerMenu.java ++++ b/net/minecraft/world/inventory/ItemCombinerMenu.java +@@ -156,7 +_,9 @@ + return ItemStack.EMPTY; + } + ++ this.activeQuickItem = itemStack; // Purpur - Anvil API + slot.onTake(player, item); ++ this.activeQuickItem = null; // Purpur - Anvil API + } + + return itemStack; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/AxeItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/AxeItem.java.patch new file mode 100644 index 000000000..91c2180ca --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/AxeItem.java.patch @@ -0,0 +1,70 @@ +--- a/net/minecraft/world/item/AxeItem.java ++++ b/net/minecraft/world/item/AxeItem.java +@@ -65,13 +_,15 @@ + if (playerHasBlockingItemUseIntent(context)) { + return InteractionResult.PASS; + } else { +- Optional optional = this.evaluateNewBlockState(level, clickedPos, player, level.getBlockState(clickedPos)); ++ Optional optional = this.evaluateActionable(level, clickedPos, player, level.getBlockState(clickedPos)); // Purpur - Tool actionable options + if (optional.isEmpty()) { + return InteractionResult.PASS; + } else { ++ org.purpurmc.purpur.tool.Actionable actionable = optional.get(); // Purpur - Tool actionable options ++ BlockState state = actionable.into().withPropertiesOf(level.getBlockState(clickedPos)); // Purpur - Tool actionable options + ItemStack itemInHand = context.getItemInHand(); + // Paper start - EntityChangeBlockEvent +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, optional.get())) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, state)) { // Purpur - Tool actionable options + return InteractionResult.PASS; + } + // Paper end +@@ -79,8 +_,15 @@ + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, clickedPos, itemInHand); + } + +- level.setBlock(clickedPos, optional.get(), Block.UPDATE_ALL_IMMEDIATE); +- level.gameEvent(GameEvent.BLOCK_CHANGE, clickedPos, GameEvent.Context.of(player, optional.get())); ++ // Purpur start - Tool actionable options ++ level.setBlock(clickedPos, state, Block.UPDATE_ALL_IMMEDIATE); ++ actionable.drops().forEach((drop, chance) -> { ++ if (level.random.nextDouble() < chance) { ++ Block.popResourceFromFace(level, clickedPos, context.getClickedFace(), new ItemStack(drop)); ++ } ++ }); ++ level.gameEvent(GameEvent.BLOCK_CHANGE, clickedPos, GameEvent.Context.of(player, state)); ++ // Purpur end - Tool actionable options + if (player != null) { + itemInHand.hurtAndBreak(1, player, context.getHand().asEquipmentSlot()); + } +@@ -97,21 +_,23 @@ + && !player.isSecondaryUseActive(); + } + +- private Optional evaluateNewBlockState(Level level, BlockPos pos, @Nullable Player player, BlockState state) { +- Optional stripped = this.getStripped(state); ++ private Optional evaluateActionable(Level level, BlockPos pos, @Nullable Player player, BlockState state) { // Purpur - Tool actionable options ++ Optional stripped = Optional.ofNullable(level.purpurConfig.axeStrippables.get(state.getBlock())); // Purpur - Tool actionable options + if (stripped.isPresent()) { +- level.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); ++ level.playSound(STRIPPABLES.containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound + return stripped; + } else { +- Optional previous = WeatheringCopper.getPrevious(state); ++ Optional previous = Optional.ofNullable(level.purpurConfig.axeWeatherables.get(state.getBlock())); // Purpur - Tool actionable options + if (previous.isPresent()) { +- spawnSoundAndParticle(level, pos, player, state, SoundEvents.AXE_SCRAPE, LevelEvent.PARTICLES_SCRAPE); ++ spawnSoundAndParticle(level, pos, WeatheringCopper.getPrevious(state).isPresent() ? player : null, state, SoundEvents.AXE_SCRAPE, LevelEvent.PARTICLES_SCRAPE); // Purpur - Tool actionable options - force sound + return previous; + } else { +- Optional optional = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(state.getBlock())) +- .map(block -> block.withPropertiesOf(state)); ++ // Purpur start - Tool actionable options ++ Optional optional = Optional.ofNullable(level.purpurConfig.axeWaxables.get(state.getBlock())); ++ // .map(block -> block.withPropertiesOf(state)); ++ // Purpur end - Tool actionable options + if (optional.isPresent()) { +- spawnSoundAndParticle(level, pos, player, state, SoundEvents.AXE_WAX_OFF, LevelEvent.PARTICLES_WAX_OFF); ++ spawnSoundAndParticle(level, pos, HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(state.getBlock()) ? player : null, state, SoundEvents.AXE_WAX_OFF, LevelEvent.PARTICLES_WAX_OFF); // Purpur - Tool actionable options - force sound + return optional; + } else { + return Optional.empty(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BlockItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BlockItem.java.patch new file mode 100644 index 000000000..d8bd11e1f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BlockItem.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/item/BlockItem.java ++++ b/net/minecraft/world/item/BlockItem.java +@@ -143,7 +_,16 @@ + } + + protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, @Nullable Player player, ItemStack stack, BlockState state) { +- return updateCustomBlockEntityTag(level, player, pos, stack); ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ boolean handled = updateCustomBlockEntityTag(level, player, pos, stack); ++ if (level.purpurConfig.persistentTileEntityLore) { ++ BlockEntity blockEntity1 = level.getBlockEntity(pos); ++ if (blockEntity1 != null) { ++ blockEntity1.setPersistentLore(stack.getOrDefault(DataComponents.LORE, net.minecraft.world.item.component.ItemLore.EMPTY)); ++ } ++ } ++ return handled; ++ // Purpur end - Persistent BlockEntity Lore and DisplayName + } + + protected @Nullable BlockState getPlacementState(BlockPlaceContext context) { +@@ -204,6 +_,7 @@ + } + + if (!type.onlyOpCanSetNbt() || player != null && (player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place")))) { // Spigot - add permission ++ if (!(level.purpurConfig.silkTouchEnabled && blockEntity instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity && player.getBukkitEntity().hasPermission("purpur.drop.spawners"))) // Purpur - Silk touch spawners + return typedEntityData.loadInto(blockEntity, level.registryAccess()); + } + +@@ -244,6 +_,7 @@ + public void onDestroyed(ItemEntity itemEntity) { + ItemContainerContents itemContainerContents = itemEntity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY); + if (itemContainerContents != null) { ++ if (itemEntity.level().purpurConfig.shulkerBoxItemDropContentsWhenDestroyed && this.getBlock() instanceof ShulkerBoxBlock) // Purpur - option to disable shulker box items from dropping contents when destroyed + ItemUtils.onContainerDestroyed(itemEntity, itemContainerContents.nonEmptyItemsCopy()); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BowItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BowItem.java.patch new file mode 100644 index 000000000..bb43e3ee4 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BowItem.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/item/BowItem.java ++++ b/net/minecraft/world/item/BowItem.java +@@ -28,6 +_,11 @@ + return false; + } else { + ItemStack projectile = player.getProjectile(stack); ++ // Purpur start - Infinity bow settings ++ if (level.purpurConfig.infinityWorksWithoutArrows && projectile.isEmpty() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, stack) > 0) { ++ projectile = new ItemStack(Items.ARROW); ++ } ++ // Purpur end - Infinity bow settings + if (projectile.isEmpty()) { + return false; + } else { +@@ -38,7 +_,7 @@ + } else { + List list = draw(stack, projectile, player); + if (level instanceof ServerLevel serverLevel && !list.isEmpty()) { +- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null, powerForTime); // Paper - Pass draw strength ++ this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, (float) serverLevel.purpurConfig.bowProjectileOffset, powerForTime == 1.0F, null, powerForTime); // Paper - Pass draw strength // Purpur - Projectile offset config + } + + level.playSound( +@@ -89,7 +_,7 @@ + public InteractionResult use(Level level, Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + boolean flag = !player.getProjectile(itemInHand).isEmpty(); +- if (!player.hasInfiniteMaterials() && !flag) { ++ if (!player.hasInfiniteMaterials() && !flag && !(level.purpurConfig.infinityWorksWithoutArrows && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemInHand) > 0)) { // Purpur - Infinity bow settings + return InteractionResult.FAIL; + } else { + player.startUsingItem(hand); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BucketItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BucketItem.java.patch new file mode 100644 index 000000000..0bcda5c05 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BucketItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/BucketItem.java ++++ b/net/minecraft/world/item/BucketItem.java +@@ -148,7 +_,7 @@ + // CraftBukkit end + if (!flag2) { + return hitResult != null && this.emptyContents(entity, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null, direction, clicked, itemstack, hand); // CraftBukkit +- } else if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos) && this.content.is(FluidTags.WATER)) { ++ } else if ((level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos) || (level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur - Add allow water in end world option + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/CrossbowItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/CrossbowItem.java.patch new file mode 100644 index 000000000..c2e3f0e41 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/CrossbowItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/CrossbowItem.java ++++ b/net/minecraft/world/item/CrossbowItem.java +@@ -66,7 +_,7 @@ + ItemStack itemInHand = player.getItemInHand(hand); + ChargedProjectiles chargedProjectiles = itemInHand.get(DataComponents.CHARGED_PROJECTILES); + if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { +- this.performShooting(level, player, hand, itemInHand, getShootingPower(chargedProjectiles), 1.0F, null); ++ this.performShooting(level, player, hand, itemInHand, getShootingPower(chargedProjectiles), (float) level.purpurConfig.crossbowProjectileOffset, null); // Purpur - Projectile offset config + return InteractionResult.CONSUME; + } else if (!player.getProjectile(itemInHand).isEmpty()) { + this.startSoundPlayed = false; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/DyeColor.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/DyeColor.java.patch new file mode 100644 index 000000000..2bafea6dd --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/DyeColor.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/item/DyeColor.java ++++ b/net/minecraft/world/item/DyeColor.java +@@ -124,4 +_,10 @@ + private static CraftingInput makeCraftColorInput(DyeColor first, DyeColor second) { + return CraftingInput.of(2, 1, List.of(new ItemStack(DyeItem.byColor(first)), new ItemStack(DyeItem.byColor(second)))); + } ++ ++ // Purpur start - Shulker spawn from bullet options ++ public static DyeColor random(net.minecraft.util.RandomSource random) { ++ return values()[random.nextInt(values().length)]; ++ } ++ // Purpur end - Shulker spawn from bullet options + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EggItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EggItem.java.patch new file mode 100644 index 000000000..7bd1d11bf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EggItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/EggItem.java ++++ b/net/minecraft/world/item/EggItem.java +@@ -24,7 +_,7 @@ + public InteractionResult use(Level level, Player player, InteractionHand hand) { + ItemStack itemInHand = player.getItemInHand(hand); + // Paper start +- final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemInHand, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemInHand, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, (float) level.purpurConfig.eggProjectileOffset); // Purpur - Projectile offset config + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEgg.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch new file mode 100644 index 000000000..baee5e0ab --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/EndCrystalItem.java ++++ b/net/minecraft/world/item/EndCrystalItem.java +@@ -24,7 +_,7 @@ + Level level = context.getLevel(); + BlockPos clickedPos = context.getClickedPos(); + BlockState blockState = level.getBlockState(clickedPos); +- if (!blockState.is(Blocks.OBSIDIAN) && !blockState.is(Blocks.BEDROCK)) { ++ if (!level.purpurConfig.endCrystalPlaceAnywhere && !blockState.is(Blocks.OBSIDIAN) && !blockState.is(Blocks.BEDROCK)) { // Purpur - place end crystal on any block + return InteractionResult.FAIL; + } else { + BlockPos blockPos = clickedPos.above(); final BlockPos aboveBlockPos = blockPos; // Paper - OBFHELPER diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch new file mode 100644 index 000000000..de340dafe --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/item/EnderpearlItem.java ++++ b/net/minecraft/world/item/EnderpearlItem.java +@@ -24,7 +_,7 @@ + if (level instanceof ServerLevel serverLevel) { + // CraftBukkit start + // Paper start - PlayerLaunchProjectileEvent +- final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemInHand, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemInHand, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.enderPearlProjectileOffset); // Purpur - Projectile offset config + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEnderpearl.attemptSpawn()) { + if (event.shouldConsume()) { +@@ -44,6 +_,7 @@ + 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) + ); + player.awardStat(Stats.ITEM_USED.get(this)); ++ player.getCooldowns().addCooldown(itemInHand, player.getAbilities().instabuild ? level.purpurConfig.enderPearlCooldownCreative : level.purpurConfig.enderPearlCooldown); // Purpur - Configurable Ender Pearl cooldown + } else { + if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { + serverPlayer.deregisterEnderPearl(thrownEnderpearl.projectile()); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/HoeItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/HoeItem.java.patch new file mode 100644 index 000000000..cfe01d6ad --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/HoeItem.java.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/world/item/HoeItem.java ++++ b/net/minecraft/world/item/HoeItem.java +@@ -44,15 +_,25 @@ + public InteractionResult useOn(UseOnContext context) { + Level level = context.getLevel(); + BlockPos clickedPos = context.getClickedPos(); +- Pair, Consumer> pair = TILLABLES.get(level.getBlockState(clickedPos).getBlock()); +- if (pair == null) { ++ // Purpur start - Tool actionable options ++ Block clickedBlock = level.getBlockState(clickedPos).getBlock(); ++ org.purpurmc.purpur.tool.Tillable tillable = level.purpurConfig.hoeTillables.get(clickedBlock); ++ if (tillable == null) { + return InteractionResult.PASS; + } else { +- Predicate predicate = pair.getFirst(); +- Consumer consumer = pair.getSecond(); ++ Predicate predicate = tillable.condition().predicate(); ++ Consumer consumer = (ctx) -> { ++ level.setBlock(clickedPos, tillable.into().defaultBlockState(), 11); ++ tillable.drops().forEach((drop, chance) -> { ++ if (level.random.nextDouble() < chance) { ++ Block.popResourceFromFace(level, clickedPos, ctx.getClickedFace(), new ItemStack(drop)); ++ } ++ }); ++ }; ++ // Purpur end - Tool actionable options + if (predicate.test(context)) { + Player player = context.getPlayer(); +- level.playSound(player, clickedPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); ++ if (!TILLABLES.containsKey(clickedBlock)) level.playSound(null, clickedPos, SoundEvents.HOE_TILL, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - Tool actionable options - force sound + if (!level.isClientSide()) { + consumer.accept(context); + if (player != null) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ItemStack.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ItemStack.java.patch new file mode 100644 index 000000000..34741405d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ItemStack.java.patch @@ -0,0 +1,58 @@ +--- a/net/minecraft/world/item/ItemStack.java ++++ b/net/minecraft/world/item/ItemStack.java +@@ -456,6 +_,7 @@ + // revert back all captured blocks + for (org.bukkit.block.BlockState blockstate : blocks) { + ((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).revertPlace(); ++ ((org.bukkit.craftbukkit.block.CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed + } + + SignItem.openSign = null; // SPIGOT-6758 - Reset on early return +@@ -479,6 +_,7 @@ + if (!(block.getBlock() instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // Containers get placed automatically + block.onPlace(serverLevel, newPos, oldBlock, true, context); + } ++ block.getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed + + serverLevel.notifyAndUpdatePhysics(newPos, null, oldBlock, block, serverLevel.getBlockState(newPos), updateFlags, net.minecraft.world.level.block.Block.UPDATE_LIMIT); // send null chunk as chunk.k() returns false by this point + } +@@ -599,6 +_,26 @@ + return this.isDamageableItem() && this.getDamageValue() > 0; + } + ++ // Purpur start - Add option to mend the most damaged equipment first ++ public float getDamagePercent() { ++ if (this.has(DataComponents.UNBREAKABLE)) { ++ return 0.0F; ++ } ++ ++ final int maxDamage = this.getOrDefault(DataComponents.MAX_DAMAGE, 0); ++ if (maxDamage == 0) { ++ return 0.0F; ++ } ++ ++ final int damage = this.getOrDefault(DataComponents.DAMAGE, 0); ++ if (damage == 0) { ++ return 0.0F; ++ } ++ ++ return (float) damage / maxDamage; ++ } ++ // Purpur end - Add option to mend the most damaged equipment first ++ + public int getDamageValue() { + return Mth.clamp(this.getOrDefault(DataComponents.DAMAGE, 0), 0, this.getMaxDamage()); + } +@@ -1247,6 +_,12 @@ + public boolean isEnchanted() { + return !this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty(); + } ++ ++ // Purpur start - Config to allow unsafe enchants ++ public boolean hasEnchantment(Holder enchantment) { ++ return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).getLevel(enchantment) > 0; ++ } ++ // Purpur end - Config to allow unsafe enchants + + public ItemEnchantments getEnchantments() { + return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/Items.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/Items.java.patch new file mode 100644 index 000000000..bebe1da21 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/Items.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/item/Items.java ++++ b/net/minecraft/world/item/Items.java +@@ -420,7 +_,7 @@ + public static final Item PURPUR_BLOCK = registerBlock(Blocks.PURPUR_BLOCK); + public static final Item PURPUR_PILLAR = registerBlock(Blocks.PURPUR_PILLAR); + public static final Item PURPUR_STAIRS = registerBlock(Blocks.PURPUR_STAIRS); +- public static final Item SPAWNER = registerBlock(Blocks.SPAWNER); ++ public static final Item SPAWNER = registerBlock(Blocks.SPAWNER, org.purpurmc.purpur.item.SpawnerItem::new, new Item.Properties().rarity(Rarity.EPIC)); // Purpur - Silk touch spawners + public static final Item CREAKING_HEART = registerBlock(Blocks.CREAKING_HEART); + public static final Item CHEST = registerBlock(Blocks.CHEST, properties -> properties.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY)); + public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE); +@@ -2169,7 +_,7 @@ + "sweet_berries", createBlockItemWithCustomItemName(Blocks.SWEET_BERRY_BUSH), new Item.Properties().food(Foods.SWEET_BERRIES) + ); + public static final Item GLOW_BERRIES = registerItem( +- "glow_berries", createBlockItemWithCustomItemName(Blocks.CAVE_VINES), new Item.Properties().food(Foods.GLOW_BERRIES) ++ "glow_berries", settings -> new org.purpurmc.purpur.item.GlowBerryItem(Blocks.CAVE_VINES, settings.useItemDescriptionPrefix()), new Item.Properties().food(Foods.GLOW_BERRIES) // Purpur - Eating glow berries adds glow effect + ); + public static final Item CAMPFIRE = registerBlock( + Blocks.CAMPFIRE, properties -> properties.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY) diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/MapItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/MapItem.java.patch new file mode 100644 index 000000000..c609ab3c7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/MapItem.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/item/MapItem.java ++++ b/net/minecraft/world/item/MapItem.java +@@ -192,6 +_,7 @@ + public static void renderBiomePreviewMap(ServerLevel level, ItemStack stack) { + MapItemSavedData savedData = getSavedData(stack, level); + if (savedData != null) { ++ savedData.isExplorerMap = true; // Purpur - Explorer Map API + if (level.dimension() == savedData.dimension) { + int i = 1 << savedData.scale; + int i1 = savedData.centerX; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/NameTagItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/NameTagItem.java.patch new file mode 100644 index 000000000..2a8639361 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/NameTagItem.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/item/NameTagItem.java ++++ b/net/minecraft/world/item/NameTagItem.java +@@ -24,6 +_,7 @@ + + LivingEntity newEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle(); + newEntity.setCustomName(event.getName() != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(event.getName()) : null); ++ if (player.level().purpurConfig.armorstandFixNametags && target instanceof net.minecraft.world.entity.decoration.ArmorStand) target.setCustomNameVisible(true); // Purpur - Set name visible when using a Name Tag on an Armor Stand + if (event.isPersistent() && newEntity instanceof Mob mob) { + // Paper end - Add PlayerNameEntityEvent + mob.setPersistenceRequired(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch new file mode 100644 index 000000000..3fa0c2dfa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/ProjectileWeaponItem.java ++++ b/net/minecraft/world/item/ProjectileWeaponItem.java +@@ -106,6 +_,8 @@ + abstractArrow.setCritArrow(true); + } + ++ abstractArrow.setActualEnchantments(weapon.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting ++ + return abstractArrow; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ShovelItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ShovelItem.java.patch new file mode 100644 index 000000000..c583f3853 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ShovelItem.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/item/ShovelItem.java ++++ b/net/minecraft/world/item/ShovelItem.java +@@ -46,9 +_,12 @@ + BlockState blockState1 = FLATTENABLES.get(blockState.getBlock()); + BlockState blockState2 = null; + Runnable afterAction = null; // Paper ++ org.purpurmc.purpur.tool.Flattenable flattenable = level.purpurConfig.shovelFlattenables.get(blockState.getBlock()); // Purpur - Tool actionable options + if (blockState1 != null && level.getBlockState(clickedPos.above()).isAir()) { +- afterAction = () -> level.playSound(player, clickedPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper +- blockState2 = blockState1; ++ // Purpur start - Tool actionable options ++ afterAction = () -> {if (!FLATTENABLES.containsKey(blockState.getBlock())) level.playSound(player, clickedPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F);}; // Paper ++ blockState2 = flattenable.into().defaultBlockState(); ++ // Purpur end - Tool actionable options + } else if (blockState.getBlock() instanceof CampfireBlock && blockState.getValue(CampfireBlock.LIT)) { + afterAction = () -> { // Paper + if (!level.isClientSide()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SnowballItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SnowballItem.java.patch new file mode 100644 index 000000000..9c09ecb72 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SnowballItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/SnowballItem.java ++++ b/net/minecraft/world/item/SnowballItem.java +@@ -26,7 +_,7 @@ + // CraftBukkit start - moved down + if (level instanceof ServerLevel serverLevel) { + // Paper start - PlayerLaunchProjectileEvent +- final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemInHand, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemInHand, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.snowballProjectileOffset); // Purpur - Projectile offset config + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity()); + if (event.callEvent() && snowball.attemptSpawn()) { + player.awardStat(Stats.ITEM_USED.get(this)); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch new file mode 100644 index 000000000..453518159 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/item/SpawnEggItem.java ++++ b/net/minecraft/world/item/SpawnEggItem.java +@@ -70,6 +_,23 @@ + return InteractionResult.FAIL; + } else { + if (level.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation ++ // Purpur start - PlayerSetSpawnerTypeWithEggEvent ++ if (spawner instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity) { ++ org.bukkit.block.Block bukkitBlock = level.getWorld().getBlockAt(clickedPos.getX(), clickedPos.getY(), clickedPos.getZ()); ++ org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.CreatureSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(type.getName())); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ type = EntityType.getFromBukkitType(event.getEntityType()); ++ } else if (spawner instanceof net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity) { ++ org.bukkit.block.Block bukkitBlock = level.getWorld().getBlockAt(clickedPos.getX(), clickedPos.getY(), clickedPos.getZ()); ++ org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent event = new org.purpurmc.purpur.event.PlayerSetTrialSpawnerTypeWithEggEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), bukkitBlock, (org.bukkit.block.TrialSpawner) bukkitBlock.getState(), org.bukkit.entity.EntityType.fromName(type.getName())); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ type = EntityType.getFromBukkitType(event.getEntityType()); ++ } ++ // Purpur end - PlayerSetSpawnerTypeWithEggEvent + spawner.setEntityId(type, level.getRandom()); + level.sendBlockUpdated(clickedPos, blockState, blockState, Block.UPDATE_ALL); + level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, clickedPos); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch new file mode 100644 index 000000000..5cf5ead6e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/ThrowablePotionItem.java ++++ b/net/minecraft/world/item/ThrowablePotionItem.java +@@ -24,7 +_,7 @@ + ItemStack itemInHand = player.getItemInHand(hand); + if (level instanceof ServerLevel serverLevel) { + // Paper start - PlayerLaunchProjectileEvent +- final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemInHand, player, -20.0F, PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemInHand, player, -20.0F, PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.throwablePotionProjectileOffset); // Purpur - Projectile offset config + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity()); + if (event.callEvent() && thrownPotion.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/TridentItem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/TridentItem.java.patch new file mode 100644 index 000000000..16aa72e60 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/TridentItem.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/item/TridentItem.java ++++ b/net/minecraft/world/item/TridentItem.java +@@ -83,7 +_,7 @@ + if (tridentSpinAttackStrength == 0.0F) { + ItemStack itemStack = stack.copyWithCount(1); // Paper + Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent( +- ThrownTrident::new, serverLevel, itemStack, player, 0.0F, 2.5F, 1.0F ++ ThrownTrident::new, serverLevel, itemStack, player, 0.0F, 2.5F, (float) serverLevel.purpurConfig.tridentProjectileOffset // Purpur - Projectile offset config + ); + // Paper start - PlayerLaunchProjectileEvent + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity()); +@@ -93,6 +_,7 @@ + return false; + } + ThrownTrident thrownTrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent ++ thrownTrident.setActualEnchantments(stack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting + if (event.shouldConsume()) { + stack.hurtWithoutBreaking(1, player); // Paper - PlayerLaunchProjectileEvent + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch new file mode 100644 index 000000000..65243f097 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java ++++ b/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java +@@ -20,6 +_,12 @@ + @Override + // CraftBukkit start + public boolean apply(Level level, ItemStack stack, LivingEntity entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { ++ // Purpur start - Option to toggle milk curing bad omen ++ net.minecraft.world.effect.MobEffectInstance badOmen = entity.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN); ++ if (!level.purpurConfig.milkCuresBadOmen && stack.is(net.minecraft.world.item.Items.MILK_BUCKET) && badOmen != null) { ++ return entity.removeAllEffects(cause) && entity.addEffect(badOmen); ++ } ++ // Purpur end - Option to toggle milk curing bad omen + return entity.removeAllEffects(cause); + // CraftBukkit end + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch new file mode 100644 index 000000000..fcee0a726 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/world/item/crafting/Ingredient.java ++++ b/net/minecraft/world/item/crafting/Ingredient.java +@@ -36,6 +_,7 @@ + // CraftBukkit start + @javax.annotation.Nullable + private java.util.Set itemStacks; // Paper - Improve exact choice recipe ingredients ++ public Predicate predicate; // Purpur - Add predicate to recipe's ExactChoice ingredient + + public boolean isExact() { + return this.itemStacks != null; +@@ -88,6 +_,11 @@ + return this.itemStacks.contains(stack); // Paper - Improve exact choice recipe ingredients (hashing FTW!) + } + // CraftBukkit end ++ // Purpur start - Add predicate to recipe's ExactChoice ingredient ++ if (predicate != null) { ++ return predicate.test(stack.asBukkitCopy()); ++ } ++ // Purpur end - Add predicate to recipe's ExactChoice ingredient + return stack.is(this.values); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch new file mode 100644 index 000000000..ff04dde7b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch @@ -0,0 +1,61 @@ +--- a/net/minecraft/world/item/enchantment/EnchantmentHelper.java ++++ b/net/minecraft/world/item/enchantment/EnchantmentHelper.java +@@ -616,4 +_,58 @@ + interface EnchantmentVisitor { + void accept(Holder enchantment, int level); + } ++ ++ // Purpur start - Enchantment convenience methods ++ public static Holder.Reference getEnchantmentHolder(ResourceKey enchantment) { ++ return net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(enchantment); ++ } ++ ++ public static int getItemEnchantmentLevel(ResourceKey enchantment, ItemStack stack) { ++ return getItemEnchantmentLevel(getEnchantmentHolder(enchantment), stack); ++ } ++ // Purpur end - Enchantment convenience methods ++ ++ // Purpur start - Add option to mend the most damaged equipment first ++ public static Optional getMostDamagedItemWith(DataComponentType componentType, LivingEntity entity) { ++ ItemStack maxStack = null; ++ EquipmentSlot maxSlot = null; ++ float maxPercent = 0.0F; ++ ++ equipmentSlotLoop: ++ for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) { ++ ItemStack stack = entity.getItemBySlot(equipmentSlot); ++ ++ // do not even check enchantments for item with lower or equal damage percent ++ float percent = stack.getDamagePercent(); ++ if (percent <= maxPercent) { ++ continue; ++ } ++ ++ ItemEnchantments itemEnchantments = stack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); ++ ++ for (Entry> entry : itemEnchantments.entrySet()) { ++ Enchantment enchantment = entry.getKey().value(); ++ ++ net.minecraft.core.component.DataComponentMap effects = enchantment.effects(); ++ if (!effects.has(componentType)) { ++ // try with another enchantment ++ continue; ++ } ++ ++ if (enchantment.matchingSlot(equipmentSlot)) { ++ maxStack = stack; ++ maxSlot = equipmentSlot; ++ maxPercent = percent; ++ ++ // check another slot now ++ continue equipmentSlotLoop; ++ } ++ } ++ } ++ ++ return maxStack != null ++ ? Optional.of(new EnchantedItemInUse(maxStack, maxSlot, entity)) ++ : Optional.empty(); ++ } ++ // Purpur end - Add option to mend the most damaged equipment first + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch new file mode 100644 index 000000000..8a8607dab --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/item/enchantment/ItemEnchantments.java ++++ b/net/minecraft/world/item/enchantment/ItemEnchantments.java +@@ -32,7 +_,7 @@ + private static final java.util.Comparator> ENCHANTMENT_ORDER = java.util.Comparator.comparing(Holder::getRegisteredName); + public static final ItemEnchantments EMPTY = new ItemEnchantments(new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER)); + // Paper end - sort enchantments +- private static final Codec LEVEL_CODEC = Codec.intRange(1, 255); ++ private static final Codec LEVEL_CODEC = Codec.intRange(1, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)); // Purpur - Add toggle for enchant level clamping + public static final Codec CODEC = Codec.unboundedMap(Enchantment.CODEC, LEVEL_CODEC) + .xmap( + map -> new net.minecraft.world.item.enchantment.ItemEnchantments(net.minecraft.util.Util.make(new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), m -> m.putAll(map))), // Paper - sort enchantments +@@ -50,7 +_,7 @@ + + for (Entry> entry : enchantments.object2IntEntrySet()) { + int intValue = entry.getIntValue(); +- if (intValue < 0 || intValue > 255) { ++ if (intValue < 0 || intValue > (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)) { // Purpur - Add toggle for enchant level clamping + throw new IllegalArgumentException("Enchantment " + entry.getKey() + " has invalid level " + intValue); + } + } +@@ -133,13 +_,13 @@ + if (level <= 0) { + this.enchantments.removeInt(enchantment); + } else { +- this.enchantments.put(enchantment, Math.min(level, 255)); ++ this.enchantments.put(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767))); // Purpur - Add toggle for enchant level clamping + } + } + + public void upgrade(Holder enchantment, int level) { + if (level > 0) { +- this.enchantments.merge(enchantment, Math.min(level, 255), Integer::max); ++ this.enchantments.merge(enchantment, Math.min(level, (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)), Integer::max); // Purpur - Add toggle for enchant level clamping + } + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch new file mode 100644 index 000000000..d173c48d1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/item/trading/MerchantOffer.java ++++ b/net/minecraft/world/item/trading/MerchantOffer.java +@@ -143,8 +_,13 @@ + } + + public void updateDemand() { ++ // Purpur start - Configurable minimum demand for trades ++ this.updateDemand(0); ++ } ++ public void updateDemand(int minimumDemand) { ++ // Purpur end - Configurable minimum demand for trades + this.demand = this.demand + this.uses - (this.maxUses - this.uses); +- if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.preventNegativeVillagerDemand) this.demand = Math.max(0, this.demand); // Paper - Fix MC-163962 ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.preventNegativeVillagerDemand) this.demand = Math.max(minimumDemand, this.demand); // Paper - Fix MC-163962 // Purpur - Configurable minimum demand for trades + } + + public ItemStack assemble() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/BaseSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/BaseSpawner.java.patch new file mode 100644 index 000000000..7e004e6aa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/BaseSpawner.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/BaseSpawner.java ++++ b/net/minecraft/world/level/BaseSpawner.java +@@ -60,6 +_,7 @@ + } + + public boolean isNearPlayer(Level level, BlockPos pos) { ++ if (level.purpurConfig.spawnerDeactivateByRedstone && level.hasNeighborSignal(pos)) return false; // Purpur - Redstone deactivates spawners + return level.hasNearbyAlivePlayerThatAffectsSpawning(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, this.requiredPlayerRange); // Paper - Affects Spawning API + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/EntityGetter.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/EntityGetter.java.patch new file mode 100644 index 000000000..a2b6b7872 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/EntityGetter.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/EntityGetter.java ++++ b/net/minecraft/world/level/EntityGetter.java +@@ -182,7 +_,7 @@ + + default boolean hasNearbyAlivePlayer(double x, double y, double z, double distance) { + for (Player player : this.players()) { +- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) { ++ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) { // Purpur - AFK API + double d = player.distanceToSqr(x, y, z); + if (distance < 0.0 || d < distance * distance) { + return true; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch new file mode 100644 index 000000000..51043e267 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch @@ -0,0 +1,82 @@ +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -166,11 +_,55 @@ + } + // Paper end - add paper world config + ++ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files + public static @Nullable BlockPos lastPhysicsProblem; // Spigot + private int tileTickPosition; + public final Map explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions + public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here + ++ // Purpur start - Add adjustable breeding cooldown to config ++ private com.google.common.cache.Cache playerBreedingCooldowns; ++ ++ private com.google.common.cache.Cache getNewBreedingCooldownCache() { ++ return com.google.common.cache.CacheBuilder.newBuilder().expireAfterWrite(this.purpurConfig.animalBreedingCooldownSeconds, java.util.concurrent.TimeUnit.SECONDS).build(); ++ } ++ ++ public void resetBreedingCooldowns() { ++ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); ++ } ++ ++ public boolean hasBreedingCooldown(java.util.UUID player, Class animalType) { // Purpur ++ return this.playerBreedingCooldowns.getIfPresent(new BreedingCooldownPair(player, animalType)) != null; ++ } ++ ++ public void addBreedingCooldown(java.util.UUID player, Class animalType) { ++ this.playerBreedingCooldowns.put(new BreedingCooldownPair(player, animalType), new Object()); ++ } ++ ++ private static final class BreedingCooldownPair { ++ private final java.util.UUID playerUUID; ++ private final Class animalType; ++ ++ public BreedingCooldownPair(java.util.UUID playerUUID, Class animalType) { ++ this.playerUUID = playerUUID; ++ this.animalType = animalType; ++ } ++ ++ @Override ++ public boolean equals(Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ BreedingCooldownPair that = (BreedingCooldownPair) o; ++ return playerUUID.equals(that.playerUUID) && animalType.equals(that.animalType); ++ } ++ ++ @Override ++ public int hashCode() { ++ return java.util.Objects.hash(playerUUID, animalType); ++ } ++ } ++ // Purpur end - Add adjustable breeding cooldown to config ++ + public CraftWorld getWorld() { + return this.world; + } +@@ -846,6 +_,8 @@ + // Paper end - getblock optimisations - cache world height/sections + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot + this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config ++ this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), environment); // Purpur - Purpur config files ++ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - Add adjustable breeding cooldown to config + this.generator = generator; + this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment); + +@@ -2091,4 +_,14 @@ + return this.moonrise$getEntityLookup().getEntityCount(); // Paper - rewrite chunk system + } + // Paper end - allow patching this logic ++ ++ // Purpur start - Add allow water in end world option ++ public boolean isNether() { ++ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER; ++ } ++ ++ public boolean isTheEnd() { ++ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END; ++ } ++ // Purpur end - Add allow water in end world option + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch new file mode 100644 index 000000000..447d32bc2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -264,7 +_,7 @@ + mutableBlockPos.set(x, y, z); + double d = x + 0.5; + double d1 = z + 0.5; +- Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, false); ++ Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, level.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur - mob spawning option to ignore creative players + if (nearestPlayer != null) { + double d2 = nearestPlayer.distanceToSqr(d, y, d1); + if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch new file mode 100644 index 000000000..898c4dda1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -640,6 +_,23 @@ + this.directMappedBlockCache = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH]; + this.mutablePos = new BlockPos.MutableBlockPos(); + // Paper end - collision optimisations ++ // Purpur start - add PreExplodeEvents ++ if (this.source != null) { ++ Location location = new Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z); ++ if(!new org.purpurmc.purpur.event.entity.PreEntityExplodeEvent(this.source.getBukkitEntity(), location, this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, org.bukkit.craftbukkit.CraftExplosionResult.toExplosionResult(getBlockInteraction())).callEvent()) { ++ this.wasCanceled = true; ++ return 0; ++ } ++ } else { ++ Location location = new Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z); ++ org.bukkit.block.Block block = location.getBlock(); ++ org.bukkit.block.BlockState blockState = (this.damageSource.causingBlockSnapshot() != null) ? this.damageSource.causingBlockSnapshot() : block.getState(); ++ if(!new org.purpurmc.purpur.event.PreBlockExplodeEvent(location.getBlock(), this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F, blockState, org.bukkit.craftbukkit.CraftExplosionResult.toExplosionResult(getBlockInteraction())).callEvent()) { ++ this.wasCanceled = true; ++ return 0; ++ } ++ } ++ // Purpur end - Add PreExplodeEvents + this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center); + List list = this.calculateExplodedPositions(); + this.hurtEntities(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch new file mode 100644 index 000000000..e604b9e74 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch @@ -0,0 +1,56 @@ +--- a/net/minecraft/world/level/block/AnvilBlock.java ++++ b/net/minecraft/world/level/block/AnvilBlock.java +@@ -54,6 +_,53 @@ + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getClockWise()); + } + ++ // Purpur start - Anvil repair/damage options ++ @Override ++ protected net.minecraft.world.InteractionResult useItemOn(final net.minecraft.world.item.ItemStack stack, final BlockState state, final Level world, final BlockPos pos, final Player player, final net.minecraft.world.InteractionHand hand, final BlockHitResult hit) { ++ if (world.purpurConfig.anvilRepairIngotsAmount > 0 && stack.is(net.minecraft.world.item.Items.IRON_INGOT)) { ++ if (stack.getCount() < world.purpurConfig.anvilRepairIngotsAmount) { ++ // not enough iron ingots, play "error" sound and consume ++ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ if (state.is(Blocks.DAMAGED_ANVIL)) { ++ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); ++ } else if (state.is(Blocks.CHIPPED_ANVIL)) { ++ world.setBlock(pos, Blocks.ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); ++ } else if (state.is(Blocks.ANVIL)) { ++ // anvil is already fully repaired, play "error" sound and consume ++ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(world.purpurConfig.anvilRepairIngotsAmount); ++ } ++ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_PLACE, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ if (world.purpurConfig.anvilDamageObsidianAmount > 0 && stack.is(net.minecraft.world.item.Items.OBSIDIAN)) { ++ if (stack.getCount() < world.purpurConfig.anvilDamageObsidianAmount) { ++ // not enough obsidian, play "error" sound and consume ++ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_HIT, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ if (state.is(Blocks.DAMAGED_ANVIL)) { ++ world.destroyBlock(pos, false); ++ } else if (state.is(Blocks.CHIPPED_ANVIL)) { ++ world.setBlock(pos, Blocks.DAMAGED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); ++ } else if (state.is(Blocks.ANVIL)) { ++ world.setBlock(pos, Blocks.CHIPPED_ANVIL.defaultBlockState().setValue(FACING, state.getValue(FACING)), 3); ++ } ++ if (!player.getAbilities().instabuild) { ++ stack.shrink(world.purpurConfig.anvilDamageObsidianAmount); ++ } ++ world.playSound(null, pos, net.minecraft.sounds.SoundEvents.ANVIL_LAND, net.minecraft.sounds.SoundSource.BLOCKS, 1.0F, 1.0F); ++ return net.minecraft.world.InteractionResult.CONSUME; ++ } ++ return net.minecraft.world.InteractionResult.TRY_WITH_EMPTY_HAND; ++ } ++ // Purpur end - Anvil repair/damage options ++ + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (!level.isClientSide()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AzaleaBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AzaleaBlock.java.patch new file mode 100644 index 000000000..066e2cfd6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AzaleaBlock.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/level/block/AzaleaBlock.java ++++ b/net/minecraft/world/level/block/AzaleaBlock.java +@@ -50,6 +_,20 @@ + + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { ++ // Purpur start - Chance for azalea blocks to grow into trees naturally ++ growTree(level, random, pos, state); ++ } ++ ++ @Override ++ public void randomTick(net.minecraft.world.level.block.state.BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { ++ double chance = state.getBlock() == Blocks.FLOWERING_AZALEA ? world.purpurConfig.floweringAzaleaGrowthChance : world.purpurConfig.azaleaGrowthChance; ++ if (chance > 0.0D && world.getMaxLocalRawBrightness(pos.above()) > 9 && random.nextDouble() < chance) { ++ growTree(world, random, pos, state); ++ } ++ } ++ ++ private void growTree(ServerLevel level, RandomSource random, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) { ++ // Purpur end - Chance for azalea blocks to grow into trees naturally + TreeGrower.AZALEA.growTree(level, level.getChunkSource().getGenerator(), pos, state, random); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java.patch new file mode 100644 index 000000000..fd6497469 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java ++++ b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java +@@ -39,6 +_,7 @@ + } + + protected static boolean scanForWater(BlockState state, BlockGetter level, BlockPos pos) { ++ if (!((net.minecraft.world.level.LevelAccessor) level).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Config to not let coral die + if (state.getValue(WATERLOGGED)) { + return true; + } else { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BedBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BedBlock.java.patch new file mode 100644 index 000000000..c9f3ab128 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BedBlock.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/world/level/block/BedBlock.java ++++ b/net/minecraft/world/level/block/BedBlock.java +@@ -100,7 +_,7 @@ + } + + Vec3 center = pos.getCenter(); +- level.explode(null, level.damageSources().badRespawnPointExplosion(center), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); ++ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(center), null, center, (float) level.purpurConfig.bedExplosionPower, level.purpurConfig.bedExplosionFire, level.purpurConfig.bedExplosionEffect); // Purpur - Implement bed explosion options + return InteractionResult.SUCCESS_SERVER; + } else if (state.getValue(OCCUPIED)) { + if (bedRule.explodes()) return this.explodeBed(state, level, pos); // Paper - check explode first +@@ -153,7 +_,7 @@ + } + + Vec3 center = pos.getCenter(); +- level.explode(null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state ++ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), null, center, (float) level.purpurConfig.bedExplosionPower, level.purpurConfig.bedExplosionFire, level.purpurConfig.bedExplosionEffect); // CraftBukkit - add state // Purpur - Implement bed explosion options + return InteractionResult.SUCCESS_SERVER; + } + // CraftBukkit end +@@ -170,7 +_,7 @@ + + @Override + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { +- super.fallOn(level, state, pos, entity, fallDistance * 0.5); ++ super.fallOn(level, state, pos, entity, fallDistance); // Purpur - Configurable block fall damage modifiers + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch new file mode 100644 index 000000000..5db370adf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/BigDripleafBlock.java ++++ b/net/minecraft/world/level/block/BigDripleafBlock.java +@@ -251,7 +_,7 @@ + playTiltSound(level, pos, sound); + } + +- int _int = DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt); ++ int _int = level.purpurConfig.bigDripleafTiltDelay.getOrDefault(tilt, -1); // Purpur - Big dripleaf tilt delay + if (_int != -1) { + level.scheduleTick(pos, this, _int); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Block.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Block.java.patch new file mode 100644 index 000000000..7ea2b64af --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Block.java.patch @@ -0,0 +1,89 @@ +--- a/net/minecraft/world/level/block/Block.java ++++ b/net/minecraft/world/level/block/Block.java +@@ -113,6 +_,10 @@ + public static final int UPDATE_LIMIT = 512; + protected final StateDefinition stateDefinition; + private BlockState defaultBlockState; ++ // Purpur start - Configurable block fall damage modifiers ++ public float fallDamageMultiplier = 1.0F; ++ public float fallDistanceMultiplier = 1.0F; ++ // Purpur end - Configurable block fall damage modifiers + // Paper start - Protect Bedrock and End Portal/Frames from being destroyed + public final boolean isDestroyable() { + return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || +@@ -395,7 +_,7 @@ + event.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping + event.callEvent(); + for (org.bukkit.inventory.ItemStack drop : event.getDrops()) { +- popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop)); ++ popResource(serverLevel, pos, applyLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur - Persistent BlockEntity Lore and DisplayName + } + state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping + block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping +@@ -413,7 +_,7 @@ + + public static void dropResources(BlockState state, LevelAccessor level, BlockPos pos, @Nullable BlockEntity blockEntity) { + if (level instanceof ServerLevel) { +- getDrops(state, (ServerLevel)level, pos, blockEntity).forEach(stack -> popResource((ServerLevel)level, pos, stack)); ++ getDrops(state, (ServerLevel)level, pos, blockEntity).forEach(stack -> popResource((ServerLevel)level, pos, applyLoreFromTile(stack, blockEntity))); // Purpur - Persistent BlockEntity Lore and DisplayName + state.spawnAfterBreak((ServerLevel)level, pos, ItemStack.EMPTY, true); + } + } +@@ -425,11 +_,30 @@ + public static void dropResources(BlockState state, Level level, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool, boolean dropExperience) { + // Paper end - Properly handle xp dropping + if (level instanceof ServerLevel) { +- getDrops(state, (ServerLevel)level, pos, blockEntity, entity, tool).forEach(stack -> popResource(level, pos, stack)); ++ getDrops(state, (ServerLevel)level, pos, blockEntity, entity, tool).forEach(stack -> popResource(level, pos, applyLoreFromTile(stack, blockEntity))); // Purpur - Persistent BlockEntity Lore and DisplayName + state.spawnAfterBreak((ServerLevel)level, pos, tool, dropExperience); // Paper - Properly handle xp dropping + } + } + ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ private static ItemStack applyLoreFromTile(ItemStack stack, @Nullable BlockEntity blockEntity) { ++ if (stack.getItem() instanceof BlockItem) { ++ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel) { ++ net.minecraft.world.item.component.ItemLore lore = blockEntity.getPersistentLore(); ++ net.minecraft.core.component.DataComponentPatch.Builder builder = net.minecraft.core.component.DataComponentPatch.builder(); ++ if (blockEntity.getLevel().purpurConfig.persistentTileEntityLore && lore != null) { ++ builder.set(net.minecraft.core.component.DataComponents.LORE, lore); ++ } ++ if (!blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayName) { ++ builder.remove(net.minecraft.core.component.DataComponents.CUSTOM_NAME); ++ } ++ stack.applyComponents(builder.build()); ++ } ++ } ++ return stack; ++ } ++ // Purpur end - Persistent BlockEntity Lore and DisplayName ++ + public static void popResource(Level level, BlockPos pos, ItemStack stack) { + double d = EntityType.ITEM.getHeight() / 2.0; + double d1 = pos.getX() + 0.5 + Mth.nextDouble(level.random, -0.25, 0.25); +@@ -509,7 +_,15 @@ + } + + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { +- } ++ this.placer = placer; // Purpur - Store placer on Block when placed ++ } ++ ++ // Purpur start - Store placer on Block when placed ++ @Nullable protected LivingEntity placer = null; ++ public void forgetPlacer() { ++ this.placer = null; ++ } ++ // Purpur end - Store placer on Block when placed + + public boolean isPossibleToRespawnInThis(BlockState state) { + return !state.isSolid() && !state.liquid(); +@@ -520,7 +_,7 @@ + } + + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { +- entity.causeFallDamage(fallDistance, 1.0F, entity.damageSources().fall()); ++ entity.causeFallDamage(fallDistance * fallDistanceMultiplier, fallDamageMultiplier, entity.damageSources().fall()); // Purpur - Configurable block fall damage modifiers + } + + public void updateEntityMovementAfterFallOn(BlockGetter level, Entity entity) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch new file mode 100644 index 000000000..9c9f12e39 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/world/level/block/Blocks.java ++++ b/net/minecraft/world/level/block/Blocks.java +@@ -6806,6 +_,7 @@ + BlockBehaviour.Properties.of() + .mapColor(MapColor.PLANT) + .forceSolidOff() ++ .randomTicks() // Purpur - Chance for azalea blocks to grow into trees naturally + .instabreak() + .sound(SoundType.AZALEA) + .noOcclusion() +@@ -6817,6 +_,7 @@ + BlockBehaviour.Properties.of() + .mapColor(MapColor.PLANT) + .forceSolidOff() ++ .randomTicks() // Purpur - Chance for azalea blocks to grow into trees naturally + .instabreak() + .sound(SoundType.FLOWERING_AZALEA) + .noOcclusion() diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch new file mode 100644 index 000000000..6d57a3ee0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/block/BubbleColumnBlock.java ++++ b/net/minecraft/world/level/block/BubbleColumnBlock.java +@@ -99,9 +_,9 @@ + if (state.is(Blocks.BUBBLE_COLUMN)) { + return state; + } else if (state.is(Blocks.SOUL_SAND)) { +- return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, false); ++ return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, org.purpurmc.purpur.PurpurConfig.soulSandBlockReverseBubbleColumnFlow); // Purpur - Config to reverse bubble column flow + } else { +- return state.is(Blocks.MAGMA_BLOCK) ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, true) : Blocks.WATER.defaultBlockState(); ++ return state.is(Blocks.MAGMA_BLOCK) ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, !org.purpurmc.purpur.PurpurConfig.magmaBlockReverseBubbleColumnFlow) : Blocks.WATER.defaultBlockState(); // Purpur - Config to reverse bubble column flow + } + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch new file mode 100644 index 000000000..02f5c4001 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch @@ -0,0 +1,55 @@ +--- a/net/minecraft/world/level/block/CactusBlock.java ++++ b/net/minecraft/world/level/block/CactusBlock.java +@@ -22,7 +_,7 @@ + import net.minecraft.world.phys.shapes.CollisionContext; + import net.minecraft.world.phys.shapes.VoxelShape; + +-public class CactusBlock extends Block { ++public class CactusBlock extends Block implements BonemealableBlock { // Purpur - bonemealable cactus + public static final MapCodec CODEC = simpleCodec(CactusBlock::new); + public static final IntegerProperty AGE = BlockStateProperties.AGE_15; + public static final int MAX_AGE = 15; +@@ -117,7 +_,7 @@ + protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + for (Direction direction : Direction.Plane.HORIZONTAL) { + BlockState blockState = level.getBlockState(pos.relative(direction)); +- if (blockState.isSolid() || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) { ++ if ((level.getWorldBorder().world == null || level.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors) && blockState.isSolid() || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) { // Purpur - Cactus breaks from solid neighbors config + return false; + } + } +@@ -141,4 +_,34 @@ + protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { + return false; + } ++ ++ // Purpur start - bonemealable cactus ++ @Override ++ public boolean isValidBonemealTarget(final LevelReader world, final BlockPos pos, final BlockState state) { ++ if (!((Level) world).purpurConfig.cactusAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false; ++ ++ int cactusHeight = 0; ++ while (world.getBlockState(pos.below(cactusHeight)).is(this)) { ++ cactusHeight++; ++ } ++ ++ return cactusHeight < ((Level) world).paperConfig().maxGrowthHeight.cactus; ++ } ++ ++ @Override ++ public boolean isBonemealSuccess(Level world, RandomSource random, BlockPos pos, BlockState state) { ++ return true; ++ } ++ ++ @Override ++ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { ++ int cactusHeight = 0; ++ while (world.getBlockState(pos.below(cactusHeight)).is(this)) { ++ cactusHeight++; ++ } ++ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.cactus - cactusHeight; i++) { ++ world.setBlockAndUpdate(pos.above(i), state.setValue(CactusBlock.AGE, 0)); ++ } ++ } ++ // Purpur end - bonemealable cactus + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch new file mode 100644 index 000000000..c78b443f8 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/CakeBlock.java ++++ b/net/minecraft/world/level/block/CakeBlock.java +@@ -109,6 +_,7 @@ + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel); + + if (!event.isCancelled()) { ++ if (player.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) player.burpDelay = player.level().purpurConfig.playerBurpDelay; // Purpur - Burp after eating food fills hunger bar completely + player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch new file mode 100644 index 000000000..d31073993 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/CampfireBlock.java ++++ b/net/minecraft/world/level/block/CampfireBlock.java +@@ -123,7 +_,7 @@ + return this.defaultBlockState() + .setValue(WATERLOGGED, flag) + .setValue(SIGNAL_FIRE, this.isSmokeSource(level.getBlockState(clickedPos.below()))) +- .setValue(LIT, !flag) ++ .setValue(LIT, level.getMinecraftWorld().purpurConfig.campFireLitWhenPlaced && !flag) // Purpur - Campfire option for lit when placed + .setValue(FACING, context.getHorizontalDirection()); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch new file mode 100644 index 000000000..145b63efa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/world/level/block/CarvedPumpkinBlock.java ++++ b/net/minecraft/world/level/block/CarvedPumpkinBlock.java +@@ -67,7 +_,7 @@ + if (blockPatternMatch != null) { + SnowGolem snowGolem = EntityType.SNOW_GOLEM.create(level, EntitySpawnReason.TRIGGERED); + if (snowGolem != null) { +- spawnGolemInWorld(level, blockPatternMatch, snowGolem, blockPatternMatch.getBlock(0, 2, 0).getPos()); ++ spawnGolemInWorld(level, blockPatternMatch, snowGolem, blockPatternMatch.getBlock(0, 2, 0).getPos(), this.placer); // Purpur - Summoner API + return; + } + } +@@ -77,7 +_,7 @@ + IronGolem ironGolem = EntityType.IRON_GOLEM.create(level, EntitySpawnReason.TRIGGERED); + if (ironGolem != null) { + ironGolem.setPlayerCreated(true); +- spawnGolemInWorld(level, blockPatternMatch1, ironGolem, blockPatternMatch1.getBlock(1, 2, 0).getPos()); ++ spawnGolemInWorld(level, blockPatternMatch1, ironGolem, blockPatternMatch1.getBlock(1, 2, 0).getPos(), this.placer); // Purpur - Summoner API + return; + } + } +@@ -86,7 +_,7 @@ + if (blockPatternMatch2 != null) { + CopperGolem copperGolem = EntityType.COPPER_GOLEM.create(level, EntitySpawnReason.TRIGGERED); + if (copperGolem != null) { +- spawnGolemInWorld(level, blockPatternMatch2, copperGolem, blockPatternMatch2.getBlock(0, 0, 0).getPos()); ++ spawnGolemInWorld(level, blockPatternMatch2, copperGolem, blockPatternMatch2.getBlock(0, 0, 0).getPos(), this.placer); // Purpur - Summoner API + if (!copperGolem.valid) return; // Paper - entityspawnevent - entity was not added to the world so prevent world mutation + this.replaceCopperBlockWithChest(level, blockPatternMatch2); + copperGolem.spawn(this.getWeatherStateFromPattern(blockPatternMatch2)); +@@ -105,7 +_,20 @@ + .getAge(); + } + ++ @io.papermc.paper.annotation.DoNotUse + private static void spawnGolemInWorld(Level level, BlockPattern.BlockPatternMatch patternMatch, Entity golem, BlockPos pos) { ++ // Purpur start - Summoner API ++ spawnGolemInWorld(level, patternMatch, golem, pos, null); ++ } ++ private static void spawnGolemInWorld(Level level, BlockPattern.BlockPatternMatch patternMatch, Entity golem, BlockPos pos, net.minecraft.world.entity.LivingEntity placer) { ++ java.util.UUID summoner = placer == null ? null : placer.getUUID(); ++ switch (golem) { ++ case SnowGolem snowGolem -> snowGolem.setSummoner(summoner); ++ case IronGolem ironGolem -> ironGolem.setSummoner(summoner); ++ case CopperGolem copperGolem -> copperGolem.setSummoner(summoner); ++ default -> throw new IllegalStateException("Unexpected value: " + golem); ++ } ++ // Purpur end - Summoner API + // clearPatternBlocks(level, patternMatch); // Paper - moved down + golem.snapTo(pos.getX() + 0.5, pos.getY() + 0.05, pos.getZ() + 0.5, 0.0F, 0.0F); + // Paper start diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch new file mode 100644 index 000000000..6ead2196b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/level/block/CauldronBlock.java ++++ b/net/minecraft/world/level/block/CauldronBlock.java +@@ -32,8 +_,8 @@ + + protected static boolean shouldHandlePrecipitation(Level level, Biome.Precipitation precipitation) { + return precipitation == Biome.Precipitation.RAIN +- ? level.getRandom().nextFloat() < 0.05F +- : precipitation == Biome.Precipitation.SNOW && level.getRandom().nextFloat() < 0.1F; ++ ? level.getRandom().nextFloat() < level.purpurConfig.cauldronRainChance // Purpur - Cauldron fill chances ++ : precipitation == Biome.Precipitation.SNOW && level.getRandom().nextFloat() < level.purpurConfig.cauldronPowderSnowChance; // Purpur - Cauldron fill chances + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch new file mode 100644 index 000000000..a024a56e2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/block/CaveVinesBlock.java ++++ b/net/minecraft/world/level/block/CaveVinesBlock.java +@@ -92,4 +_,11 @@ + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + level.setBlock(pos, state.setValue(BERRIES, true), Block.UPDATE_CLIENTS); + } ++ ++ // Purpur start - cave vines configurable max growth age ++ @Override ++ public int getMaxGrowthAge() { ++ return org.purpurmc.purpur.PurpurConfig.caveVinesMaxGrowthAge; ++ } ++ // Purpur end - cave vines configurable max growth age + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch new file mode 100644 index 000000000..54d2fa463 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/ChangeOverTimeBlock.java ++++ b/net/minecraft/world/level/block/ChangeOverTimeBlock.java +@@ -51,7 +_,7 @@ + } + + float f = (float)(i1 + 1) / (i1 + i + 1); +- float f1 = f * f * this.getChanceModifier(); ++ float f1 = level.purpurConfig.disableOxidationProximityPenalty ? this.getChanceModifier() :f * f * this.getChanceModifier();// Purpur - option to disable the copper oxidation proximity penalty + return random.nextFloat() < f1 ? this.getNext(state) : Optional.empty(); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch new file mode 100644 index 000000000..7da971fbb --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/ChestBlock.java ++++ b/net/minecraft/world/level/block/ChestBlock.java +@@ -370,6 +_,7 @@ + } + + public static boolean isBlockedChestByBlock(BlockGetter level, BlockPos pos) { ++ if (level instanceof Level level1 && level1.purpurConfig.chestOpenWithBlockOnTop) return false; // Purpur - Option for chests to open even with a solid block on top + BlockPos blockPos = pos.above(); + return level.getBlockState(blockPos).isRedstoneConductor(level, blockPos); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch new file mode 100644 index 000000000..f2b522991 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch @@ -0,0 +1,66 @@ +--- a/net/minecraft/world/level/block/ComposterBlock.java ++++ b/net/minecraft/world/level/block/ComposterBlock.java +@@ -250,23 +_,52 @@ + ) { + int levelValue = state.getValue(LEVEL); + if (levelValue < 8 && COMPOSTABLES.containsKey(stack.getItem())) { +- if (levelValue < 7 && !level.isClientSide()) { +- BlockState blockState = addItem(player, state, level, pos, stack); +- // Paper start - handle cancelled events +- if (blockState == null) { +- return InteractionResult.PASS; +- } +- // Paper end +- level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != blockState ? 1 : 0); +- player.awardStat(Stats.ITEM_USED.get(stack.getItem())); +- stack.consume(1, player); +- } ++ // Purpur start - sneak to bulk process composter ++ BlockState newState = process(levelValue, player, state, level, pos, stack); ++ if (newState == null) { ++ return InteractionResult.PASS; ++ } ++ if (level.purpurConfig.composterBulkProcess && player.isShiftKeyDown() && newState != state) { ++ BlockState oldState; ++ int oldCount, newCount, oldLevel, newLevel; ++ do { ++ oldState = newState; ++ oldCount = stack.getCount(); ++ oldLevel = oldState.getValue(ComposterBlock.LEVEL); ++ newState = process(oldLevel, player, oldState, level, pos, stack); ++ if (newState == null) { ++ return InteractionResult.PASS; ++ } ++ newCount = stack.getCount(); ++ newLevel = newState.getValue(ComposterBlock.LEVEL); ++ } while (newCount > 0 && (newCount != oldCount || newLevel != oldLevel || newState != oldState)); ++ } ++ // Purpur end - Sneak to bulk process composter + + return InteractionResult.SUCCESS; + } else { + return super.useItemOn(stack, state, level, pos, player, hand, hitResult); + } + } ++ ++ // Purpur start - sneak to bulk process composter ++ private static @Nullable BlockState process(int levelValue, Player player, BlockState state, Level level, BlockPos pos, ItemStack stack) { ++ if (levelValue < 7 && !level.isClientSide()) { ++ BlockState blockState = ComposterBlock.addItem(player, state, level, pos, stack); ++ // Paper start - handle cancelled events ++ if (blockState == null) { ++ return null; ++ } ++ // Paper end ++ ++ level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != blockState ? 1 : 0); ++ player.awardStat(Stats.ITEM_USED.get(stack.getItem())); ++ stack.consume(1, player); ++ return blockState; ++ } ++ return state; ++ } ++ // Purpur end - Sneak to bulk process composter + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch new file mode 100644 index 000000000..690cfe031 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/CoralBlock.java ++++ b/net/minecraft/world/level/block/CoralBlock.java +@@ -65,6 +_,7 @@ + } + + protected boolean scanForWater(BlockGetter level, BlockPos pos) { ++ if (!((net.minecraft.world.level.LevelAccessor) level).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Config to not let coral die + for (Direction direction : Direction.values()) { + FluidState fluidState = level.getFluidState(pos.relative(direction)); + if (fluidState.is(FluidTags.WATER)) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CropBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CropBlock.java.patch new file mode 100644 index 000000000..1db271458 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CropBlock.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/level/block/CropBlock.java ++++ b/net/minecraft/world/level/block/CropBlock.java +@@ -169,7 +_,7 @@ + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent +- if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit ++ if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.purpurConfig.ravagerGriefableBlocks.contains(serverLevel.getBlockState(pos).getBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list + serverLevel.destroyBlock(pos, true, entity); + } + +@@ -204,4 +_,15 @@ + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(AGE); + } ++ ++ // Purpur start - Ability for hoe to replant crops ++ @Override ++ public void playerDestroy(Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops, boolean dropExp) { ++ if (world.purpurConfig.hoeReplantsCrops && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) { ++ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, getBaseSeedId()); ++ } else { ++ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp); ++ } ++ } ++ // Purpur end - Ability for hoe to replant crops + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch new file mode 100644 index 000000000..87a008eb0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/world/level/block/DoorBlock.java ++++ b/net/minecraft/world/level/block/DoorBlock.java +@@ -198,6 +_,7 @@ + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (!this.type.canOpenByHand()) { + return InteractionResult.PASS; ++ } else if (requiresRedstone(level, state, pos)) { return InteractionResult.CONSUME; // Purpur - Option to make doors require redstone + } else { + state = state.cycle(OPEN); + level.setBlock(pos, state, Block.UPDATE_CLIENTS | Block.UPDATE_IMMEDIATE); +@@ -286,4 +_,18 @@ + public static boolean isWoodenDoor(BlockState state) { + return state.getBlock() instanceof DoorBlock doorBlock && doorBlock.type().canOpenByHand(); + } ++ ++ // Purpur start - Option to make doors require redstone ++ public static boolean requiresRedstone(Level level, BlockState state, BlockPos pos) { ++ if (level.purpurConfig.doorRequiresRedstone.contains(state.getBlock())) { ++ // force update client ++ BlockPos otherPos = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN); ++ BlockState otherState = level.getBlockState(otherPos); ++ level.sendBlockUpdated(pos, state, state, Block.UPDATE_ALL); ++ level.sendBlockUpdated(otherPos, otherState, otherState, Block.UPDATE_ALL); ++ return true; ++ } ++ return false; ++ } ++ // Purpur end - Option to make doors require redstone + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch new file mode 100644 index 000000000..d08f61da6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/DragonEggBlock.java ++++ b/net/minecraft/world/level/block/DragonEggBlock.java +@@ -46,6 +_,7 @@ + } + + private void teleport(BlockState state, Level level, BlockPos pos) { ++ if (!level.purpurConfig.dragonEggTeleport) return; // Purpur - Option to disable dragon egg teleporting + WorldBorder worldBorder = level.getWorldBorder(); + + for (int i = 0; i < 1000; i++) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch new file mode 100644 index 000000000..6ee873e25 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch @@ -0,0 +1,49 @@ +--- a/net/minecraft/world/level/block/FarmBlock.java ++++ b/net/minecraft/world/level/block/FarmBlock.java +@@ -112,7 +_,7 @@ + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { + super.fallOn(level, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage. + if (level instanceof ServerLevel serverLevel +- && level.random.nextFloat() < fallDistance - 0.5 ++ && (serverLevel.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= serverLevel.purpurConfig.farmlandTrampleHeight : level.random.nextFloat() < fallDistance - 0.5) // Purpur - Configurable farmland trample height + && entity instanceof LivingEntity + && (entity instanceof Player || serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) + && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { +@@ -129,6 +_,28 @@ + return; + } + ++ if (level.purpurConfig.farmlandTramplingDisabled) return; // Purpur - Farmland trampling changes ++ if (level.purpurConfig.farmlandTramplingOnlyPlayers && !(entity instanceof Player)) return; // Purpur - Farmland trampling changes ++ ++ // Purpur start - Ability to re-add farmland mechanics from Alpha ++ if (level.purpurConfig.farmlandAlpha) { ++ Block block = level.getBlockState(pos.below()).getBlock(); ++ if (block instanceof FenceBlock || block instanceof WallBlock) { ++ return; ++ } ++ } ++ // Purpur end - Ability to re-add farmland mechanics from Alpha ++ ++ // Purpur start - Farmland trampling changes ++ if (level.purpurConfig.farmlandTramplingFeatherFalling) { ++ net.minecraft.world.item.ItemStack bootsItem = ((net.minecraft.world.entity.LivingEntity) entity).getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET); ++ ++ if (bootsItem != net.minecraft.world.item.ItemStack.EMPTY && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, bootsItem) >= (int) entity.fallDistance) { ++ return; ++ } ++ } ++ // Purpur end - Farmland trampling changes ++ + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { + return; + } +@@ -177,7 +_,7 @@ + } + } + +- return false; ++ return ((ServerLevel) level).purpurConfig.farmlandGetsMoistFromBelow && level.getFluidState(pos.relative(Direction.DOWN)).is(FluidTags.WATER); // Purpur - Allow soil to moisten from water directly under it + // Paper end - Perf: remove abstract block iteration + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch new file mode 100644 index 000000000..7dd50302a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch @@ -0,0 +1,63 @@ +--- a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java ++++ b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +@@ -34,12 +_,12 @@ + + @Override + public BlockState getStateForPlacement(RandomSource random) { +- return this.defaultBlockState().setValue(AGE, random.nextInt(25)); ++ return this.defaultBlockState().setValue(AGE, getMaxGrowthAge() == 0 ? 0 : random.nextInt(getMaxGrowthAge())); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } + + @Override + protected boolean isRandomlyTicking(BlockState state) { +- return state.getValue(AGE) < 25; ++ return state.getValue(AGE) < getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } + + @Override +@@ -55,7 +_,7 @@ + } else if (this == Blocks.CAVE_VINES) { + modifier = level.spigotConfig.caveVinesModifier; + } +- if (state.getValue(AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution ++ if (state.getValue(AGE) < getMaxGrowthAge() && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution // Purpur - kelp, cave, weeping, and twisting configurable max growth age + // Spigot end + BlockPos blockPos = pos.relative(this.growthDirection); + if (this.canGrowInto(level.getBlockState(blockPos))) { +@@ -75,11 +_,11 @@ + } + + public BlockState getMaxAgeState(BlockState state) { +- return state.setValue(AGE, 25); ++ return state.setValue(AGE, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } + + public boolean isMaxAge(BlockState state) { +- return state.getValue(AGE) == 25; ++ return state.getValue(AGE) >= getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } + + protected BlockState updateBodyAfterConvertedFromHead(BlockState head, BlockState body) { +@@ -137,13 +_,13 @@ + @Override + public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + BlockPos blockPos = pos.relative(this.growthDirection); +- int min = Math.min(state.getValue(AGE) + 1, 25); ++ int min = Math.min(state.getValue(AGE) + 1, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + int blocksToGrowWhenBonemealed = this.getBlocksToGrowWhenBonemealed(random); + + for (int i = 0; i < blocksToGrowWhenBonemealed && this.canGrowInto(level.getBlockState(blockPos)); i++) { + level.setBlockAndUpdate(blockPos, state.setValue(AGE, min)); + blockPos = blockPos.relative(this.growthDirection); +- min = Math.min(min + 1, 25); ++ min = Math.min(min + 1, getMaxGrowthAge()); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } + } + +@@ -155,4 +_,6 @@ + protected GrowingPlantHeadBlock getHeadBlock() { + return this; + } ++ ++ public abstract int getMaxGrowthAge(); // Purpur - kelp, cave, weeping, and twisting configurable max growth age + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/HayBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/HayBlock.java.patch new file mode 100644 index 000000000..1b21c6e0a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/HayBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/HayBlock.java ++++ b/net/minecraft/world/level/block/HayBlock.java +@@ -23,6 +_,6 @@ + + @Override + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { +- entity.causeFallDamage(fallDistance, 0.2F, level.damageSources().fall()); ++ super.fallOn(level, state, pos, entity, fallDistance); // Purpur - Configurable block fall damage modifiers + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/IceBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/IceBlock.java.patch new file mode 100644 index 000000000..40b35e6d5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/IceBlock.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/block/IceBlock.java ++++ b/net/minecraft/world/level/block/IceBlock.java +@@ -41,7 +_,7 @@ + public void afterDestroy(Level level, BlockPos pos, ItemStack stack) { + // Paper end - Improve Block#breakNaturally API + if (!EnchantmentHelper.hasTag(stack, EnchantmentTags.PREVENTS_ICE_MELTING)) { +- if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos)) { ++ if ((level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd) || level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos)) { // Purpur - Add allow water in end world option + level.removeBlock(pos, false); + return; + } +@@ -62,7 +_,7 @@ + + protected void melt(BlockState state, Level level, BlockPos pos) { + // Paper start - call BlockFadeEvent +- final boolean waterEvaporates = level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos); ++ final boolean waterEvaporates = (level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd) || level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos); // Purpur - Add allow water in end + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, waterEvaporates ? Blocks.AIR.defaultBlockState() : Blocks.WATER.defaultBlockState()).isCancelled()) { + return; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/KelpBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/KelpBlock.java.patch new file mode 100644 index 000000000..dd74c4921 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/KelpBlock.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/block/KelpBlock.java ++++ b/net/minecraft/world/level/block/KelpBlock.java +@@ -71,4 +_,11 @@ + protected FluidState getFluidState(BlockState state) { + return Fluids.WATER.getSource(false); + } ++ ++ // Purpur start - kelp vines configurable max growth age ++ @Override ++ public int getMaxGrowthAge() { ++ return org.purpurmc.purpur.PurpurConfig.kelpMaxGrowthAge; ++ } ++ // Purpur end - kelp vines configurable max growth age + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch new file mode 100644 index 000000000..51480df96 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/world/level/block/LiquidBlock.java ++++ b/net/minecraft/world/level/block/LiquidBlock.java +@@ -138,7 +_,7 @@ + + @Override + protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { +- if (this.shouldSpreadLiquid(level, pos, state)) { ++ if (level.purpurConfig.tickFluids && this.shouldSpreadLiquid(level, pos, state)) { // Purpur - Tick fluids config + level.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(level, pos)); // Paper - Configurable speed for water flowing over lava + } + } +@@ -175,7 +_,7 @@ + BlockState neighborState, + RandomSource random + ) { +- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { ++ if ((level.getWorldBorder().world == null || level.getWorldBorder().world.purpurConfig.tickFluids) && state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { // Purpur - Tick fluids config + scheduledTickAccess.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(level)); + } + +@@ -184,7 +_,7 @@ + + @Override + protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { +- if (this.shouldSpreadLiquid(level, pos, state)) { ++ if (level.purpurConfig.tickFluids && this.shouldSpreadLiquid(level, pos, state)) { // Purpur - Tick fluids config + level.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(level, pos)); // Paper - Configurable speed for water flowing over lava + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch new file mode 100644 index 000000000..f9d8ca928 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/MagmaBlock.java ++++ b/net/minecraft/world/level/block/MagmaBlock.java +@@ -28,7 +_,7 @@ + + @Override + public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { +- if (!entity.isSteppingCarefully() && entity instanceof LivingEntity) { ++ if ((!entity.isSteppingCarefully() || level.purpurConfig.magmaBlockDamageWhenSneaking) && entity instanceof LivingEntity) { // Purpur - Configurable damage settings for magma blocks + entity.hurt(level.damageSources().hotFloor().eventBlockDamager(level, pos), 1.0F); // CraftBukkit + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch new file mode 100644 index 000000000..c6e751612 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/block/NetherPortalBlock.java ++++ b/net/minecraft/world/level/block/NetherPortalBlock.java +@@ -68,7 +_,7 @@ + protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (level.spigotConfig.enableZombiePigmenPortalSpawns && level.isSpawningMonsters() // Spigot + && level.environmentAttributes().getValue(EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS, pos) +- && random.nextInt(2000) < level.getDifficulty().getId() ++ && random.nextInt(level.purpurConfig.piglinPortalSpawnModifier) < level.getDifficulty().getId() // Purpur - Piglin portal spawn modifier + && level.anyPlayerCloseEnoughForSpawning(pos)) { + while (level.getBlockState(pos).is(this)) { + pos = pos.below(); +@@ -126,7 +_,7 @@ + @Override + public int getPortalTransitionTime(ServerLevel level, Entity entity) { + return entity instanceof Player player +- ? Math.max( ++ ? player.canPortalInstant ? 1 : Math.max( // Purpur - Add portal permission bypass + 0, + level.getGameRules() + .get(player.getAbilities().invulnerable ? GameRules.PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.PLAYERS_NETHER_PORTAL_DEFAULT_DELAY) diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch new file mode 100644 index 000000000..362dae20a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/world/level/block/NetherWartBlock.java ++++ b/net/minecraft/world/level/block/NetherWartBlock.java +@@ -16,7 +_,7 @@ + import net.minecraft.world.phys.shapes.CollisionContext; + import net.minecraft.world.phys.shapes.VoxelShape; + +-public class NetherWartBlock extends VegetationBlock { ++public class NetherWartBlock extends VegetationBlock implements BonemealableBlock { // Purpur - bonemealable netherwart + public static final MapCodec CODEC = simpleCodec(NetherWartBlock::new); + public static final int MAX_AGE = 3; + public static final IntegerProperty AGE = BlockStateProperties.AGE_3; +@@ -65,4 +_,34 @@ + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(AGE); + } ++ ++ // Purpur start - Ability for hoe to replant nether warts ++ @Override ++ public void playerDestroy(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ItemStack itemInHand, boolean includeDrops, boolean dropExp) { ++ if (world.purpurConfig.hoeReplantsNetherWarts && itemInHand.getItem() instanceof net.minecraft.world.item.HoeItem) { ++ super.playerDestroyAndReplant(world, player, pos, state, blockEntity, itemInHand, Items.NETHER_WART); ++ } else { ++ super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp); ++ } ++ } ++ // Purpur end - Ability for hoe to replant nether warts ++ ++ // Purpur start - bonemealable netherwart ++ @Override ++ public boolean isValidBonemealTarget(final net.minecraft.world.level.LevelReader world, final BlockPos pos, final BlockState state) { ++ return ((net.minecraft.world.level.Level) world).purpurConfig.netherWartAffectedByBonemeal && state.getValue(NetherWartBlock.AGE) < 3; ++ } ++ ++ @Override ++ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) { ++ return true; ++ } ++ ++ @Override ++ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { ++ int i = Math.min(3, state.getValue(NetherWartBlock.AGE) + 1); ++ state = state.setValue(NetherWartBlock.AGE, i); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, pos, state, 2); // CraftBukkit ++ } ++ // Purpur end - bonemealable netherwart + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch new file mode 100644 index 000000000..42c3d0b22 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/NoteBlock.java ++++ b/net/minecraft/world/level/block/NoteBlock.java +@@ -101,7 +_,7 @@ + } + + private void playNote(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { +- if (state.getValue(INSTRUMENT).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { ++ if (level.purpurConfig.noteBlockIgnoreAbove || state.getValue(INSTRUMENT).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { // Purpur - Config to allow Note Block sounds when blocked + level.blockEvent(pos, this, 0, 0); + level.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch new file mode 100644 index 000000000..73f23d62d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/ObserverBlock.java ++++ b/net/minecraft/world/level/block/ObserverBlock.java +@@ -81,6 +_,7 @@ + RandomSource random + ) { + if (state.getValue(FACING) == direction && !state.getValue(POWERED)) { ++ if (!(level.getWorldBorder().world != null && level.getWorldBorder().world.purpurConfig.disableObserverClocks) || !(neighborState.getBlock() instanceof ObserverBlock) || neighborState.getValue(ObserverBlock.FACING).getOpposite() != direction) // Purpur - Add Option for disable observer clocks + this.startSignal(level, scheduledTickAccess, pos); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch new file mode 100644 index 000000000..6843415d9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/level/block/PointedDripstoneBlock.java ++++ b/net/minecraft/world/level/block/PointedDripstoneBlock.java +@@ -194,20 +_,20 @@ + + @VisibleForTesting + public static void maybeTransferFluid(BlockState state, ServerLevel level, BlockPos pos, float randChance) { +- if (!(randChance > 0.17578125F) || !(randChance > 0.05859375F)) { ++ if (!(randChance > level.purpurConfig.cauldronDripstoneWaterFillChance) || !(randChance > level.purpurConfig.cauldronDripstoneLavaFillChance)) { // Purpur - Cauldron fill chances + if (isStalactiteStartPos(state, level, pos)) { + Optional fluidAboveStalactite = getFluidAboveStalactite(level, pos, state); + if (!fluidAboveStalactite.isEmpty()) { + Fluid fluid = fluidAboveStalactite.get().fluid; + float f; + if (fluid == Fluids.WATER) { +- f = 0.17578125F; ++ f = level.purpurConfig.cauldronDripstoneWaterFillChance; // Purpur - Cauldron fill chances + } else { + if (fluid != Fluids.LAVA) { + return; + } + +- f = 0.05859375F; ++ f = level.purpurConfig.cauldronDripstoneLavaFillChance; // Purpur - Cauldron fill chances + } + + if (!(randChance >= f)) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch new file mode 100644 index 000000000..f6c12cc4c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/PoweredRailBlock.java ++++ b/net/minecraft/world/level/block/PoweredRailBlock.java +@@ -28,7 +_,7 @@ + } + + protected boolean findPoweredRailSignal(Level level, BlockPos pos, BlockState state, boolean searchForward, int recursionCount) { +- if (recursionCount >= 8) { ++ if (recursionCount >= level.purpurConfig.railActivationRange) { // Purpur - Config for powered rail activation distance + return false; + } else { + int x = pos.getX(); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch new file mode 100644 index 000000000..fd70f38b5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/RespawnAnchorBlock.java ++++ b/net/minecraft/world/level/block/RespawnAnchorBlock.java +@@ -166,7 +_,7 @@ + }; + Vec3 center = pos2.getCenter(); + level.explode( +- null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK // CraftBukkit - add state ++ null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), explosionDamageCalculator, center, (float) level.purpurConfig.respawnAnchorExplosionPower, level.purpurConfig.respawnAnchorExplosionFire, level.purpurConfig.respawnAnchorExplosionEffect // CraftBukkit - add state // Purpur - Implement respawn anchor explosion options + ); + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch new file mode 100644 index 000000000..4a529408c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/SculkShriekerBlock.java ++++ b/net/minecraft/world/level/block/SculkShriekerBlock.java +@@ -116,7 +_,7 @@ + + @Override + public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) { +- return this.defaultBlockState().setValue(WATERLOGGED, context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER); ++ return this.defaultBlockState().setValue(WATERLOGGED, context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER).setValue(SculkShriekerBlock.CAN_SUMMON, context.getLevel().purpurConfig.sculkShriekerCanSummonDefault); // Purpur - Config for sculk shrieker can_summon state + } + + @Override diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SlabBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SlabBlock.java.patch new file mode 100644 index 000000000..d26a15acf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SlabBlock.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/level/block/SlabBlock.java ++++ b/net/minecraft/world/level/block/SlabBlock.java +@@ -143,4 +_,25 @@ + return false; + } + } ++ ++ // Purpur start - Break individual slabs when sneaking ++ public boolean halfBreak(BlockState state, BlockPos pos, net.minecraft.server.level.ServerPlayer player) { ++ if (state.getValue(SlabBlock.TYPE) != SlabType.DOUBLE) { ++ return false; ++ } ++ net.minecraft.world.phys.HitResult result = player.getRayTrace(16, net.minecraft.world.level.ClipContext.Fluid.NONE); ++ if (result.getType() != net.minecraft.world.phys.HitResult.Type.BLOCK) { ++ return false; ++ } ++ double hitY = result.getLocation().y(); ++ int blockY = org.bukkit.util.NumberConversions.floor(hitY); ++ player.level().setBlock(pos, state.setValue(SlabBlock.TYPE, (hitY - blockY > 0.5 || blockY - pos.getY() == 1) ? SlabType.BOTTOM : SlabType.TOP), 3); ++ if (!player.getAbilities().instabuild) { ++ net.minecraft.world.entity.item.ItemEntity item = new net.minecraft.world.entity.item.ItemEntity(player.level(), pos.getX(), pos.getY(), pos.getZ(), new ItemStack(asItem())); ++ item.setDefaultPickUpDelay(); ++ player.level().addFreshEntity(item); ++ } ++ return true; ++ } ++ // Purpur end - Break individual slabs when sneaking + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch new file mode 100644 index 000000000..a23c4bf07 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/SnowLayerBlock.java ++++ b/net/minecraft/world/level/block/SnowLayerBlock.java +@@ -76,6 +_,7 @@ + @Override + protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos.below()); ++ if (blockState.is(Blocks.BLUE_ICE) && !(level.getWorldBorder().world == null || level.getWorldBorder().world.purpurConfig.snowOnBlueIce)) return false; // Purpur - Add config for snow on blue ice + return !blockState.is(BlockTags.SNOW_LAYER_CANNOT_SURVIVE_ON) + && ( + blockState.is(BlockTags.SNOW_LAYER_CAN_SURVIVE_ON) diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch new file mode 100644 index 000000000..80b4881ec --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch @@ -0,0 +1,81 @@ +--- a/net/minecraft/world/level/block/SpawnerBlock.java ++++ b/net/minecraft/world/level/block/SpawnerBlock.java +@@ -14,6 +_,7 @@ + import org.jspecify.annotations.Nullable; + + public class SpawnerBlock extends BaseEntityBlock { ++ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); + public static final MapCodec CODEC = simpleCodec(SpawnerBlock::new); + + @Override +@@ -37,6 +_,62 @@ + ); + } + ++ // Purpur start - Silk touch spawners ++ @Override ++ public void playerDestroy(Level level, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) { ++ if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.drop.spawners") && isSilkTouch(level, stack)) { ++ ItemStack item = new ItemStack(Blocks.SPAWNER.asItem()); ++ ++ net.minecraft.world.level.SpawnData nextSpawnData = blockEntity instanceof SpawnerBlockEntity spawnerBlock ? spawnerBlock.getSpawner().nextSpawnData : null; ++ java.util.Optional> type = java.util.Optional.empty(); ++ if (nextSpawnData != null) { ++ try (net.minecraft.util.ProblemReporter.ScopedCollector scopedCollector = new net.minecraft.util.ProblemReporter.ScopedCollector(blockEntity.problemPath(), LOGGER)) { ++ net.minecraft.world.level.storage.ValueInput valueInput = net.minecraft.world.level.storage.TagValueInput.create(scopedCollector, player.level().registryAccess(), nextSpawnData.entityToSpawn()); ++ type = net.minecraft.world.entity.EntityType.by(valueInput); ++ } ++ net.minecraft.nbt.CompoundTag spawnDataTag = new net.minecraft.nbt.CompoundTag(); ++ spawnDataTag.storeNullable("SpawnData", net.minecraft.world.level.SpawnData.CODEC, nextSpawnData); ++ item.set(net.minecraft.core.component.DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY.update(compoundTag -> compoundTag.put("Purpur.SpawnData", spawnDataTag))); ++ } ++ ++ if (type.isPresent()) { ++ final net.kyori.adventure.text.Component mobName = io.papermc.paper.adventure.PaperAdventure.asAdventure(type.get().getDescription()); ++ ++ String name = level.purpurConfig.silkTouchSpawnerName; ++ if (name != null && !name.isEmpty() && !name.equals("Monster Spawner")) { ++ net.kyori.adventure.text.Component displayName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName)); ++ if (name.startsWith("")) { ++ displayName = displayName.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); ++ } ++ item.set(net.minecraft.core.component.DataComponents.CUSTOM_NAME, io.papermc.paper.adventure.PaperAdventure.asVanilla(displayName)); ++ } ++ ++ java.util.List lore = level.purpurConfig.silkTouchSpawnerLore; ++ if (lore != null && !lore.isEmpty()) { ++ ++ java.util.List loreComponentList = new java.util.ArrayList<>(); ++ for (String line : lore) { ++ net.kyori.adventure.text.Component lineComponent = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(line, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("mob", mobName)); ++ if (line.startsWith("")) { ++ lineComponent = lineComponent.decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false); ++ } ++ loreComponentList.add(io.papermc.paper.adventure.PaperAdventure.asVanilla(lineComponent)); ++ } ++ ++ item.set(net.minecraft.core.component.DataComponents.LORE, new net.minecraft.world.item.component.ItemLore(loreComponentList, loreComponentList)); ++ } ++ item.set(net.minecraft.core.component.DataComponents.TOOLTIP_DISPLAY, net.minecraft.world.item.component.TooltipDisplay.DEFAULT.withHidden(net.minecraft.core.component.DataComponents.BLOCK_ENTITY_DATA, true)); ++ } ++ popResource(level, pos, item); ++ } ++ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops, dropExp); ++ } ++ ++ private boolean isSilkTouch(Level level, ItemStack stack) { ++ return stack != null && level.purpurConfig.silkTouchTools.contains(stack.getItem()) && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH, stack) >= level.purpurConfig.minimumSilkTouchSpawnerRequire; ++ } ++ // Purpur end - Silk touch spawners ++ + @Override + protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, stack, dropExperience); +@@ -45,6 +_,7 @@ + + @Override + public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ if (level.purpurConfig.silkTouchEnabled && isSilkTouch(level, stack)) return 0; // Purpur - Silk touch spawners + if (dropExperience) { + int i = 15 + level.random.nextInt(15) + level.random.nextInt(15); + // this.popExperience(level, pos, i); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch new file mode 100644 index 000000000..ab343ffb3 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/world/level/block/SpongeBlock.java ++++ b/net/minecraft/world/level/block/SpongeBlock.java +@@ -53,8 +_,8 @@ + org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(level); // CraftBukkit - Use BlockStateListPopulator + BlockPos.breadthFirstTraversal( + pos, +- 6, +- 65, ++ level.purpurConfig.spongeAbsorptionRadius, // Purpur - Configurable sponge absorption ++ level.purpurConfig.spongeAbsorptionArea, // Purpur - Configurable sponge absorption + (validPos, queueAdder) -> { + for (Direction direction : ALL_DIRECTIONS) { + queueAdder.accept(validPos.relative(direction)); +@@ -68,7 +_,7 @@ + BlockState blockState = blockList.getBlockState(blockPos); + FluidState fluidState = blockList.getFluidState(blockPos); + // CraftBukkit end +- if (!fluidState.is(FluidTags.WATER)) { ++ if (!fluidState.is(FluidTags.WATER) && (!level.purpurConfig.spongeAbsorbsLava || !fluidState.is(FluidTags.LAVA)) && (!level.purpurConfig.spongeAbsorbsWaterFromMud || !blockState.is(Blocks.MUD))) { // Purpur - Option for sponges to work on lava and mud + return BlockPos.TraversalNodeStatus.SKIP; + } else if (blockState.getBlock() instanceof BucketPickup bucketPickup + && !bucketPickup.pickupBlock(null, blockList, blockPos, blockState).isEmpty()) { // CraftBukkit +@@ -76,6 +_,10 @@ + } else { + if (blockState.getBlock() instanceof LiquidBlock) { + blockList.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3); // CraftBukkit ++ // Purpur start - Option for sponges to work on lava and mud ++ } else if (blockState.is(Blocks.MUD)) { ++ blockList.setBlock(blockPos, Blocks.CLAY.defaultBlockState(), 3); ++ // Purpur end - Option for sponges to work on lava and mud + } else { + if (!blockState.is(Blocks.KELP) + && !blockState.is(Blocks.KELP_PLANT) diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch new file mode 100644 index 000000000..7d7bdf207 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/level/block/StonecutterBlock.java ++++ b/net/minecraft/world/level/block/StonecutterBlock.java +@@ -92,4 +_,14 @@ + protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { + return false; + } ++ ++ // Purpur start - Stonecutter damage ++ @Override ++ public void stepOn(Level level, BlockPos pos, BlockState state, net.minecraft.world.entity.Entity entity) { ++ if (level.purpurConfig.stonecutterDamage > 0.0F && entity instanceof net.minecraft.world.entity.LivingEntity) { ++ entity.hurtServer((net.minecraft.server.level.ServerLevel) level, entity.damageSources().stonecutter().eventBlockDamager(level, pos), level.purpurConfig.stonecutterDamage); ++ } ++ super.stepOn(level, pos, state, entity); ++ } ++ // Purpur end - Stonecutter damage + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch new file mode 100644 index 000000000..f388f13ce --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/world/level/block/SugarCaneBlock.java ++++ b/net/minecraft/world/level/block/SugarCaneBlock.java +@@ -19,7 +_,7 @@ + import net.minecraft.world.phys.shapes.CollisionContext; + import net.minecraft.world.phys.shapes.VoxelShape; + +-public class SugarCaneBlock extends Block { ++public class SugarCaneBlock extends Block implements BonemealableBlock { // Purpur - bonemealable sugarcane + public static final MapCodec CODEC = simpleCodec(SugarCaneBlock::new); + public static final IntegerProperty AGE = BlockStateProperties.AGE_15; + private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 16.0); +@@ -112,4 +_,34 @@ + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(AGE); + } ++ ++ // Purpur start - bonemealable sugarcane ++ @Override ++ public boolean isValidBonemealTarget(final LevelReader world, final BlockPos pos, final BlockState state) { ++ if (!((net.minecraft.world.level.Level) world).purpurConfig.sugarCanAffectedByBonemeal || !world.isEmptyBlock(pos.above())) return false; ++ ++ int reedHeight = 0; ++ while (world.getBlockState(pos.below(reedHeight)).is(this)) { ++ reedHeight++; ++ } ++ ++ return reedHeight < ((net.minecraft.world.level.Level) world).paperConfig().maxGrowthHeight.reeds; ++ } ++ ++ @Override ++ public boolean isBonemealSuccess(net.minecraft.world.level.Level world, RandomSource random, BlockPos pos, BlockState state) { ++ return true; ++ } ++ ++ @Override ++ public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { ++ int reedHeight = 0; ++ while (world.getBlockState(pos.below(reedHeight)).is(this)) { ++ reedHeight++; ++ } ++ for (int i = 0; i <= world.paperConfig().maxGrowthHeight.reeds - reedHeight; i++) { ++ world.setBlockAndUpdate(pos.above(i), state.setValue(SugarCaneBlock.AGE, 0)); ++ } ++ } ++ // Purpur end - bonemealable sugarcane + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch new file mode 100644 index 000000000..1c699e149 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch @@ -0,0 +1,48 @@ +--- a/net/minecraft/world/level/block/TurtleEggBlock.java ++++ b/net/minecraft/world/level/block/TurtleEggBlock.java +@@ -156,7 +_,7 @@ + } + + private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) { +- float value = level.environmentAttributes().getValue(EnvironmentAttributes.TURTLE_EGG_HATCH_CHANCE, pos); ++ float value = level.purpurConfig.turtleEggsRandomTickCrackChance == 500 ? level.environmentAttributes().getValue(EnvironmentAttributes.TURTLE_EGG_HATCH_CHANCE, pos) : level.purpurConfig.turtleEggsRandomTickCrackChance; // Purpur - Turtle eggs random tick crack chance + return value > 0.0F && level.random.nextFloat() < value; + } + +@@ -189,9 +_,32 @@ + } + + private boolean canDestroyEgg(ServerLevel level, Entity entity) { +- return !(entity instanceof Turtle) +- && !(entity instanceof Bat) +- && entity instanceof LivingEntity +- && (entity instanceof Player || level.getGameRules().get(GameRules.MOB_GRIEFING)); ++ // Purpur start - Add turtle egg block options ++ if (entity instanceof Turtle || entity instanceof Bat) { ++ return false; ++ } ++ if (level.purpurConfig.turtleEggsBreakFromExpOrbs && entity instanceof net.minecraft.world.entity.ExperienceOrb) { ++ return true; ++ } ++ if (level.purpurConfig.turtleEggsBreakFromItems && entity instanceof net.minecraft.world.entity.item.ItemEntity) { ++ return true; ++ } ++ if (level.purpurConfig.turtleEggsBreakFromMinecarts && entity instanceof net.minecraft.world.entity.vehicle.minecart.AbstractMinecart) { ++ return true; ++ } ++ if (!(entity instanceof LivingEntity)) { ++ return false; ++ } ++ // Purpur start - Option to disable turtle egg trampling with feather falling ++ if (level.purpurConfig.turtleEggsTramplingFeatherFalling) { ++ net.minecraft.world.item.ItemStack bootsItem = ((net.minecraft.world.entity.LivingEntity) entity).getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET); ++ ++ return bootsItem != net.minecraft.world.item.ItemStack.EMPTY || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, bootsItem) < (int) entity.fallDistance; ++ } ++ // Purpur end - Option to disable turtle egg trampling with feather falling ++ if (entity instanceof Player) return true; ++ ++ return level.getGameRules().get(GameRules.MOB_GRIEFING); ++ // Purpur end - Add turtle egg block options + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TwistingVinesBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TwistingVinesBlock.java.patch new file mode 100644 index 000000000..89633d8f7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TwistingVinesBlock.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/block/TwistingVinesBlock.java ++++ b/net/minecraft/world/level/block/TwistingVinesBlock.java +@@ -34,4 +_,11 @@ + protected boolean canGrowInto(BlockState state) { + return NetherVines.isValidGrowthState(state); + } ++ ++ // Purpur start - twisting vines configurable max growth age ++ @Override ++ public int getMaxGrowthAge() { ++ return org.purpurmc.purpur.PurpurConfig.twistingVinesMaxGrowthAge; ++ } ++ // Purpur end - twisting vines configurable max growth age + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch new file mode 100644 index 000000000..af22ad6c3 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/level/block/VegetationBlock.java ++++ b/net/minecraft/world/level/block/VegetationBlock.java +@@ -61,4 +_,24 @@ + protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { + return pathComputationType == PathComputationType.AIR && !this.hasCollision || super.isPathfindable(state, pathComputationType); + } ++ ++ // Purpur start - Ability for hoe to replant crops ++ public void playerDestroyAndReplant(net.minecraft.world.level.Level world, net.minecraft.world.entity.player.Player player, BlockPos pos, BlockState state, @javax.annotation.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, net.minecraft.world.item.ItemStack itemInHand, net.minecraft.world.level.ItemLike itemToReplant) { ++ player.awardStat(net.minecraft.stats.Stats.BLOCK_MINED.get(this)); ++ player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); ++ java.util.List dropList = Block.getDrops(state, (net.minecraft.server.level.ServerLevel) world, pos, blockEntity, player, itemInHand); ++ ++ boolean planted = false; ++ for (net.minecraft.world.item.ItemStack itemToDrop : dropList) { ++ if (!planted && itemToDrop.getItem() == itemToReplant) { ++ world.setBlock(pos, defaultBlockState(), 3); ++ itemToDrop.setCount(itemToDrop.getCount() - 1); ++ planted = true; ++ } ++ Block.popResource(world, pos, itemToDrop); ++ } ++ ++ state.spawnAfterBreak((net.minecraft.server.level.ServerLevel) world, pos, itemInHand, true); ++ } ++ // Purpur end - Ability for hoe to replant crops + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WeepingVinesBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WeepingVinesBlock.java.patch new file mode 100644 index 000000000..be28707f2 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WeepingVinesBlock.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/block/WeepingVinesBlock.java ++++ b/net/minecraft/world/level/block/WeepingVinesBlock.java +@@ -34,4 +_,11 @@ + protected boolean canGrowInto(BlockState state) { + return NetherVines.isValidGrowthState(state); + } ++ ++ // Purpur start - weeping vines configurable max growth age ++ @Override ++ public int getMaxGrowthAge() { ++ return org.purpurmc.purpur.PurpurConfig.weepingVinesMaxGrowthAge; ++ } ++ // Purpur end - weeping vines configurable max growth age + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch new file mode 100644 index 000000000..3958f6bb5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/WitherSkullBlock.java ++++ b/net/minecraft/world/level/block/WitherSkullBlock.java +@@ -69,6 +_,7 @@ + ); + witherBoss.yBodyRot = blockPatternMatch.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F; + witherBoss.makeInvulnerable(); ++ witherBoss.setSummoner(blockState.getBlock().placer == null ? null : blockState.getBlock().placer.getUUID()); // Purpur - Summoner API + // CraftBukkit start + if (!level.addFreshEntity(witherBoss, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BUILD_WITHER)) { + return; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch new file mode 100644 index 000000000..db42baa35 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +@@ -187,6 +_,21 @@ + } + + ItemStack itemStack = furnace.items.get(1); ++ // Purpur start - Furnace uses lava from underneath ++ boolean usedLavaFromUnderneath = false; ++ if (level.purpurConfig.furnaceUseLavaFromUnderneath && !furnace.isLit() && itemStack.isEmpty() && !furnace.items.get(0).isEmpty() && level.getGameTime() % 20 == 0) { ++ BlockPos below = furnace.getBlockPos().below(); ++ BlockState belowState = level.getBlockStateIfLoaded(below); ++ if (belowState != null && belowState.is(Blocks.LAVA)) { ++ net.minecraft.world.level.material.FluidState fluidState = belowState.getFluidState(); ++ if (fluidState != null && fluidState.isSource()) { ++ level.setBlock(below, Blocks.AIR.defaultBlockState(), 3); ++ itemStack = Items.LAVA_BUCKET.getDefaultInstance(); ++ usedLavaFromUnderneath = true; ++ } ++ } ++ } ++ // Purpur end - Furnace uses lava from underneath + ItemStack itemStack1 = furnace.items.get(0); + boolean flag1 = !itemStack1.isEmpty(); + boolean flag2 = !itemStack.isEmpty(); +@@ -270,6 +_,8 @@ + if (flag) { + setChanged(level, pos, state); + } ++ ++ if (usedLavaFromUnderneath) furnace.items.set(1, ItemStack.EMPTY); // Purpur - Furnace uses lava from underneath + } + + private static boolean canBurn( diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch new file mode 100644 index 000000000..c39579e78 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/world/level/block/entity/BeaconBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BeaconBlockEntity.java +@@ -138,6 +_,16 @@ + + public double getEffectRange() { + if (this.effectRange < 0) { ++ // Purpur start - Beacon Activation Range Configurable ++ if (this.level != null) { ++ switch (this.levels) { ++ case 1: return this.level.purpurConfig.beaconLevelOne; ++ case 2: return this.level.purpurConfig.beaconLevelTwo; ++ case 3: return this.level.purpurConfig.beaconLevelThree; ++ case 4: return this.level.purpurConfig.beaconLevelFour; ++ } ++ } ++ // Purpur end - Beacon Activation Range Configurable + return this.levels * 10 + 10; + } else { + return effectRange; +@@ -166,6 +_,7 @@ + int y = pos.getY(); + int z = pos.getZ(); + BlockPos blockPos; ++ boolean isTintedGlass = false; // Purpur - allow beacon effects when covered by tinted glass + if (blockEntity.lastCheckY < y) { + blockPos = pos; + blockEntity.checkingBeamSections = Lists.newArrayList(); +@@ -195,6 +_,7 @@ + } + } + } else { ++ if (level.purpurConfig.beaconAllowEffectsWithTintedGlass && blockState.getBlock().equals(Blocks.TINTED_GLASS)) {isTintedGlass = true;} // Purpur - allow beacon effects when covered by tinted glass + if (section == null || blockState.getLightBlock() >= 15 && !blockState.is(Blocks.BEDROCK)) { + blockEntity.checkingBeamSections.clear(); + blockEntity.lastCheckY = height; +@@ -214,7 +_,7 @@ + blockEntity.levels = updateBase(level, x, y, z); + } + +- if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) { ++ if (blockEntity.levels > 0 && (!blockEntity.beamSections.isEmpty() || (level.purpurConfig.beaconAllowEffectsWithTintedGlass && isTintedGlass))) { // Purpur - allow beacon effects when covered by tinted glass + applyEffects(level, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - Custom beacon ranges + playSound(level, pos, SoundEvents.BEACON_AMBIENT); + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch new file mode 100644 index 000000000..ec70d8701 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch @@ -0,0 +1,56 @@ +--- a/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +@@ -79,7 +_,7 @@ + "leash", + "UUID" + ); +- public static final int MAX_OCCUPANTS = 3; ++ public static final int MAX_OCCUPANTS = org.purpurmc.purpur.PurpurConfig.beeInsideBeeHive; // Purpur - Config to change max number of bees + private static final int MIN_TICKS_BEFORE_REENTERING_HIVE = 400; + private static final int MIN_OCCUPATION_TICKS_NECTAR = 2400; + public static final int MIN_OCCUPATION_TICKS_NECTARLESS = 600; +@@ -153,11 +_,33 @@ + return list; + } + ++ // Purpur start - Stored Bee API ++ public List releaseBee(BlockState iblockdata, BeehiveBlockEntity.BeeData data, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) { ++ List list = Lists.newArrayList(); ++ ++ BeehiveBlockEntity.releaseOccupant(this.level, this.worldPosition, iblockdata, data.occupant, list, tileentitybeehive_releasestatus, this.savedFlowerPos, force); ++ ++ if (!list.isEmpty()) { ++ stored.remove(data); ++ ++ super.setChanged(); ++ } ++ ++ return list; ++ } ++ // Purpur end - Stored Bee API ++ + @VisibleForDebug + public int getOccupantCount() { + return this.stored.size(); + } + ++ // Purpur start - Stored Bee API ++ public List getStored() { ++ return stored; ++ } ++ // Purpur end - Stored Bee API ++ + // Paper start - Add EntityBlockStorage clearEntities + public void clearBees() { + this.stored.clear(); +@@ -398,8 +_,8 @@ + registrar.register(DebugSubscriptions.BEE_HIVES, () -> DebugHiveInfo.pack(this)); + } + +- static class BeeData { +- private final BeehiveBlockEntity.Occupant occupant; ++ public static class BeeData { // Purpur - make public - Stored Bee API ++ public final BeehiveBlockEntity.Occupant occupant; // Purpur - make public - Stored Bee API + private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts + private int ticksInHive; + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch new file mode 100644 index 000000000..a59bbfece --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -105,6 +_,10 @@ + input.read("PublicBukkitValues", CompoundTag.CODEC) + .ifPresent(this.persistentDataContainer::putAll); + // Paper end - read persistent data container ++ ++ ++ this.persistentLore = input.read("Purpur.persistentLore", net.minecraft.world.item.component.ItemLore.CODEC).orElse(null); // Purpur - Persistent BlockEntity Lore and DisplayName ++ + } + + public final void loadWithComponents(ValueInput input) { +@@ -117,6 +_,11 @@ + } + + protected void saveAdditional(ValueOutput output) { ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ if (this.persistentLore != null) { ++ output.store("Purpur.persistentLore", net.minecraft.world.item.component.ItemLore.CODEC, this.persistentLore); ++ } ++ // Purpur end - Persistent BlockEntity Lore and DisplayName + } + + public final CompoundTag saveWithFullMetadata(HolderLookup.Provider registries) { +@@ -402,4 +_,16 @@ + return this.blockEntity.getNameForReporting() + "@" + this.blockEntity.getBlockPos(); + } + } ++ ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ private net.minecraft.world.item.component.@Nullable ItemLore persistentLore = null; ++ ++ public void setPersistentLore(net.minecraft.world.item.component.ItemLore lore) { ++ this.persistentLore = lore; ++ } ++ ++ public @org.jetbrains.annotations.Nullable net.minecraft.world.item.component.ItemLore getPersistentLore() { ++ return this.persistentLore; ++ } ++ // Purpur end - Persistent BlockEntity Lore and DisplayName + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch new file mode 100644 index 000000000..0b72e4dbb --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch @@ -0,0 +1,66 @@ +--- a/net/minecraft/world/level/block/entity/ConduitBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/ConduitBlockEntity.java +@@ -150,7 +_,7 @@ + BlockPos blockPos1 = pos.offset(i, i1, i2x); + BlockState blockState = level.getBlockState(blockPos1); + +- for (Block block : VALID_BLOCKS) { ++ for (Block block : level.purpurConfig.conduitBlocks) { // Purpur - Conduit behavior configuration + if (blockState.is(block)) { + positions.add(blockPos1); + } +@@ -165,13 +_,13 @@ + + private static void applyEffects(Level level, BlockPos pos, List positions) { + // CraftBukkit start +- ConduitBlockEntity.applyEffects(level, pos, ConduitBlockEntity.getRange(positions)); ++ ConduitBlockEntity.applyEffects(level, pos, ConduitBlockEntity.getRange(positions, level)); // Purpur - Conduit behavior configuration + } + +- public static int getRange(List positions) { ++ public static int getRange(List positions, Level level) { // Purpur - Conduit behavior configuration + // CraftBukkit end + int size = positions.size(); +- int i = size / 7 * 16; ++ int i = size / 7 * level.purpurConfig.conduitDistance; // Purpur - Conduit behavior configuration + // CraftBukkit start + return i; + } +@@ -201,7 +_,7 @@ + EntityReference entityReference = updateDestroyTarget(blockEntity.destroyTarget, level, pos, canDestroy); + LivingEntity livingEntity = EntityReference.getLivingEntity(entityReference, level); + if (damageTarget && livingEntity != null) { // CraftBukkit +- if (livingEntity.hurtServer(level, level.damageSources().magic().eventBlockDamager(level, pos), 4.0F)) // CraftBukkit - move up ++ if (livingEntity.hurtServer(level, level.damageSources().magic().eventBlockDamager(level, pos), level.purpurConfig.conduitDamageAmount)) // CraftBukkit - move up // Purpur - Conduit behavior configuration + level.playSound( + null, livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F + ); +@@ -222,19 +_,25 @@ + return selectNewTarget(level, pos); + } else { + LivingEntity livingEntity = EntityReference.getLivingEntity(destroyTarget, level); +- return livingEntity != null && livingEntity.isAlive() && pos.closerThan(livingEntity.blockPosition(), 8.0) ? destroyTarget : null; ++ return livingEntity != null && livingEntity.isAlive() && pos.closerThan(livingEntity.blockPosition(), level.purpurConfig.conduitDamageDistance) ? destroyTarget : null; // Purpur - Conduit behavior configuration + } + } + + private static @Nullable EntityReference selectNewTarget(ServerLevel level, BlockPos pos) { + List entitiesOfClass = level.getEntitiesOfClass( +- LivingEntity.class, getDestroyRangeAABB(pos), livingEntity -> livingEntity instanceof Enemy && livingEntity.isInWaterOrRain() ++ LivingEntity.class, getDestroyRangeAABB(pos, level), livingEntity -> livingEntity instanceof Enemy && livingEntity.isInWaterOrRain() // Purpur - Conduit behavior configuration + ); + return entitiesOfClass.isEmpty() ? null : EntityReference.of(Util.getRandom(entitiesOfClass, level.random)); + } + + public static AABB getDestroyRangeAABB(BlockPos pos) { +- return new AABB(pos).inflate(8.0); ++ // Purpur start - Conduit behavior configuration ++ return getDestroyRangeAABB(pos, null); ++ } ++ ++ private static AABB getDestroyRangeAABB(BlockPos pos, Level level) { ++ // Purpur end - Conduit behavior configuration ++ return new AABB(pos).inflate(level == null ? 8.0 : level.purpurConfig.conduitDamageDistance); // Purpur - Conduit behavior configuration + } + + private static void animationTick(Level level, BlockPos pos, List positions, @Nullable Entity entity, int tickCount) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java.patch new file mode 100644 index 000000000..175e17e0c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java.patch @@ -0,0 +1,48 @@ +--- a/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java +@@ -30,6 +_,7 @@ + public float tRot; + private static final RandomSource RANDOM = RandomSource.create(); + private @Nullable Component name; ++ private int lapis = 0; // Purpur - Enchantment Table Persists Lapis + + public EnchantingTableBlockEntity(BlockPos pos, BlockState blockState) { + super(BlockEntityType.ENCHANTING_TABLE, pos, blockState); +@@ -39,12 +_,14 @@ + protected void saveAdditional(ValueOutput output) { + super.saveAdditional(output); + output.storeNullable("CustomName", ComponentSerialization.CODEC, this.name); ++ output.putInt("Purpur.Lapis", this.lapis); // Purpur - Enchantment Table Persists Lapis + } + + @Override + protected void loadAdditional(ValueInput input) { + super.loadAdditional(input); + this.name = parseCustomNameSafe(input, "CustomName"); ++ this.lapis = input.getIntOr("Purpur.Lapis", 0); // Purpur - Enchantment Table Persists Lapis + } + + public static void bookAnimationTick(Level level, BlockPos pos, BlockState state, EnchantingTableBlockEntity enchantingTable) { +@@ -135,4 +_,22 @@ + public void removeComponentsFromTag(ValueOutput output) { + output.discard("CustomName"); + } ++ ++ // Purpur start - Enchantment Table Persists Lapis ++ public int getLapis() { ++ return this.lapis; ++ } ++ ++ public void setLapis(int lapis) { ++ this.lapis = lapis; ++ } ++ ++ @Override ++ public void preRemoveSideEffects(BlockPos pos, BlockState state) { ++ super.preRemoveSideEffects(pos, state); ++ if (this.level != null && this.level.purpurConfig.enchantmentTableLapisPersists) { ++ net.minecraft.world.Containers.dropItemStack(this.level, pos.getX(), pos.getY(), pos.getZ(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.LAPIS_LAZULI, this.getLapis())); ++ } ++ } ++ // Purpur end - Enchantment Table Persists Lapis + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch new file mode 100644 index 000000000..9a7b2c116 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -0,0 +1,65 @@ +--- a/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -149,16 +_,32 @@ + return this.setText(updater.apply(text), isFrontText); + } + ++ // Purpur start - Signs allow color codes ++ private Component translateColors(org.bukkit.entity.Player player, String line, Style style) { ++ if (level.purpurConfig.signAllowColors) { ++ if (player.hasPermission("purpur.sign.color")) line = line.replaceAll("(?i)&([0-9a-fr])", "\u00a7$1"); ++ if (player.hasPermission("purpur.sign.style")) line = line.replaceAll("(?i)&([l-or])", "\u00a7$1"); ++ if (player.hasPermission("purpur.sign.magic")) line = line.replaceAll("(?i)&([kr])", "\u00a7$1"); ++ ++ return io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(line)); ++ } else { ++ return Component.literal(line).setStyle(style); ++ } ++ } ++ // Purpur end - Signs allow color codes ++ + private SignText setMessages(Player player, List filteredText, SignText text, boolean front) { // CraftBukkit + SignText originalText = text; // CraftBukkit + for (int i = 0; i < filteredText.size(); i++) { + FilteredText filteredText1 = filteredText.get(i); + Style style = text.getMessage(i, player.isTextFilteringEnabled()).getStyle(); ++ ++ org.bukkit.entity.Player craftPlayer = (org.bukkit.craftbukkit.entity.CraftPlayer) player.getBukkitEntity(); // Purpur - Signs allow color codes + if (player.isTextFilteringEnabled()) { +- text = text.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style)); // Paper - filter sign text to chat only ++ text = text.setMessage(i, translateColors(craftPlayer, net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty()), style)); // Paper - filter sign text to chat only // Purpur - Signs allow color codes + } else { + text = text.setMessage( +- i, Component.literal(filteredText1.raw()).setStyle(style), Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style) // Paper - filter sign text to chat only ++ i, translateColors(craftPlayer, net.minecraft.util.StringUtil.filterText(filteredText1.raw()), style), translateColors(craftPlayer, net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty()), style) // Paper - filter sign text to chat only // Purpur - Signs allow color codes + ); + } + } +@@ -307,6 +_,27 @@ + // CraftBukkit - this + return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, string, component, level.getServer(), player); // Paper - Fix commands from signs not firing command events + } ++ ++ // Purpur start - Signs allow color codes ++ public ClientboundBlockEntityDataPacket getTranslatedUpdatePacket(boolean filtered, boolean front) { ++ try (net.minecraft.util.ProblemReporter.ScopedCollector scopedCollector = new net.minecraft.util.ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) { ++ net.minecraft.world.level.storage.TagValueOutput tagValueOutput = net.minecraft.world.level.storage.TagValueOutput.createWithContext(scopedCollector, this.getLevel().registryAccess()); ++ this.saveAdditional(tagValueOutput); ++ ++ final Component[] lines = front ? frontText.getMessages(filtered) : backText.getMessages(filtered); ++ final String side = front ? "front_text" : "back_text"; ++ net.minecraft.world.level.storage.ValueOutput sideNbt = tagValueOutput.child(side); ++ net.minecraft.world.level.storage.ValueOutput.TypedOutputList messagesNbt = sideNbt.list("messages", com.mojang.serialization.Codec.STRING); ++ for (int i = 0; i < 4; i++) { ++ final net.kyori.adventure.text.Component component = io.papermc.paper.adventure.PaperAdventure.asAdventure(lines[i]); ++ final String line = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().serialize(component); ++ messagesNbt.add(line); ++ } ++ tagValueOutput.putString("PurpurEditor", "true"); ++ return ClientboundBlockEntityDataPacket.create(this, (blockEntity, registryAccess) -> tagValueOutput.buildResult()); ++ } ++ } ++ // Purpur end - Signs allow color codes + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/piston/PistonStructureResolver.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/piston/PistonStructureResolver.java.patch new file mode 100644 index 000000000..bc6293606 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/piston/PistonStructureResolver.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/world/level/block/piston/PistonStructureResolver.java ++++ b/net/minecraft/world/level/block/piston/PistonStructureResolver.java +@@ -81,7 +_,7 @@ + return true; + } else { + int i = 1; +- if (i + this.toPush.size() > 12) { ++ if (i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - Configurable piston push limit + return false; + } else { + while (isSticky(blockState)) { +@@ -95,7 +_,7 @@ + break; + } + +- if (++i + this.toPush.size() > 12) { ++ if (++i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - Configurable piston push limit + return false; + } + } +@@ -140,7 +_,7 @@ + return true; + } + +- if (this.toPush.size() >= 12) { ++ if (this.toPush.size() >= this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - Configurable piston push limit + return false; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/EntityStorage.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/EntityStorage.java.patch new file mode 100644 index 000000000..accc2318d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/EntityStorage.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/chunk/storage/EntityStorage.java ++++ b/net/minecraft/world/level/chunk/storage/EntityStorage.java +@@ -108,6 +_,7 @@ + } + // Paper end - Entity load/save limit per chunk + TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector.forChild(entity.problemPath()), entity.registryAccess()); ++ if (!entity.canSaveToDisk()) return; // Purpur - Add canSaveToDisk to Entity + if (entity.save(tagValueOutput)) { + CompoundTag compoundTag1 = tagValueOutput.buildResult(); + listTag.add(compoundTag1); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch new file mode 100644 index 000000000..a49716364 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -309,7 +_,7 @@ + + // Paper start + private static void printOversizedLog(String msg, Path file, int x, int z) { +- org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); ++ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PURPUR - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // Purpur - Rebrand + } + + private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch new file mode 100644 index 000000000..5652d79c6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/level/levelgen/PhantomSpawner.java ++++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java +@@ -38,13 +_,13 @@ + int spawnAttemptMaxSeconds = level.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds; + this.nextTick += (spawnAttemptMinSeconds + randomSource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; + // Paper end - Ability to control player's insomnia and phantoms +- if (level.getSkyDarken() >= 5 || !level.dimensionType().hasSkyLight()) { ++ if (level.getSkyDarken() >= level.purpurConfig.phantomSpawnMinSkyDarkness || !level.dimensionType().hasSkyLight()) { // Purpur - Add phantom spawning options + for (ServerPlayer serverPlayer : level.players()) { + if (!serverPlayer.isSpectator() && (!level.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !serverPlayer.isCreative())) { // Paper - Add phantom creative and insomniac controls + BlockPos blockPos = serverPlayer.blockPosition(); +- if (!level.dimensionType().hasSkyLight() || blockPos.getY() >= level.getSeaLevel() && level.canSeeSky(blockPos)) { ++ if (!level.dimensionType().hasSkyLight() || (!level.purpurConfig.phantomSpawnOnlyAboveSeaLevel || blockPos.getY() >= level.getSeaLevel()) && (!level.purpurConfig.phantomSpawnOnlyWithVisibleSky || level.canSeeSky(blockPos))) { // Purpur - Add phantom spawning options + DifficultyInstance currentDifficultyAt = level.getCurrentDifficultyAt(blockPos); +- if (currentDifficultyAt.isHarderThan(randomSource.nextFloat() * 3.0F)) { ++ if (currentDifficultyAt.isHarderThan(randomSource.nextFloat() * (float) level.purpurConfig.phantomSpawnLocalDifficultyChance)) { // Purpur - Add phantom spawning options + ServerStatsCounter stats = serverPlayer.getStats(); + int i = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); + int i1 = 24000; +@@ -56,7 +_,7 @@ + FluidState fluidState = level.getFluidState(blockPos1); + if (NaturalSpawner.isValidEmptySpawnBlock(level, blockPos1, blockState, fluidState, EntityType.PHANTOM)) { + SpawnGroupData spawnGroupData = null; +- int i2 = 1 + randomSource.nextInt(currentDifficultyAt.getDifficulty().getId() + 1); ++ int i2 = level.purpurConfig.phantomSpawnMinPerAttempt + randomSource.nextInt((level.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? currentDifficultyAt.getDifficulty().getId() : level.purpurConfig.phantomSpawnMaxPerAttempt - level.purpurConfig.phantomSpawnMinPerAttempt) + 1); // Purpur - Add phantom spawning options + + for (int i3 = 0; i3 < i2; i3++) { + // Paper start - PhantomPreSpawnEvent diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch new file mode 100644 index 000000000..f174b46a5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch @@ -0,0 +1,24 @@ +--- a/net/minecraft/world/level/material/FlowingFluid.java ++++ b/net/minecraft/world/level/material/FlowingFluid.java +@@ -233,7 +_,7 @@ + } + } + +- if (i1 >= 2 && this.canConvertToSource(level)) { ++ if (i1 >= this.getRequiredSources(level) && this.canConvertToSource(level)) { // Purpur - Implement infinite liquids + BlockState blockState1 = level.getBlockState(mutableBlockPos.setWithOffset(pos, Direction.DOWN)); + FluidState fluidState1 = blockState1.getFluidState(); + if (blockState1.isSolid() || this.isSourceBlockOfThisType(fluidState1)) { +@@ -320,6 +_,12 @@ + } + + protected abstract boolean canConvertToSource(ServerLevel level); ++ ++ // Purpur start - Implement infinite liquids ++ protected int getRequiredSources(Level level) { ++ return 2; ++ } ++ // Purpur end - Implement infinite liquids + + protected void spreadTo(LevelAccessor level, BlockPos pos, BlockState state, Direction direction, FluidState fluidState) { + if (state.getBlock() instanceof LiquidBlockContainer liquidBlockContainer) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch new file mode 100644 index 000000000..1832422c1 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/world/level/material/LavaFluid.java ++++ b/net/minecraft/world/level/material/LavaFluid.java +@@ -190,7 +_,7 @@ + + @Override + public int getTickDelay(LevelReader level) { +- return isFastLava(level) ? 10 : 30; ++ return level.getWorldBorder().world != null ? (isFastLava(level) ? level.getWorldBorder().world.purpurConfig.lavaSpeedNether : level.getWorldBorder().world.purpurConfig.lavaSpeedNotNether) : (isFastLava(level) ? 10 : 30); // Purpur - Make lava flow speed configurable + } + + @Override +@@ -211,6 +_,13 @@ + private void fizz(LevelAccessor level, BlockPos pos) { + level.levelEvent(LevelEvent.LAVA_FIZZ, pos, 0); + } ++ ++ // Purpur start - Implement infinite liquids ++ @Override ++ protected int getRequiredSources(Level level) { ++ return level.purpurConfig.lavaInfiniteRequiredSources; ++ } ++ // Purpur end - Implement infinite liquids + + @Override + protected boolean canConvertToSource(ServerLevel level) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch new file mode 100644 index 000000000..0a3d7b259 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/level/material/WaterFluid.java ++++ b/net/minecraft/world/level/material/WaterFluid.java +@@ -77,6 +_,13 @@ + return level.getGameRules().get(GameRules.WATER_SOURCE_CONVERSION); + } + ++ // Purpur start - Implement infinite liquids ++ @Override ++ protected int getRequiredSources(Level level) { ++ return level.purpurConfig.waterInfiniteRequiredSources; ++ } ++ // Purpur end - Implement infinite liquids ++ + // Paper start - Add BlockBreakBlockEvent + @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch new file mode 100644 index 000000000..0988ceb36 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +@@ -239,7 +_,7 @@ + if ((node == null || node.costMalus < 0.0F) + && verticalDeltaLimit > 0 + && (cachedPathType != PathType.FENCE || this.canWalkOverFences()) +- && cachedPathType != PathType.UNPASSABLE_RAIL ++ && (this.mob.level().purpurConfig.mobsIgnoreRails || cachedPathType != PathType.UNPASSABLE_RAIL) // Purpur - Config to allow mobs to pathfind over rails + && cachedPathType != PathType.TRAPDOOR + && cachedPathType != PathType.POWDER_SNOW) { + node = this.tryJumpOn(x, y, z, verticalDeltaLimit, nodeFloorLevel, direction, pathType, mutableBlockPos); +@@ -490,7 +_,7 @@ + return PathType.TRAPDOOR; + } else if (blockState.is(Blocks.POWDER_SNOW)) { + return PathType.POWDER_SNOW; +- } else if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH)) { ++ } else if (blockState.is(Blocks.CACTUS) || blockState.is(Blocks.SWEET_BERRY_BUSH) || blockState.is(Blocks.STONECUTTER)) { // Purpur - Stonecutter damage + return PathType.DAMAGE_OTHER; + } else if (blockState.is(Blocks.HONEY_BLOCK)) { + return PathType.STICKY_HONEY; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch new file mode 100644 index 000000000..bb195b4da --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/portal/PortalShape.java ++++ b/net/minecraft/world/level/portal/PortalShape.java +@@ -29,7 +_,7 @@ + public static final int MAX_WIDTH = 21; + private static final int MIN_HEIGHT = 3; + public static final int MAX_HEIGHT = 21; +- private static final BlockBehaviour.StatePredicate FRAME = (state, level, pos) -> state.is(Blocks.OBSIDIAN); ++ private static final BlockBehaviour.StatePredicate FRAME = (state, level, pos) -> state.is(Blocks.OBSIDIAN) || (org.purpurmc.purpur.PurpurConfig.cryingObsidianValidForPortalFrame && state.is(Blocks.CRYING_OBSIDIAN)); // Purpur - Crying obsidian valid for portal frames + private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F; + private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0; + private final Direction.Axis axis; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch new file mode 100644 index 000000000..138758dfe --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java ++++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +@@ -81,6 +_,7 @@ + public final Map decorations = Maps.newLinkedHashMap(); + private final Map frameMarkers = Maps.newHashMap(); + private int trackedDecorationCount; ++ public boolean isExplorerMap; // Purpur - Explorer Map API + + // CraftBukkit start + public final org.bukkit.craftbukkit.map.CraftMapView mapView; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java.patch new file mode 100644 index 000000000..280652166 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java ++++ b/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java +@@ -66,6 +_,11 @@ + Entity entity = context.getOptionalParameter(LootContextParams.ATTACKING_ENTITY); + if (entity instanceof LivingEntity livingEntity) { + int enchantmentLevel = EnchantmentHelper.getEnchantmentLevel(this.enchantment, livingEntity); ++ // Purpur start - Add an option to fix MC-3304 projectile looting ++ if (org.purpurmc.purpur.PurpurConfig.fixProjectileLootingTransfer && context.getOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY) instanceof net.minecraft.world.entity.projectile.arrow.AbstractArrow arrow) { ++ enchantmentLevel = arrow.actualEnchantments.getLevel(this.enchantment); ++ } ++ // Purpur end - Add an option to fix MC-3304 projectile looting + if (enchantmentLevel == 0) { + return stack; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/phys/AABB.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/phys/AABB.java.patch new file mode 100644 index 000000000..23da8c2e0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/phys/AABB.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/phys/AABB.java ++++ b/net/minecraft/world/phys/AABB.java +@@ -474,4 +_,10 @@ + return new AABB(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ); + } + } ++ ++ // Purpur start - Stop squids floating on top of water - tuinity added method ++ public final AABB offsetY(double dy) { ++ return new AABB(this.minX, this.minY + dy, this.minZ, this.maxX, this.maxY + dy, this.maxZ); ++ } ++ // Purpur end - Stop squids floating on top of water + } diff --git a/patches/server/0003-Rebrand.patch b/purpur-server/paper-patches/features/0001-Rebrand.patch similarity index 60% rename from patches/server/0003-Rebrand.patch rename to purpur-server/paper-patches/features/0001-Rebrand.patch index 60e8d3a91..6a8a0f7d5 100644 --- a/patches/server/0003-Rebrand.patch +++ b/purpur-server/paper-patches/features/0001-Rebrand.patch @@ -4,347 +4,6 @@ Date: Sat, 4 May 2019 01:02:11 -0500 Subject: [PATCH] Rebrand -diff --git a/build.gradle.kts b/build.gradle.kts -index f6cd7b910ce41a254e71bf0fcfe93c38abbb1445..139e2b17b899da6f0147bb8b4412e2e54e817be4 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -13,12 +13,12 @@ configurations.named(log4jPlugins.compileClasspathConfigurationName) { - val alsoShade: Configuration by configurations.creating - - dependencies { -- implementation(project(":pufferfish-api")) // Pufferfish // Paper -- // Pufferfish start -- implementation("io.papermc.paper:paper-mojangapi:1.19.2-R0.1-SNAPSHOT") { -+ // Purpur start -+ implementation(project(":purpur-api")) -+ implementation("io.papermc.paper:paper-mojangapi:${project.version}") { - exclude("io.papermc.paper", "paper-api") - } -- // Pufferfish end -+ // Purpur end - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -@@ -56,6 +56,10 @@ dependencies { - runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") - runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") - -+ implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur -+ implementation("org.mozilla:rhino-engine:1.7.14") // Purpur -+ implementation("dev.omega24:upnp4j:1.0") // Purpur -+ - // Pufferfish start - implementation("org.yaml:snakeyaml:1.32") - implementation ("com.github.carleslc.Simple-YAML:Simple-Yaml:1.8.4") { -@@ -91,7 +95,7 @@ tasks.jar { - attributes( - "Main-Class" to "org.bukkit.craftbukkit.Main", - "Implementation-Title" to "CraftBukkit", -- "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish -+ "Implementation-Version" to "git-Purpur-$implementationVersion", // Pufferfish // Purpur - "Implementation-Vendor" to date, // Paper - "Specification-Title" to "Bukkit", - "Specification-Version" to project.version, -@@ -168,7 +172,7 @@ fun TaskContainer.registerRunTask( - name: String, - block: JavaExec.() -> Unit - ): TaskProvider = register(name) { -- group = "paper" -+ group = "paperweight" // Purpur - mainClass.set("org.bukkit.craftbukkit.Main") - standardInput = System.`in` - workingDir = rootProject.layout.projectDirectory -diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -index 9d687da5bdf398bb3f6c84cdf1249a7213d09f2e..462a6eed350fd660ddaf25d567bb6e97b77d0b2b 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -@@ -19,8 +19,10 @@ import java.util.stream.StreamSupport; - - public class PaperVersionFetcher implements VersionFetcher { - private static final java.util.regex.Pattern VER_PATTERN = java.util.regex.Pattern.compile("^([0-9\\.]*)\\-.*R"); // R is an anchor, will always give '-R' at end -- private static final String GITHUB_BRANCH_NAME = "master"; -- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper"; -+ // Purpur start -+ private static final String DOWNLOAD_PAGE = "https://purpurmc.org/downloads"; -+ private static int distance = -2; public int distance() { return distance; } -+ // Purpur end - private static @Nullable String mcVer; - - @Override -@@ -31,11 +33,11 @@ public class PaperVersionFetcher implements VersionFetcher { - @Nonnull - @Override - public Component getVersionMessage(@Nonnull String serverVersion) { -- String[] parts = serverVersion.substring("git-Paper-".length()).split("[-\\s]"); -- final Component updateMessage = getUpdateStatusMessage("PaperMC/Paper", GITHUB_BRANCH_NAME, parts[0]); -+ String[] parts = serverVersion.substring("git-Purpur-".length()).split("[-\\s]"); // Purpur -+ final Component updateMessage = getUpdateStatusMessage("PurpurMC/Purpur", "ver/" + getMinecraftVersion(), parts[0]); // Purpur - final Component history = getHistory(); - -- return history != null ? TextComponent.ofChildren(updateMessage, Component.newline(), history) : updateMessage; -+ return history != null ? Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), history, updateMessage) : updateMessage; // Purpur - } - - private static @Nullable String getMinecraftVersion() { -@@ -45,7 +47,7 @@ public class PaperVersionFetcher implements VersionFetcher { - String result = matcher.group(); - mcVer = result.substring(0, result.length() - 2); // strip 'R' anchor and trailing '-' - } else { -- org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to PaperMC!"); -+ org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to Purpur!"); // Purpur - org.bukkit.Bukkit.getLogger().warning("Pattern: " + VER_PATTERN.toString()); - org.bukkit.Bukkit.getLogger().warning("Version: " + org.bukkit.Bukkit.getBukkitVersion()); - } -@@ -55,7 +57,7 @@ public class PaperVersionFetcher implements VersionFetcher { - } - - private static Component getUpdateStatusMessage(@Nonnull String repo, @Nonnull String branch, @Nonnull String versionInfo) { -- int distance; -+ //int distance; // Purpur - use field - try { - int jenkinsBuild = Integer.parseInt(versionInfo); - distance = fetchDistanceFromSiteApi(jenkinsBuild, getMinecraftVersion()); -@@ -66,13 +68,13 @@ public class PaperVersionFetcher implements VersionFetcher { - - switch (distance) { - case -1: -- return Component.text("Error obtaining version information", NamedTextColor.YELLOW); -+ return Component.text("* Error obtaining version information", NamedTextColor.RED); // Purpur - case 0: -- return Component.text("You are running the latest version", NamedTextColor.GREEN); -+ return Component.text("* You are running the latest version", NamedTextColor.GREEN); // Purpur - case -2: -- return Component.text("Unknown version", NamedTextColor.YELLOW); -+ return Component.text("* Unknown version", NamedTextColor.YELLOW); // Purpur - default: -- return Component.text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW) -+ return Component.text("* You are " + distance + " version(s) behind", NamedTextColor.YELLOW) // Purpur - .append(Component.newline()) - .append(Component.text("Download the new version at: ") - .append(Component.text(DOWNLOAD_PAGE, NamedTextColor.GOLD) -@@ -85,15 +87,11 @@ public class PaperVersionFetcher implements VersionFetcher { - if (siteApiVersion == null) { return -1; } - try { - try (BufferedReader reader = Resources.asCharSource( -- new URL("https://api.papermc.io/v2/projects/paper/versions/" + siteApiVersion), -+ new URL("https://api.purpurmc.org/v2/purpur/" + siteApiVersion), // Purpur - Charsets.UTF_8 - ).openBufferedStream()) { - JsonObject json = new Gson().fromJson(reader, JsonObject.class); -- JsonArray builds = json.getAsJsonArray("builds"); -- int latest = StreamSupport.stream(builds.spliterator(), false) -- .mapToInt(e -> e.getAsInt()) -- .max() -- .getAsInt(); -+ int latest = json.getAsJsonObject("builds").getAsJsonPrimitive("latest").getAsInt(); // Purpur - return latest - jenkinsBuild; - } catch (JsonSyntaxException ex) { - ex.printStackTrace(); -@@ -144,6 +142,6 @@ public class PaperVersionFetcher implements VersionFetcher { - return null; - } - -- return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); -+ return org.bukkit.ChatColor.parseMM("Previous: %s", oldVersion); // Purpur - } - } -diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -index c5d5648f4ca603ef2b1df723b58f9caf4dd3c722..3cb56595822799926a8141e60a42f5d1edfc6de5 100644 ---- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -@@ -17,7 +17,7 @@ public final class PaperConsole extends SimpleTerminalConsole { - @Override - protected LineReader buildReader(LineReaderBuilder builder) { - builder -- .appName("Paper") -+ .appName("Purpur") // Purpur - .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history")) - .completer(new ConsoleCommandCompleter(this.server)) - .option(LineReader.Option.COMPLETE_IN_WORD, true); -diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java -index abe37c7c3c6f5ab73afd738ec78f06d7e4d2ed96..b5b6657e52e4f7a630229bd3ba433438af293e22 100644 ---- a/src/main/java/net/minecraft/CrashReport.java -+++ b/src/main/java/net/minecraft/CrashReport.java -@@ -123,6 +123,10 @@ public class CrashReport { - StringBuilder stringbuilder = new StringBuilder(); - - stringbuilder.append("---- Minecraft Crash Report ----\n"); -+ // Purpur start -+ stringbuilder.append("// "); -+ stringbuilder.append("// DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!"); -+ // Purpur end - stringbuilder.append("// "); - stringbuilder.append(CrashReport.getErrorComment()); - stringbuilder.append("\n\n"); -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 7ca6c81afc99d42d2f39b4b6d7f5e8a18b58fba3..34737c730d724d4be67d480587a17792fd3a11f0 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -923,7 +923,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! -+ return "Purpur"; // Purpur - Purpur > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! - } - - public SystemReport fillSystemReport(SystemReport details) { -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 20dff22409325fb39e1d78282310e828977e571b..a2e1a7f5afbdc9c9c7fea251bf7b50ccf9c32dfc 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -265,7 +265,7 @@ import javax.annotation.Nullable; // Paper - import javax.annotation.Nonnull; // Paper - - public final class CraftServer implements Server { -- private final String serverName = "Pufferfish"; // Paper // Pufferfish -+ private final String serverName = "Purpur"; // Paper // Pufferfish // Purpur - private final String serverVersion; - private final String bukkitVersion = Versioning.getBukkitVersion(); - private final Logger logger = Logger.getLogger("Minecraft"); -diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java -index 4e56018b64d11f76c8da43fd8f85c6de72204e36..9607675e6c5bff2183c4420d11fc63eeb5747fb6 100644 ---- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java -+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java -@@ -21,7 +21,12 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co - - @Override - public void sendMessage(String message) { -- this.sendRawMessage(message); -+ // Purpur start -+ String[] parts = message.split("\n"); -+ for (String part : parts) { -+ this.sendRawMessage(part); -+ } -+ // Purpur end - } - - @Override -@@ -91,7 +96,7 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co - // Paper start - @Override - public void sendMessage(final net.kyori.adventure.identity.Identity identity, final net.kyori.adventure.text.Component message, final net.kyori.adventure.audience.MessageType type) { -- this.sendRawMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); -+ this.sendMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); // Purpur - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index f0ce29d21fe9af803ce4e41b8c037b2ec5d1b124..7bc8b62ff6aa355f3f025fc6c3a3d4c6b355853b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -907,7 +907,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - return EntityCategory.WATER; - } - -- throw new UnsupportedOperationException("Unsupported monster type: " + type + ". This is a bug, report this to Spigot."); -+ throw new UnsupportedOperationException("Unsupported monster type: " + type + ". This is a bug, report this to Purpur."); // Purpur - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index d7ce4971d9271dbeff4adb9d852e4e7bdf60bf03..5a47a8785bc2e251d041f80a79295c43459de3bc 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -501,7 +501,7 @@ public class CraftScheduler implements BukkitScheduler { - this.parsePending(); - } else { - // this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper -- task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper -+ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Purpur"); // Paper // Purpur - // We don't need to parse pending - // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) - } -diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 2b4581f92543c11f31bcc1417e90d7f90b2aea20..ffca5970a6259b024c9aa935e22cf72ed8cd8e9f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -470,7 +470,7 @@ public final class CraftMagicNumbers implements UnsafeValues { - - @Override - public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { -- return new gg.pufferfish.pufferfish.PufferfishVersionFetcher(); // Pufferfish -+ return new com.destroystokyo.paper.PaperVersionFetcher(); // Pufferfish // Purpur - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 80553face9c70c2a3d897681e7761df85b22d464..99597258e8e88cd9e2c901c4ac3ff7faeeabee2b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -+++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -@@ -11,7 +11,7 @@ public final class Versioning { - public static String getBukkitVersion() { - String result = "Unknown-Version"; - -- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish -+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur - Properties properties = new Properties(); - - if (stream != null) { -diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index 98fba0288be9ed2cb18ffba5cf81148157dd4fcf..0801dea155048ac5383295f4fef9bd597b678535 100644 ---- a/src/main/java/org/spigotmc/WatchdogThread.java -+++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -96,7 +96,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - - private WatchdogThread(long timeoutTime, boolean restart) - { -- super( "Paper Watchdog Thread" ); -+ super( "Watchdog Thread" ); // Purpur - use a generic name - this.timeoutTime = timeoutTime; - this.restart = restart; - earlyWarningEvery = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper -@@ -155,14 +155,14 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - if (isLongTimeout) { - // Paper end - log.log( Level.SEVERE, "------------------------------" ); -- log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper -+ log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug." ); // Paper // Purpur - log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" ); - log.log( Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring" ); - log.log( Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once" ); - log.log( Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes" ); -- log.log( Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues" ); -+ log.log( Level.SEVERE, "If you are unsure or still think this is a Purpur bug, please report this to https://github.com/PurpurMC/Purpur/issues" ); // Purpur - log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); -- log.log( Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion() ); -+ log.log( Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion() ); // Purpur - // - if ( net.minecraft.world.level.Level.lastPhysicsProblem != null ) - { -@@ -185,12 +185,12 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - // Paper end - } else - { -- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); -+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur - log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump"); - } - // Paper end - Different message for short timeout - log.log( Level.SEVERE, "------------------------------" ); -- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper -+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):" ); // Paper // Purpur - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system - this.dumpTickingInfo(); // Paper - log detailed tick information - WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); -@@ -206,7 +206,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa - WatchdogThread.dumpThread( thread, log ); - } - } else { -- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); -+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur - } - - log.log( Level.SEVERE, "------------------------------" ); diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png index 8b924977b7886df9ab8790b1e4ff9b1c04a2af45..518591dd83289e041a16e2c2e7d7e7640d4b2e1b 100644 GIT binary patch diff --git a/purpur-server/paper-patches/features/0002-Ridables.patch b/purpur-server/paper-patches/features/0002-Ridables.patch new file mode 100644 index 000000000..79c94bb84 --- /dev/null +++ b/purpur-server/paper-patches/features/0002-Ridables.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sun, 5 Jul 2020 22:19:49 -0500 +Subject: [PATCH] Ridables + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 2f940ec42f4d657e40f4da1d974f46c87837a48c..da588e4622aac635ecc2205ea09ceb9722cd59c9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1343,4 +1343,26 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return this.entity.get(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type)) != null; + } + ++ // Purpur start - Ridables ++ @Override ++ public org.bukkit.entity.Player getRider() { ++ net.minecraft.world.entity.player.Player rider = getHandle().getRider(); ++ return rider != null ? (org.bukkit.entity.Player) rider.getBukkitEntity() : null; ++ } ++ ++ @Override ++ public boolean hasRider() { ++ return getHandle().getRider() != null; ++ } ++ ++ @Override ++ public boolean isRidable() { ++ return getHandle().isRidable(); ++ } ++ ++ @Override ++ public boolean isRidableInWater() { ++ return !getHandle().dismountsUnderwater(); ++ } ++ // Purpur end - Ridables + } +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 0cc84ce2074734a910599cdc66d7ddad3b081a76..690181c032667326c2e9c7edf678b2cdcd319c64 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -653,6 +653,15 @@ public class CraftEventFactory { + } + craftServer.getPluginManager().callEvent(event); + ++ // Purpur start - Ridables ++ if (player != null) { ++ switch (action) { ++ case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> player.processClick(InteractionHand.MAIN_HAND); ++ case RIGHT_CLICK_BLOCK, RIGHT_CLICK_AIR -> player.processClick(InteractionHand.OFF_HAND); ++ } ++ } ++ // Purpur end - Ridables ++ + return event; + } + +@@ -1174,6 +1183,7 @@ public class CraftEventFactory { + EntityDamageEvent event; + if (damager != null) { + event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions, critical); ++ damager.processClick(InteractionHand.MAIN_HAND); // Purpur - Ridables + } else { + event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions); + } diff --git a/purpur-server/paper-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/paper-patches/features/0003-Barrels-and-enderchests-6-rows.patch new file mode 100644 index 000000000..c3f53e760 --- /dev/null +++ b/purpur-server/paper-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 23 May 2019 21:50:37 -0500 +Subject: [PATCH] Barrels and enderchests 6 rows + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +index 2f41a92465b9da28e026297cc3528898bb1c8412..83ae4b40e33e0dc58e0fd6aacf22e22ae0cd3684 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +@@ -148,8 +148,19 @@ public class CraftContainer extends AbstractContainerMenu { + case PLAYER: + case CHEST: + case ENDER_CHEST: ++ // Purpur start - Barrels and enderchests 6 rows ++ this.delegate = new ChestMenu(org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? net.minecraft.world.inventory.MenuType.GENERIC_9x6 : net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); ++ break; + case BARREL: +- this.delegate = new ChestMenu(net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9); ++ this.delegate = new ChestMenu(switch (org.purpurmc.purpur.PurpurConfig.barrelRows) { ++ case 6 -> net.minecraft.world.inventory.MenuType.GENERIC_9x6; ++ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5; ++ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4; ++ case 2 -> net.minecraft.world.inventory.MenuType.GENERIC_9x2; ++ case 1 -> net.minecraft.world.inventory.MenuType.GENERIC_9x1; ++ default -> net.minecraft.world.inventory.MenuType.GENERIC_9x3; ++ }, windowId, bottom, top, top.getContainerSize() / 9); ++ // Purpur end - Barrels and enderchests 6 rows + break; + case DISPENSER: + case DROPPER: +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +index 9d5fe6fc727948955d89f9ed5e181185393a9611..5e5a4e48b2a96c355256b8f8bfcc09225d562944 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +@@ -85,7 +85,7 @@ public class CraftInventory implements Inventory { + + @Override + public void setContents(ItemStack[] items) { +- Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); ++ // Preconditions.checkArgument(items.length <= this.getSize(), "Invalid inventory size (%s); expected %s or less", items.length, this.getSize()); // Purpur - Barrels and enderchests 6 rows + + for (int i = 0; i < this.getSize(); i++) { + if (i >= items.length) { diff --git a/purpur-server/paper-patches/features/0004-API-for-any-mob-to-burn-daylight.patch b/purpur-server/paper-patches/features/0004-API-for-any-mob-to-burn-daylight.patch new file mode 100644 index 000000000..41d99e777 --- /dev/null +++ b/purpur-server/paper-patches/features/0004-API-for-any-mob-to-burn-daylight.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ben Kerllenevich +Date: Tue, 25 May 2021 16:31:09 -0400 +Subject: [PATCH] API for any mob to burn daylight + +Co-authored by: Encode42 + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index da588e4622aac635ecc2205ea09ceb9722cd59c9..d04ff11b293c458fd4f18f04336421e5e183c3f4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -126,6 +126,13 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + // Purpur end - Fire Immunity API + ++ // Purpur start - API for any mob to burn daylight ++ @Override ++ public boolean isInDaylight() { ++ return getHandle().isSunBurnTick(); ++ } ++ // Purpur end - API for any mob to burn daylight ++ + public static CraftEntity getEntity(CraftServer server, T entity) { + Preconditions.checkArgument(entity != null, "Unknown entity"); + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index d9129420acaf992c36e171d1b6d399a4b322fea6..d64c10a86a63dac5ce9babcd826e2ecc83b08423 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -1088,4 +1088,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + public CombatTracker getCombatTracker() { + return this.getHandle().getCombatTracker().paperCombatTracker; + } ++ ++ // Purpur start - API for any mob to burn daylight ++ @Override ++ public boolean shouldBurnInDay() { ++ return this.getHandle().shouldBurnInDay(); ++ } ++ ++ @Override ++ public void setShouldBurnInDay(final boolean shouldBurnInDay) { ++ this.getHandle().setShouldBurnInDay(shouldBurnInDay); ++ } ++ // Purpur end - API for any mob to burn daylight + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +index cabdcbef0e6f7ca41ff5677bc2e6a81665ca812d..c47ca9ab164b6abd28e979c94456918162e49214 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +@@ -35,12 +35,12 @@ public class CraftPhantom extends CraftMob implements Phantom, CraftEnemy { + + @Override + public boolean shouldBurnInDay() { +- return this.getHandle().shouldBurnInDay; ++ return this.getHandle().shouldBurnInDay(); // Purpur - API for any mob to burn daylight + } + + @Override + public void setShouldBurnInDay(boolean shouldBurnInDay) { +- this.getHandle().shouldBurnInDay = shouldBurnInDay; ++ this.getHandle().setShouldBurnInDay(shouldBurnInDay); // Purpur - API for any mob to burn daylight + } + + @Override diff --git a/purpur-server/paper-patches/features/0005-Setting-to-reintroduce-end-void-rings.patch b/purpur-server/paper-patches/features/0005-Setting-to-reintroduce-end-void-rings.patch new file mode 100644 index 000000000..b7cc77220 --- /dev/null +++ b/purpur-server/paper-patches/features/0005-Setting-to-reintroduce-end-void-rings.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: granny +Date: Mon, 8 Dec 2025 15:44:50 -0800 +Subject: [PATCH] Setting to reintroduce end void rings + + +diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +index c52d2530f6bfd601e8af8fd6713e6870cf5611d3..c95bca1a3100952fb02a7bff46fcf9155b185a80 100644 +--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +@@ -300,6 +300,7 @@ public class GlobalConfiguration extends ConfigurationPart { + } + } + ++ @Setting(org.purpurmc.purpur.configuration.transformation.FarEndTerrainGenerationMigration.MISC_KEY) // Purpur - Migrate Setting to reintroduce end void rings + public Misc misc; + + public class Misc extends ConfigurationPart { +@@ -346,6 +347,7 @@ public class GlobalConfiguration extends ConfigurationPart { + @Comment("Whether the nether dimension is enabled and will be loaded.") + public boolean enableNether = true; + @Comment("Keeps Paper's fix for MC-159283 enabled. Disable to use vanilla End ring terrain.") ++ @Setting(org.purpurmc.purpur.configuration.transformation.FarEndTerrainGenerationMigration.FIX_FAR_END_TERRAIN_GENERATION_KEY) // Purpur - Migrate Setting to reintroduce end void rings + public boolean fixFarEndTerrainGeneration = true; + } + +diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +index e0491ef5cfc06b5c23d448b8c9679a17e58bca26..0d3d9bd2f6aa1284dc237b5169372a2431c85fb8 100644 +--- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java ++++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +@@ -291,6 +291,7 @@ public class PaperConfigurations extends Configurations +Date: Wed, 17 Dec 2025 20:20:00 -0800 +Subject: [PATCH] lightning transforms blocks + + +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 690181c032667326c2e9c7edf678b2cdcd319c64..3d7a26bed64c53be69eb19d14c1991dee224da46 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1962,7 +1962,13 @@ public class CraftEventFactory { + } + + public static LightningStrikeEvent callLightningStrikeEvent(LightningStrike entity, LightningStrikeEvent.Cause cause) { +- LightningStrikeEvent event = new LightningStrikeEvent(entity.getWorld(), entity, cause); ++ // Purpur start - lightning transforms blocks ++ List blocks = ((org.bukkit.craftbukkit.entity.CraftLightningStrike) entity).getHandle().blocks; ++ LightningStrikeEvent event = new LightningStrikeEvent(entity.getWorld(), entity, cause, (List) (List) blocks); ++ if (!event.getBlocks().equals(blocks)) { ++ ((org.bukkit.craftbukkit.entity.CraftLightningStrike) entity).getHandle().blocks = (List) (List) event.getBlocks(); ++ } ++ // Purpur end - lightning transforms blocks + Bukkit.getPluginManager().callEvent(event); + return event; + } diff --git a/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch new file mode 100644 index 000000000..4d41a9d45 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/Metrics.java.patch @@ -0,0 +1,30 @@ +--- a/src/main/java/com/destroystokyo/paper/Metrics.java ++++ b/src/main/java/com/destroystokyo/paper/Metrics.java +@@ -593,7 +_,7 @@ + boolean logFailedRequests = config.getBoolean("logFailedRequests", false); + // Only start Metrics, if it's enabled in the config + if (config.getBoolean("enabled", true)) { +- Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); ++ Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Purpur - Purpur config files + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); +@@ -602,16 +_,8 @@ + })); + + metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); +- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); +- final String paperVersion; +- final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion(); +- if (implVersion != null) { +- final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1); +- paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash); +- } else { +- paperVersion = "unknown"; +- } +- metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion)); ++ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur - Purpur config files ++ metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur - Purpur config files + + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); diff --git a/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java.patch b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java.patch new file mode 100644 index 000000000..8440b425a --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java.patch @@ -0,0 +1,78 @@ +--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java ++++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +@@ -35,7 +_,10 @@ + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final int DISTANCE_ERROR = -1; + private static final int DISTANCE_UNKNOWN = -2; +- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper"; ++ // Purpur start - Rebrand ++ private static final String DOWNLOAD_PAGE = "https://purpurmc.org/downloads"; ++ private static int distance = DISTANCE_UNKNOWN; public int distance() { return distance; } ++ // Purpur end - Rebrand + + @Override + public long getCacheTime() { +@@ -49,7 +_,7 @@ + if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) { + updateMessage = text("You are running a development version without access to version information", color(0xFF5300)); + } else { +- updateMessage = getUpdateStatusMessage("PaperMC/Paper", build); ++ updateMessage = getUpdateStatusMessage("PurpurMC/Purpur", build); // Purpur - Rebrand + } + final @Nullable Component history = this.getHistory(); + +@@ -57,7 +_,7 @@ + } + + private static Component getUpdateStatusMessage(final String repo, final ServerBuildInfo build) { +- int distance = DISTANCE_ERROR; ++ //int distance = DISTANCE_ERROR; // Purpur - use field - Rebrand + + final OptionalInt buildNumber = build.buildNumber(); + if (buildNumber.isPresent()) { +@@ -71,10 +_,10 @@ + } + + return switch (distance) { +- case DISTANCE_ERROR -> text("Error obtaining version information", NamedTextColor.YELLOW); +- case 0 -> text("You are running the latest version", NamedTextColor.GREEN); +- case DISTANCE_UNKNOWN -> text("Unknown version", NamedTextColor.YELLOW); +- default -> text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW) ++ case DISTANCE_ERROR -> text("* Error obtaining version information", NamedTextColor.RED); // Purpur - Rebrand ++ case 0 -> text("* You are running the latest version", NamedTextColor.GREEN); // Purpur - Rebrand ++ case DISTANCE_UNKNOWN -> text("* Unknown version", NamedTextColor.YELLOW); // Purpur - Rebrand ++ default -> text("* You are " + distance + " version(s) behind", NamedTextColor.YELLOW) // Purpur - Rebrand + .append(Component.newline()) + .append(text("Download the new version at: ") + .append(text(DOWNLOAD_PAGE, NamedTextColor.GOLD) +@@ -86,18 +_,15 @@ + private static int fetchDistanceFromSiteApi(final ServerBuildInfo build, final int jenkinsBuild) { + try { + try (final BufferedReader reader = Resources.asCharSource( +- URI.create("https://api.papermc.io/v2/projects/paper/versions/" + build.minecraftVersionId()).toURL(), ++ URI.create("https://api.purpurmc.org/v2/purpur/" + build.minecraftVersionId()).toURL(), // Purpur - Rebrand + StandardCharsets.UTF_8 + ).openBufferedStream()) { + final JsonObject json = new Gson().fromJson(reader, JsonObject.class); +- final JsonArray builds = json.getAsJsonArray("builds"); +- final int latest = StreamSupport.stream(builds.spliterator(), false) +- .mapToInt(JsonElement::getAsInt) +- .max() +- .orElseThrow(); ++ //final JsonArray builds = json.getAsJsonArray("builds"); // Purpur - Rebrand ++ final int latest = json.getAsJsonObject("builds").getAsJsonPrimitive("latest").getAsInt(); // Purpur - Rebrand + return latest - jenkinsBuild; + } catch (final JsonSyntaxException ex) { +- LOGGER.error("Error parsing json from Paper's downloads API", ex); ++ LOGGER.error("Error parsing json from Purpur's downloads API", ex); // Purpur - Rebrand + return DISTANCE_ERROR; + } + } catch (final IOException e) { +@@ -141,6 +_,6 @@ + return null; + } + +- return text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); ++ return text("Previous: " + oldVersion, NamedTextColor.GRAY); // Purpur - Rebrand + } + } diff --git a/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/console/PaperConsole.java.patch b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/console/PaperConsole.java.patch new file mode 100644 index 000000000..43b5c7a41 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/console/PaperConsole.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java ++++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +@@ -20,7 +_,7 @@ + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + builder +- .appName("Paper") ++ .appName("Purpur") // Purpur - Rebrand + .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history")) + .completer(new ConsoleCommandCompleter(this.server)) + .option(LineReader.Option.COMPLETE_IN_WORD, true); diff --git a/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java.patch b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java.patch new file mode 100644 index 000000000..47789fe9b --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java.patch @@ -0,0 +1,14 @@ +--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java ++++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +@@ -148,6 +_,11 @@ + private static final Map RENAMES = Util.make(new HashMap<>(), map -> { + map.put("AbstractSkeleton$1", "AbstractSkeletonMelee"); + ++ // Purpur start - Add option to disable zombie aggressiveness towards villagers ++ map.put("Zombie$1", "ZombieAttackVillager"); ++ map.put("Drowned$1", "DrownedAttackVillager"); ++ // Purpur end - Add option to disable zombie aggressiveness towards villagers ++ + // remove duplicate + map.put("TraderLlama$TraderLlamaDefendWanderingTraderGoal", "TraderLlamaDefendWanderingTraderGoal"); + map.put("AbstractIllager$RaiderOpenDoorGoal", "RaiderOpenDoorGoal"); diff --git a/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java.patch b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java.patch new file mode 100644 index 000000000..795d9b9f2 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java ++++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java +@@ -60,7 +_,7 @@ + Vector vector = new Vector<>(); + + // Follows CraftServer#getTPS +- double[] tps = server.getTPS(); ++ double[] tps = server.getTPS(); // Purpur - diff on change + String[] tpsAvg = new String[tps.length]; + + for ( int g = 0; g < tps.length; g++) { +@@ -69,7 +_,7 @@ + vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)"); + vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb"); + vector.add("Avg tick: " + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / (double) TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms"); +- vector.add("TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg)); ++ vector.add("TPS from last 5s, 1m, 5m, 15m: " + String.join(", ", tpsAvg)); // Purpur - Add 5 second tps average in /tps + setListData(vector); + } + diff --git a/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java.patch b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java.patch new file mode 100644 index 000000000..13971e34d --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java.patch @@ -0,0 +1,31 @@ +--- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +@@ -31,6 +_,7 @@ + private static final String ATTRIBUTE_GIT_COMMIT = "Git-Commit"; + + private static final String BRAND_PAPER_NAME = "Paper"; ++ private static final String BRAND_PURPUR_NAME = "Purpur"; // Purpur - Rebrand + + private static final String BUILD_DEV = "DEV"; + +@@ -42,9 +_,9 @@ + this( + getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) + .map(Key::key) +- .orElse(BRAND_PAPER_ID), ++ .orElse(BRAND_PURPUR_ID), // Purpur - Rebrand + getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) +- .orElse(BRAND_PAPER_NAME), ++ .orElse(BRAND_PURPUR_NAME), // Purpur - Rebrand + SharedConstants.getCurrentVersion().id(), + SharedConstants.getCurrentVersion().name(), + getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) +@@ -61,7 +_,7 @@ + + @Override + public boolean isBrandCompatible(final @NotNull Key brandId) { +- return brandId.equals(this.brandId); ++ return brandId.equals(this.brandId) || brandId.equals(BRAND_PAPER_ID); // Purpur - Fix pufferfish issues // Purpur - Rebrand + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch new file mode 100644 index 000000000..2b8d161ef --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch @@ -0,0 +1,125 @@ +--- a/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java ++++ b/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java +@@ -74,10 +_,10 @@ + .build(); + } + +- private static List formatProviders(final TreeMap> plugins) { ++ private static List formatProviders(final TreeMap> plugins, final CommandSender sender) { // Purpur - Improve output of plugins command + final List components = new ArrayList<>(plugins.size()); + for (final PluginProvider entry : plugins.values()) { +- components.add(formatProvider(entry)); ++ components.add(formatProvider(entry, sender)); // Purpur - Improve output of plugins command + } + + boolean isFirst = true; +@@ -104,15 +_,49 @@ + return formattedSubLists; + } + +- private static Component formatProvider(final PluginProvider provider) { ++ private static Component formatProvider(final PluginProvider provider, CommandSender sender) { // Purpur - Improve output of plugins command + final TextComponent.Builder builder = Component.text(); + if (provider instanceof final SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) { + builder.append(LEGACY_PLUGIN_STAR); + } + + final String name = provider.getMeta().getName(); +- final Component pluginName = Component.text(name, fromStatus(provider)) +- .clickEvent(ClickEvent.runCommand("/version " + name)); ++ // Purpur start - Improve output of plugins command ++ Component pluginName = Component.text(name, fromStatus(provider)) ++ .clickEvent(ClickEvent.suggestCommand("/version " + name)); ++ ++ if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) { ++ // Event components ++ String description = provider.getMeta().getDescription(); ++ TextComponent.Builder hover = Component.text(); ++ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(provider.getMeta().getVersion(), NamedTextColor.GREEN)); ++ ++ if (description != null) { ++ hover.append(Component.newline()) ++ .append(Component.text("Description: ", NamedTextColor.WHITE)) ++ .append(Component.text(description, NamedTextColor.GREEN)); ++ } ++ ++ if (provider.getMeta().getWebsite() != null) { ++ hover.append(Component.newline()) ++ .append(Component.text("Website: ", NamedTextColor.WHITE)) ++ .append(Component.text(provider.getMeta().getWebsite(), NamedTextColor.GREEN)); ++ } ++ ++ if (!provider.getMeta().getAuthors().isEmpty()) { ++ hover.append(Component.newline()); ++ if (provider.getMeta().getAuthors().size() == 1) { ++ hover.append(Component.text("Author: ")); ++ } else { ++ hover.append(Component.text("Authors: ")); ++ } ++ ++ hover.append(getAuthors(provider.getMeta())); ++ } ++ ++ pluginName = pluginName.hoverEvent(hover.build()); ++ } ++ // Purpur end - Improve output of plugins command + + builder.append(pluginName); + +@@ -130,6 +_,23 @@ + return componentHeader.append(Component.text(":")).build(); + } + ++ // Purpur start - Improve output of plugins command ++ private static TextComponent getAuthors(final PluginMeta pluginMeta) { ++ TextComponent.Builder builder = Component.text(); ++ List authors = pluginMeta.getAuthors(); ++ ++ for (int i = 0; i < authors.size(); i++) { ++ if (i > 0) { ++ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE)); ++ } ++ ++ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN)); ++ } ++ ++ return builder.build(); ++ } ++ // Purpur end - Improve output of plugins command ++ + private static Component asPlainComponents(final String strings) { + final net.kyori.adventure.text.TextComponent.Builder builder = Component.text(); + for (final String string : strings.split("\n")) { +@@ -188,25 +_,25 @@ + final int sizePaperPlugins = paperPlugins.size(); + final int sizeSpigotPlugins = spigotPlugins.size(); + final int sizePlugins = sizePaperPlugins + sizeSpigotPlugins; +- final boolean hasAllPluginTypes = (sizePaperPlugins > 0 && sizeSpigotPlugins > 0); ++ final boolean hasAllPluginTypes = true; // Purpur - Improve output of plugins command + + final Component infoMessage = Component.text().append(INFO_ICON_SERVER_PLUGIN).append(Component.text("Server Plugins (%s):".formatted(sizePlugins), NamedTextColor.WHITE)).build(); + + sender.sendMessage(infoMessage); + +- if (!paperPlugins.isEmpty()) { ++ //if (!paperPlugins.isEmpty()) { // Purpur - Improve output of plugins command + sender.sendMessage(header("Paper Plugins", 0x0288D1, sizePaperPlugins, hasAllPluginTypes)); +- } ++ //} // Purpur - Improve output of plugins command + +- for (final Component component : formatProviders(paperPlugins)) { ++ for (final Component component : formatProviders(paperPlugins, sender)) { // Purpur - Improve output of plugins command + sender.sendMessage(component); + } + +- if (!spigotPlugins.isEmpty()) { ++ //if (!spigotPlugins.isEmpty()) { // Purpur - Improve output of plugins command + sender.sendMessage(header("Bukkit Plugins", 0xED8106, sizeSpigotPlugins, hasAllPluginTypes)); +- } ++ //} // Purpur - Improve output of plugins command + +- for (final Component component : formatProviders(spigotPlugins)) { ++ for (final Component component : formatProviders(spigotPlugins, sender)) { // Purpur - Improve output of plugins command + sender.sendMessage(component); + } + diff --git a/patches/server/0188-Enhance-SysoutCatcher.patch b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/logging/SysoutCatcher.java.patch similarity index 55% rename from patches/server/0188-Enhance-SysoutCatcher.patch rename to purpur-server/paper-patches/files/src/main/java/io/papermc/paper/logging/SysoutCatcher.java.patch index 2a1703e67..d8d9954f6 100644 --- a/patches/server/0188-Enhance-SysoutCatcher.patch +++ b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/logging/SysoutCatcher.java.patch @@ -1,22 +1,14 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Mon, 28 Jun 2021 13:33:12 -0500 -Subject: [PATCH] Enhance SysoutCatcher - - -diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java -index a8e813ca89b033f061e695288b3383bdcf128531..1ab65af9359d19530bba7f985a604d2a430ee234 100644 --- a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java +++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java -@@ -54,9 +54,9 @@ public final class SysoutCatcher { +@@ -54,9 +_,9 @@ final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(clazz); // Instead of just printing the message, send it to the plugin's logger - plugin.getLogger().log(this.level, this.prefix + line); -+ plugin.getLogger().log(this.level, /*this.prefix +*/ line); // Purpur - prefix not needed ++ plugin.getLogger().log(this.level, /*this.prefix +*/ line); // Purpur - Enhance SysoutCatcher - prefix not needed - if (SysoutCatcher.SUPPRESS_NAGS) { -+ if (true || SysoutCatcher.SUPPRESS_NAGS) { // Purpur - nagging is annoying ++ if (true || SysoutCatcher.SUPPRESS_NAGS) { // Purpur - Enhance SysoutCatcher - nagging is annoying return; } if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) { diff --git a/patches/server/0210-Extended-OfflinePlayer-API.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch similarity index 59% rename from patches/server/0210-Extended-OfflinePlayer-API.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch index 97ca4ef26..c9e217b4e 100644 --- a/patches/server/0210-Extended-OfflinePlayer-API.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch @@ -1,14 +1,19 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: YouHaveTrouble -Date: Sun, 22 Aug 2021 05:12:05 +0200 -Subject: [PATCH] Extended OfflinePlayer API - - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf8d0defe2 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -@@ -567,4 +567,213 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa +@@ -342,6 +_,12 @@ + + @Override + public Location getLocation() { ++ // Purpur start - OfflinePlayer API ++ if (this.isOnline()) { ++ return this.getPlayer().getLocation(); ++ } ++ // Purpur end - OfflinePlayer API ++ + CompoundTag data = this.getData(); + if (data == null) { + return null; +@@ -579,4 +_,183 @@ manager.save(); } } @@ -21,9 +26,8 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return false; -+ if (!data.contains("abilities")) return false; -+ CompoundTag abilities = data.getCompound("abilities"); -+ return abilities.getByte("mayfly") == (byte) 1; ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return false; ++ return abilities.getByteOr("mayfly", (byte) 0) == (byte) 1; + } + } + @@ -34,8 +38,7 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return; -+ if (!data.contains("abilities")) return; -+ CompoundTag abilities = data.getCompound("abilities"); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return; + abilities.putByte("mayfly", (byte) (flight ? 1 : 0)); + data.put("abilities", abilities); + save(data); @@ -49,9 +52,8 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return false; -+ if (!data.contains("abilities")) return false; -+ CompoundTag abilities = data.getCompound("abilities"); -+ return abilities.getByte("flying") == (byte) 1; ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return false; ++ return abilities.getByteOr("flying", (byte) 0) == (byte) 1; + } + } + @@ -62,8 +64,7 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return; -+ if (!data.contains("abilities")) return; -+ CompoundTag abilities = data.getCompound("abilities"); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return; + abilities.putByte("mayfly", (byte) (value ? 1 : 0)); + data.put("abilities", abilities); + save(data); @@ -78,8 +79,7 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return; -+ if (!data.contains("abilities")) return; -+ CompoundTag abilities = data.getCompound("abilities"); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return; + abilities.putFloat("flySpeed", value); + data.put("abilities", abilities); + save(data); @@ -93,9 +93,8 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return 0; -+ if (!data.contains("abilities")) return 0; -+ CompoundTag abilities = data.getCompound("abilities"); -+ return abilities.getFloat("flySpeed"); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return 0; ++ return abilities.getFloatOr("flySpeed", 0); + } + } + @@ -107,8 +106,7 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return; -+ if (!data.contains("abilities")) return; -+ CompoundTag abilities = data.getCompound("abilities"); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return; + abilities.putFloat("walkSpeed", value); + data.put("abilities", abilities); + save(data); @@ -122,31 +120,8 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + } else { + CompoundTag data = this.getData(); + if (data == null) return 0; -+ if (!data.contains("abilities")) return 0; -+ CompoundTag abilities = data.getCompound("abilities"); -+ return abilities.getFloat("walkSpeed"); -+ } -+ } -+ -+ @Override -+ public Location getLocation() { -+ if (this.isOnline()) { -+ return this.getPlayer().getLocation(); -+ } else { -+ CompoundTag data = this.getData(); -+ if (data == null) return null; -+ long worldUUIDMost = data.getLong("WorldUUIDMost"); -+ long worldUUIDLeast = data.getLong("WorldUUIDLeast"); -+ net.minecraft.nbt.ListTag position = data.getList("Pos", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_DOUBLE); -+ net.minecraft.nbt.ListTag rotation = data.getList("Rotation", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_FLOAT); -+ UUID worldUuid = new UUID(worldUUIDMost, worldUUIDLeast); -+ org.bukkit.World world = server.getWorld(worldUuid); -+ double x = position.getDouble(0); -+ double y = position.getDouble(1); -+ double z = position.getDouble(2); -+ float yaw = rotation.getFloat(0); -+ float pitch = rotation.getFloat(1); -+ return new Location(world, x, y, z, yaw, pitch); ++ if (!(data.get("abilities") instanceof CompoundTag abilities)) return 0; ++ return abilities.getFloatOr("walkSpeed", 0); + } + } + @@ -212,46 +187,13 @@ index c1b874cd6e0498fce3cd53fdbaca30d290e004d7..23087d511b609693f0bb06cbaac8b6cf + File playerDir = server.console.playerDataStorage.getPlayerDir(); + try { + File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir); -+ net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile); ++ net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile.toPath()); + File playerDataFile = new File(playerDir, this.getUniqueId()+".dat"); + File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old"); -+ net.minecraft.Util.safeReplaceFile(playerDataFile, tempFile, playerDataFileOld); ++ net.minecraft.util.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath()); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + } + // Purpur end - OfflinePlayer API } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 5965b4e91e63a3d3b64e5193399ae3b6d774120b..5baf36d8d65574f3e57de0937a949cff03435d30 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -2505,6 +2505,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - return this.getHandle().getAbilities().walkingSpeed * 2f; - } - -+ // Purpur start - OfflinePlayer API -+ @Override -+ public boolean teleportOffline(@NotNull Location destination) { -+ return this.teleport(destination); -+ } -+ -+ @Override -+ public boolean teleportOffline(Location destination, PlayerTeleportEvent.TeleportCause cause) { -+ return this.teleport(destination, cause); -+ } -+ -+ @Override -+ public java.util.concurrent.CompletableFuture teleportOfflineAsync(@NotNull Location destination) { -+ return this.teleportAsync(destination); -+ } -+ -+ @Override -+ public java.util.concurrent.CompletableFuture teleportOfflineAsync(@NotNull Location destination, PlayerTeleportEvent.TeleportCause cause) { -+ return this.teleportAsync(destination, cause); -+ } -+ // Purpur end - OfflinePlayer API -+ - private void validateSpeed(float value) { - Preconditions.checkArgument(value <= 1f && value >= -1f, "Speed value (%s) need to be between -1f and 1f", value); - } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch new file mode 100644 index 000000000..201143cb7 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -0,0 +1,146 @@ +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -417,6 +_,20 @@ + this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); + this.pluginManager.paperPluginManager = this.paperPluginManager; + // Paper end ++ // Purpur start - Language API ++ org.purpurmc.purpur.language.Language.setLanguage(new org.purpurmc.purpur.language.Language() { ++ private net.minecraft.locale.Language language = net.minecraft.locale.Language.getInstance(); ++ @Override ++ public boolean has(@org.jetbrains.annotations.NotNull String key) { ++ return language.has(key); ++ } ++ ++ @Override ++ public @org.jetbrains.annotations.NotNull String getOrDefault(@org.jetbrains.annotations.NotNull String key) { ++ return language.getOrDefault(key); ++ } ++ }); ++ // Purpur end - Language API + + CraftRegistry.setMinecraftRegistry(console.registryAccess()); + +@@ -992,6 +_,7 @@ + + org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot + this.console.paperConfigurations.reloadConfigs(this.console); ++ org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur - Purpur config files + for (ServerLevel world : this.console.getAllLevels()) { + // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty + world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && world.getGameRules().get(GameRules.SPAWN_MONSTERS)); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) +@@ -1007,6 +_,7 @@ + } + } + world.spigotConfig.init(); // Spigot ++ world.purpurConfig.init(); // Purpur - Purpur config files + } + + Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper +@@ -1024,6 +_,7 @@ + org.spigotmc.SpigotConfig.registerCommands(); // Spigot + io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper + this.spark.registerCommandBeforePlugins(this); // Paper - spark ++ org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - Purpur config files + this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); + this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); + +@@ -1484,6 +_,60 @@ + return true; + } + ++ // Purpur start - Added the ability to add combustible items ++ @Override ++ public void addFuel(org.bukkit.Material material, int burnTime) { ++ Preconditions.checkArgument(burnTime > 0, "BurnTime must be greater than 0"); ++ ++ net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)); ++ MinecraftServer.getServer().fuelValues().values.put(itemStack.getItem(), burnTime); ++ } ++ ++ @Override ++ public void removeFuel(org.bukkit.Material material) { ++ net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.fromBukkitCopy(new ItemStack(material)); ++ MinecraftServer.getServer().fuelValues().values.keySet().removeIf(itemStack::is); ++ } ++ // Purpur end - Added the ability to add combustible items ++ ++ // Purpur start - Debug Marker API ++ @Override ++ public void sendBlockHighlight(Location location, int duration) { ++ sendBlockHighlight(location, duration, "", 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, int argb) { ++ sendBlockHighlight(location, duration, "", argb); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text) { ++ sendBlockHighlight(location, duration, text, 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, int argb) { ++ this.worlds.forEach((name, world) -> world.sendBlockHighlight(location, duration, text, argb)); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { ++ sendBlockHighlight(location, duration, "", color, transparency); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { ++ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); ++ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); ++ } ++ ++ @Override ++ public void clearBlockHighlights() { ++ this.worlds.forEach((name, world) -> world.clearBlockHighlights()); ++ } ++ // Purpur end - Debug Marker API ++ + @Override + public List getRecipesFor(ItemStack result) { + Preconditions.checkArgument(result != null, "ItemStack cannot be null"); +@@ -2729,6 +_,18 @@ + return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); + } + ++ // Purpur start - Purpur config files ++ @Override ++ public YamlConfiguration getPurpurConfig() { ++ return org.purpurmc.purpur.PurpurConfig.config; ++ } ++ ++ @Override ++ public java.util.Properties getServerProperties() { ++ return getProperties().properties; ++ } ++ // Purpur end - Purpur config files ++ + @Override + public void restart() { + CraftServer.this.restart(); +@@ -2964,4 +_,18 @@ + public void allowPausing(final Plugin plugin, final boolean value) { + this.console.addPluginAllowingSleep(plugin.getName(), value); + } ++ ++ // Purpur start - Bring back server name ++ @Override ++ public String getServerName() { ++ return this.getProperties().serverName; ++ } ++ // Purpur end - Bring back server name ++ ++ // Purpur start - Lagging threshold ++ @Override ++ public boolean isLagging() { ++ return getServer().lagging; ++ } ++ // Purpur end - Lagging threshold + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftWorld.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftWorld.java.patch new file mode 100644 index 000000000..d13886470 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftWorld.java.patch @@ -0,0 +1,55 @@ +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1931,6 +_,52 @@ + return (this.getHandle().getDragonFight() == null) ? null : new CraftDragonBattle(this.getHandle().getDragonFight()); + } + ++ // Purpur start - Add local difficulty api ++ public float getLocalDifficultyAt(Location location) { ++ return getHandle().getCurrentDifficultyAt(org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(location)).getEffectiveDifficulty(); ++ } ++ // Purpur end - Add local difficulty api ++ ++ // Purpur start - Debug Marker API ++ @Override ++ public void sendBlockHighlight(Location location, int duration) { ++ sendBlockHighlight(location, duration, "", 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, int argb) { ++ sendBlockHighlight(location, duration, "", argb); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text) { ++ sendBlockHighlight(location, duration, text, 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, int argb) { ++ // Purpur - TODO: NOOP until further notice ++ //net.minecraft.network.protocol.game.DebugPackets.sendGameTestAddMarker(getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(location), text, argb, duration); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { ++ sendBlockHighlight(location, duration, "", color, transparency); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { ++ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); ++ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); ++ } ++ ++ @Override ++ public void clearBlockHighlights() { ++ // Purpur - TODO: NOOP until further notice ++ //net.minecraft.network.protocol.game.DebugPackets.sendGameTestClearPacket(getHandle()); ++ } ++ // Purpur end - Debug Marker API ++ + @Override + public Collection getStructures(int x, int z) { + return this.getStructures(x, z, struct -> true); diff --git a/patches/server/0024-Disable-outdated-build-check.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch similarity index 50% rename from patches/server/0024-Disable-outdated-build-check.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch index f6d5981b2..5de962abf 100644 --- a/patches/server/0024-Disable-outdated-build-check.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch @@ -1,19 +1,26 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Sun, 15 Dec 2019 12:53:59 -0600 -Subject: [PATCH] Disable outdated build check - - -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 2be5346e48f9f8410f3e4e9a2aa57b063495eb93..20aed44f6d50ff279002259c05c84e7d15904142 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -292,7 +292,7 @@ public class Main { +@@ -164,6 +_,14 @@ + .defaultsTo(new File[] {}) + .describedAs("Jar file"); + ++ // Purpur start - Purpur config files ++ this.acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("purpur.yml")) ++ .describedAs("Yml file"); ++ // Purpur end - Purpur config files ++ + this.accepts("server-name", "Name of the server") + .withRequiredArg() + .ofType(String.class) +@@ -223,7 +_,7 @@ System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper } - if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { -+ if (false && Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { // Purpur ++ if (false && Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { // Purpur - Disable outdated build check Date buildDate = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse(Main.class.getPackage().getImplementationVendor()); // Paper Calendar deadline = Calendar.getInstance(); diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java.patch new file mode 100644 index 000000000..b2fe7e396 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java.patch @@ -0,0 +1,81 @@ +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +@@ -16,8 +_,15 @@ + + public class CraftBeehive extends CraftBlockEntityState implements Beehive { + ++ private final List> storage = new ArrayList<>(); // Purpur - Stored Bee API ++ + public CraftBeehive(World world, BeehiveBlockEntity blockEntity) { + super(world, blockEntity); ++ // Purpur start - load bees to be able to modify them individually - Stored Bee API ++ for(BeehiveBlockEntity.BeeData data : blockEntity.getStored()) { ++ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this, blockEntity)); ++ } ++ // Purpur end - Stored Bee API + } + + protected CraftBeehive(CraftBeehive state, Location location) { +@@ -76,14 +_,54 @@ + } + } + ++ storage.clear(); // Purpur - Stored Bee API + return bees; + } + ++ // Purpur start - Stored Bee API ++ @Override ++ public Bee releaseEntity(org.purpurmc.purpur.entity.StoredEntity entity) { ++ ensureNoWorldGeneration(); ++ ++ if(!getEntities().contains(entity)) { ++ return null; ++ } ++ ++ if(isPlaced()) { ++ BeehiveBlockEntity beehive = ((BeehiveBlockEntity) this.getBlockEntityFromWorld()); ++ BeehiveBlockEntity.BeeData data = ((org.purpurmc.purpur.entity.PurpurStoredBee) entity).getHandle(); ++ ++ List list = beehive.releaseBee(getHandle(), data, BeeReleaseStatus.BEE_RELEASED, true); ++ ++ if (list.size() == 1) { ++ storage.remove(entity); ++ ++ return (Bee) list.get(0).getBukkitEntity(); ++ } ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public List> getEntities() { ++ return new ArrayList<>(storage); ++ } ++ // Purpur end - Stored Bee API ++ + @Override + public void addEntity(Bee entity) { + Preconditions.checkArgument(entity != null, "Entity must not be null"); + ++ int length = this.getSnapshot().getStored().size(); // Purpur - Stored Bee API + this.getSnapshot().addOccupant(((CraftBee) entity).getHandle()); ++ ++ // Purpur start - check if new bee was added, and if yes, add to stored bees - Stored Bee API ++ List storedBeeData = this.getSnapshot().getStored(); ++ if(length < storedBeeData.size()) { ++ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(storedBeeData.getLast(), this, this.getBlockEntity())); ++ } ++ // Purpur end - Stored Bee API + } + + @Override +@@ -100,6 +_,7 @@ + @Override + public void clearEntities() { + getSnapshot().clearBees(); ++ storage.clear(); // Purpur - Stored Bee API + } + // Paper end + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java.patch new file mode 100644 index 000000000..7ce19fa24 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java +@@ -75,7 +_,7 @@ + public int getRange() { + this.ensureNoWorldGeneration(); + ConduitBlockEntity conduit = (ConduitBlockEntity) this.getBlockEntityFromWorld(); +- return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks) : 0; ++ return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks, this.world.getHandle()) : 0; // Purpur - Conduit behavior configuration + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java.patch new file mode 100644 index 000000000..18d1a9162 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java.patch @@ -0,0 +1,25 @@ +--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java +@@ -21,7 +_,12 @@ + + @Override + public void sendMessage(String message) { +- this.sendRawMessage(message); ++ // Purpur start - Rebrand ++ String[] parts = message.split("\n"); ++ for (String part : parts) { ++ this.sendRawMessage(part); ++ } ++ // Purpur end - Rebrand + } + + @Override +@@ -88,7 +_,7 @@ + + @Override + public void sendMessage(final net.kyori.adventure.identity.Identity identity, final net.kyori.adventure.text.Component message, final net.kyori.adventure.audience.MessageType type) { +- this.sendRawMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); ++ this.sendMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message)); // Purpur - Rebrand + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java.patch new file mode 100644 index 000000000..997bb79b3 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCopperGolem.java +@@ -55,4 +_,17 @@ + default -> throw new IllegalStateException("Unexpected value: " + oxidizing); + } + } ++ ++ // Purpur start - Summoner API ++ @Override ++ @org.jetbrains.annotations.Nullable ++ public java.util.UUID getSummoner() { ++ return getHandle().getSummoner(); ++ } ++ ++ @Override ++ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { ++ getHandle().setSummoner(summoner); ++ } ++ // Purpur end - Summoner API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java.patch new file mode 100644 index 000000000..e28023a49 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java.patch @@ -0,0 +1,17 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java +@@ -16,12 +_,12 @@ + + @Override + public boolean isPlayerSpawned() { +- return false; ++ return getHandle().isPlayerSpawned(); // Purpur - Add back player spawned endermite API + } + + @Override + public void setPlayerSpawned(boolean playerSpawned) { +- // Nop ++ getHandle().setPlayerSpawned(playerSpawned); // Purpur - Add back player spawned endermite API + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java.patch new file mode 100644 index 000000000..8fd4ad85d --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java.patch @@ -0,0 +1,21 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -114,6 +_,18 @@ + this.entityType = CraftEntityType.minecraftToBukkit(entity.getType()); + } + ++ // Purpur start - Fire Immunity API ++ @Override ++ public boolean isImmuneToFire() { ++ return getHandle().fireImmune(); ++ } ++ ++ @Override ++ public void setImmuneToFire(Boolean fireImmune) { ++ getHandle().immuneToFire = fireImmune; ++ } ++ // Purpur end - Fire Immunity API ++ + public static CraftEntity getEntity(CraftServer server, T entity) { + Preconditions.checkArgument(entity != null, "Unknown entity"); + diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java.patch new file mode 100644 index 000000000..c8c0b7a9a --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +@@ -291,6 +_,7 @@ + @Override + public void recalculatePermissions() { + this.perm.recalculatePermissions(); ++ getHandle().canPortalInstant = hasPermission("purpur.portal.instant"); // Purpur - Add portal permission bypass + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java.patch new file mode 100644 index 000000000..6acff6eeb --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java +@@ -22,4 +_,17 @@ + public void setPlayerCreated(boolean playerCreated) { + this.getHandle().setPlayerCreated(playerCreated); + } ++ ++ // Purpur start - Summoner API ++ @Override ++ @org.jetbrains.annotations.Nullable ++ public java.util.UUID getSummoner() { ++ return getHandle().getSummoner(); ++ } ++ ++ @Override ++ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { ++ getHandle().setSummoner(summoner); ++ } ++ // Purpur end - Summoner API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java.patch new file mode 100644 index 000000000..7a2d0a451 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java.patch @@ -0,0 +1,56 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +@@ -145,4 +_,53 @@ + public UUID getThrower() { + return Optionull.map(this.getHandle().thrower, EntityReference::getUUID); + } ++ ++ // Purpur start - Item entity immunities ++ @Override ++ public void setImmuneToCactus(boolean immuneToCactus) { ++ this.getHandle().immuneToCactus = immuneToCactus; ++ } ++ ++ @Override ++ public boolean isImmuneToCactus() { ++ return this.getHandle().immuneToCactus; ++ } ++ ++ @Override ++ public void setImmuneToExplosion(boolean immuneToExplosion) { ++ this.getHandle().immuneToExplosion = immuneToExplosion; ++ } ++ ++ @Override ++ public boolean isImmuneToExplosion() { ++ return this.getHandle().immuneToExplosion; ++ } ++ ++ // Purpur start - Fire Immunity API ++ @Override ++ public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) { ++ this.getHandle().immuneToFire = (immuneToFire != null && immuneToFire); ++ } ++ // Purpur end - Fire Immunity API ++ ++ @Override ++ public void setImmuneToFire(boolean immuneToFire) { ++ this.setImmuneToFire((Boolean) immuneToFire); // Purpur - Fire Immunity API ++ } ++ ++ @Override ++ public boolean isImmuneToFire() { ++ return this.getHandle().immuneToFire; ++ } ++ ++ @Override ++ public void setImmuneToLightning(boolean immuneToLightning) { ++ this.getHandle().immuneToLightning = immuneToLightning; ++ } ++ ++ @Override ++ public boolean isImmuneToLightning() { ++ return this.getHandle().immuneToLightning; ++ } ++ // Purpur end - Item entity immunities + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java.patch new file mode 100644 index 000000000..c713f73ad --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -482,7 +_,7 @@ + net.minecraft.server.level.ServerPlayer nmsKiller = killer == null ? null : ((CraftPlayer) killer).getHandle(); + this.getHandle().setLastHurtByMob(nmsKiller); + if (nmsKiller != null) { +- this.getHandle().setLastHurtByPlayer(nmsKiller, 100); // value taken from LivingEntity#resolvePlayerResponsibleForDamage ++ this.getHandle().setLastHurtByPlayer(nmsKiller, this.getHandle().level().purpurConfig.mobLastHurtByPlayerTime); // value taken from LivingEntity#resolvePlayerResponsibleForDamage // Purpur - Config for mob last hurt by player time + } else { + this.getHandle().lastHurtByPlayer = null; + this.getHandle().lastHurtByPlayerMemoryTime = 0; diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java.patch new file mode 100644 index 000000000..f185fa5ca --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java +@@ -86,4 +_,17 @@ + public Llama getCaravanTail() { + return this.getHandle().caravanTail == null ? null : (Llama) this.getHandle().caravanTail.getBukkitEntity(); + } ++ // Paper end ++ ++ // Purpur start - Llama API ++ @Override ++ public boolean shouldJoinCaravan() { ++ return getHandle().shouldJoinCaravan; ++ } ++ ++ @Override ++ public void setShouldJoinCaravan(boolean shouldJoinCaravan) { ++ getHandle().shouldJoinCaravan = shouldJoinCaravan; ++ } ++ // Purpur end - Llama API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch new file mode 100644 index 000000000..57f7dc3d0 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch @@ -0,0 +1,129 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -582,10 +_,15 @@ + + @Override + public void setPlayerListName(String name) { ++ // Purpur start - AFK API ++ setPlayerListName(name, false); ++ } ++ public void setPlayerListName(String name, boolean useMM) { ++ // Purpur end - AFK API + if (name == null) { + name = this.getName(); + } +- this.getHandle().listName = name.equals(this.getName()) ? null : CraftChatMessage.fromStringOrNull(name); ++ this.getHandle().listName = name.equals(this.getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur - AFK API + if (this.getHandle().connection == null) return; // Paper - Updates are possible before the player has fully joined + for (ServerPlayer player : this.server.getHandle().players) { + if (player.getBukkitEntity().canSee(this)) { +@@ -988,6 +_,80 @@ + } + } + ++ // Purpur start - Purpur client support ++ @Override ++ public boolean usesPurpurClient() { ++ return getHandle().connection.purpurClient; ++ } ++ // Purpur end - Purpur client support ++ ++ // Purpur start - AFK API ++ @Override ++ public boolean isAfk() { ++ return getHandle().isAfk(); ++ } ++ ++ @Override ++ public void setAfk(boolean setAfk) { ++ getHandle().setAfk(setAfk); ++ } ++ ++ @Override ++ public void resetIdleTimer() { ++ getHandle().resetLastActionTime(); ++ } ++ // Purpur end - AFK API ++ ++ // Purpur start - Debug Marker API ++ @Override ++ public void sendBlockHighlight(Location location, int duration) { ++ sendBlockHighlight(location, duration, "", 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, int argb) { ++ sendBlockHighlight(location, duration, "", argb); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text) { ++ sendBlockHighlight(location, duration, text, 0x6400FF00); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, int argb) { ++ if (this.getHandle().connection == null) return; ++ // Purpur - TODO: NOOP until further notice ++ //this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestAddMarkerDebugPayload(org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(location), argb, text, duration))); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, org.bukkit.Color color, int transparency) { ++ sendBlockHighlight(location, duration, "", color, transparency); ++ } ++ ++ @Override ++ public void sendBlockHighlight(Location location, int duration, String text, org.bukkit.Color color, int transparency) { ++ if (transparency < 0 || transparency > 255) throw new IllegalArgumentException("transparency is outside of 0-255 range"); ++ sendBlockHighlight(location, duration, text, transparency << 24 | color.asRGB()); ++ } ++ ++ @Override ++ public void clearBlockHighlights() { ++ if (this.getHandle().connection == null) return; ++ // Purpur - TODO: NOOP until further notice ++ //this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestClearMarkersDebugPayload())); ++ } ++ // Purpur end - Debug Marker API ++ ++ // Purpur start - Death screen API ++ @Override ++ public void sendDeathScreen(net.kyori.adventure.text.Component message) { ++ if (this.getHandle().connection == null) return; ++ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message))); ++ } ++ // Purpur end - Death screen API ++ + @Override + public void sendBlockDamage(Location loc, float progress, org.bukkit.entity.Entity source) { + Preconditions.checkArgument(source != null, "source must not be null"); +@@ -2495,6 +_,28 @@ + public float getWalkSpeed() { + return this.getHandle().getAbilities().walkingSpeed * 2f; + } ++ ++ // Purpur start - OfflinePlayer API ++ @Override ++ public boolean teleportOffline(Location destination) { ++ return this.teleport(destination); ++ } ++ ++ @Override ++ public boolean teleportOffline(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ return this.teleport(destination, cause); ++ } ++ ++ @Override ++ public java.util.concurrent.CompletableFuture teleportOfflineAsync(Location destination) { ++ return this.teleportAsync(destination); ++ } ++ ++ @Override ++ public java.util.concurrent.CompletableFuture teleportOfflineAsync(Location destination, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ return this.teleportAsync(destination, cause); ++ } ++ // Purpur end - OfflinePlayer API + + private void validateSpeed(float value) { + Preconditions.checkArgument(value <= 1f && value >= -1f, "Speed value (%s) need to be between -1f and 1f", value); diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java.patch new file mode 100644 index 000000000..0c72e0175 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java +@@ -24,4 +_,17 @@ + public void setDerp(boolean derpMode) { + this.getHandle().setPumpkin(!derpMode); + } ++ ++ // Purpur start - Summoner API ++ @Override ++ @org.jetbrains.annotations.Nullable ++ public java.util.UUID getSummoner() { ++ return getHandle().getSummoner(); ++ } ++ ++ @Override ++ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { ++ getHandle().setSummoner(summoner); ++ } ++ // Purpur end - Summoner API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java.patch new file mode 100644 index 000000000..6f33f6cc9 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java.patch @@ -0,0 +1,14 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +@@ -243,4 +_,11 @@ + public void restock() { + getHandle().restock(); + } ++ ++ // Purpur start - Lobotomize stuck villagers ++ @Override ++ public boolean isLobotomized() { ++ return getHandle().isLobotomized(); ++ } ++ // Purpur end - Lobotomize stuck villagers + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java.patch new file mode 100644 index 000000000..61556048d --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java.patch @@ -0,0 +1,21 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java +@@ -92,4 +_,18 @@ + public void enterInvulnerabilityPhase() { + this.getHandle().makeInvulnerable(); + } ++ // Paper end ++ ++ // Purpur start - Summoner API ++ @Override ++ @org.jetbrains.annotations.Nullable ++ public java.util.UUID getSummoner() { ++ return getHandle().getSummoner(); ++ } ++ ++ @Override ++ public void setSummoner(@org.jetbrains.annotations.Nullable java.util.UUID summoner) { ++ getHandle().setSummoner(summoner); ++ } ++ // Purpur end - Summoner API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java.patch new file mode 100644 index 000000000..708b9bc1e --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java.patch @@ -0,0 +1,21 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java +@@ -90,6 +_,18 @@ + this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(soundVariant)); + } + ++ // Purpur start - Configurable chance for wolves to spawn rabid ++ @Override ++ public boolean isRabid() { ++ return getHandle().isRabid(); ++ } ++ ++ @Override ++ public void setRabid(boolean isRabid) { ++ getHandle().setRabid(isRabid); ++ } ++ // Purpur end - Configurable chance for wolves to spawn rabid ++ + public static class CraftVariant extends HolderableBase implements Variant { + + public static Variant minecraftHolderToBukkit(Holder minecraft) { diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java.patch new file mode 100644 index 000000000..58b895e59 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java.patch @@ -0,0 +1,55 @@ +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java +@@ -19,6 +_,10 @@ + private int repairCost; + private int repairCostAmount; + private int maximumRepairCost; ++ // Purpur start - Anvil API ++ private boolean bypassCost; ++ private boolean canDoUnsafeEnchants; ++ // Purpur end - Anvil API + + public CraftInventoryAnvil(Location location, Container inventory, Container resultInventory) { + super(inventory, resultInventory); +@@ -27,6 +_,10 @@ + this.repairCost = CraftInventoryAnvil.DEFAULT_REPAIR_COST; + this.repairCostAmount = CraftInventoryAnvil.DEFAULT_REPAIR_COST_AMOUNT; + this.maximumRepairCost = CraftInventoryAnvil.DEFAULT_MAXIMUM_REPAIR_COST; ++ // Purpur start - Anvil API ++ this.bypassCost = false; ++ this.canDoUnsafeEnchants = false; ++ // Purpur end - Anvil API + } + + @Override +@@ -113,4 +_,30 @@ + consumer.accept(cav); + } + } ++ ++ // Purpur start - Anvil API ++ @Override ++ public boolean canBypassCost() { ++ this.syncWithArbitraryViewValue((cav) -> this.bypassCost = cav.canBypassCost()); ++ return this.bypassCost; ++ } ++ ++ @Override ++ public void setBypassCost(boolean bypassCost) { ++ this.bypassCost = bypassCost; ++ this.syncViews((cav) -> cav.setBypassCost(bypassCost)); ++ } ++ ++ @Override ++ public boolean canDoUnsafeEnchants() { ++ this.syncWithArbitraryViewValue((cav) -> this.canDoUnsafeEnchants = cav.canDoUnsafeEnchants()); ++ return this.canDoUnsafeEnchants; ++ } ++ ++ @Override ++ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) { ++ this.canDoUnsafeEnchants = canDoUnsafeEnchants; ++ this.syncViews((cav) -> cav.setDoUnsafeEnchants(canDoUnsafeEnchants)); ++ } ++ // Purpur end - Anvil API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch new file mode 100644 index 000000000..0708b4d47 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch @@ -0,0 +1,288 @@ +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +@@ -672,4 +_,285 @@ + } + + // Paper end - data component API ++ ++ // Purpur start - ItemStack convenience methods ++ @Override ++ public String getDisplayName() { ++ return getItemMeta().getDisplayName(); ++ } ++ ++ @Override ++ public void setDisplayName(String name) { ++ ItemMeta itemMeta = getItemMeta(); ++ itemMeta.setDisplayName(name); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public boolean hasDisplayName() { ++ return hasItemMeta() && getItemMeta().hasDisplayName(); ++ } ++ ++ @Override ++ public String getLocalizedName() { ++ return getItemMeta().getLocalizedName(); ++ } ++ ++ @Override ++ public void setLocalizedName(String name) { ++ ItemMeta itemMeta = getItemMeta(); ++ itemMeta.setLocalizedName(name); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public boolean hasLocalizedName() { ++ return hasItemMeta() && getItemMeta().hasLocalizedName(); ++ } ++ ++ @Override ++ public boolean hasLore() { ++ return hasItemMeta() && getItemMeta().hasLore(); ++ } ++ ++ @Override ++ public boolean hasEnchant(Enchantment ench) { ++ return hasItemMeta() && getItemMeta().hasEnchant(ench); ++ } ++ ++ @Override ++ public int getEnchantLevel(Enchantment ench) { ++ return getItemMeta().getEnchantLevel(ench); ++ } ++ ++ @Override ++ public Map getEnchants() { ++ return getItemMeta().getEnchants(); ++ } ++ ++ @Override ++ public boolean addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.addEnchant(ench, level, ignoreLevelRestriction); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public boolean removeEnchant(Enchantment ench) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.removeEnchant(ench); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public boolean hasEnchants() { ++ return hasItemMeta() && getItemMeta().hasEnchants(); ++ } ++ ++ @Override ++ public boolean hasConflictingEnchant(Enchantment ench) { ++ return hasItemMeta() && getItemMeta().hasConflictingEnchant(ench); ++ } ++ ++ @Override ++ public void setCustomModelData(Integer data) { ++ ItemMeta itemMeta = getItemMeta(); ++ itemMeta.setCustomModelData(data); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public int getCustomModelData() { ++ return getItemMeta().getCustomModelData(); ++ } ++ ++ @Override ++ public boolean hasCustomModelData() { ++ return hasItemMeta() && getItemMeta().hasCustomModelData(); ++ } ++ ++ @Override ++ public boolean hasBlockData() { ++ return hasItemMeta() && ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).hasBlockData(); ++ } ++ ++ @Override ++ public org.bukkit.block.data.BlockData getBlockData(Material material) { ++ return ((org.bukkit.inventory.meta.BlockDataMeta) getItemMeta()).getBlockData(material); ++ } ++ ++ @Override ++ public void setBlockData(org.bukkit.block.data.BlockData blockData) { ++ ItemMeta itemMeta = getItemMeta(); ++ ((org.bukkit.inventory.meta.BlockDataMeta) itemMeta).setBlockData(blockData); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public int getRepairCost() { ++ return ((org.bukkit.inventory.meta.Repairable) getItemMeta()).getRepairCost(); ++ } ++ ++ @Override ++ public void setRepairCost(int cost) { ++ ItemMeta itemMeta = getItemMeta(); ++ ((org.bukkit.inventory.meta.Repairable) itemMeta).setRepairCost(cost); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public boolean hasRepairCost() { ++ return hasItemMeta() && ((org.bukkit.inventory.meta.Repairable) getItemMeta()).hasRepairCost(); ++ } ++ ++ @Override ++ public boolean isUnbreakable() { ++ return hasItemMeta() && getItemMeta().isUnbreakable(); ++ } ++ ++ @Override ++ public void setUnbreakable(boolean unbreakable) { ++ ItemMeta itemMeta = getItemMeta(); ++ itemMeta.setUnbreakable(unbreakable); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public boolean hasAttributeModifiers() { ++ return hasItemMeta() && getItemMeta().hasAttributeModifiers(); ++ } ++ ++ @Override ++ public com.google.common.collect.Multimap getAttributeModifiers() { ++ return getItemMeta().getAttributeModifiers(); ++ } ++ ++ @Override ++ public com.google.common.collect.Multimap getAttributeModifiers(org.bukkit.inventory.EquipmentSlot slot) { ++ return getItemMeta().getAttributeModifiers(slot); ++ } ++ ++ @Override ++ public java.util.Collection getAttributeModifiers(org.bukkit.attribute.Attribute attribute) { ++ return getItemMeta().getAttributeModifiers(attribute); ++ } ++ ++ @Override ++ public boolean addAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.addAttributeModifier(attribute, modifier); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public void setAttributeModifiers(com.google.common.collect.Multimap attributeModifiers) { ++ ItemMeta itemMeta = getItemMeta(); ++ itemMeta.setAttributeModifiers(attributeModifiers); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.removeAttributeModifier(attribute); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public boolean removeAttributeModifier(org.bukkit.inventory.EquipmentSlot slot) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.removeAttributeModifier(slot); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public boolean removeAttributeModifier(org.bukkit.attribute.Attribute attribute, org.bukkit.attribute.AttributeModifier modifier) { ++ ItemMeta itemMeta = getItemMeta(); ++ boolean result = itemMeta.removeAttributeModifier(attribute, modifier); ++ setItemMeta(itemMeta); ++ return result; ++ } ++ ++ @Override ++ public boolean hasDamage() { ++ return hasItemMeta() && ((org.bukkit.inventory.meta.Damageable) getItemMeta()).hasDamage(); ++ } ++ ++ @Override ++ public int getDamage() { ++ return ((org.bukkit.inventory.meta.Damageable) getItemMeta()).getDamage(); ++ } ++ ++ @Override ++ public void setDamage(int damage) { ++ ItemMeta itemMeta = getItemMeta(); ++ ((org.bukkit.inventory.meta.Damageable) itemMeta).setDamage(damage); ++ setItemMeta(itemMeta); ++ } ++ ++ @Override ++ public void repair() { ++ repair(1); ++ } ++ ++ @Override ++ public boolean damage() { ++ return damage(1); ++ } ++ ++ @Override ++ public void repair(int amount) { ++ damage(-amount); ++ } ++ ++ @Override ++ public boolean damage(int amount) { ++ return damage(amount, false); ++ } ++ ++ @Override ++ public boolean damage(int amount, boolean ignoreUnbreaking) { ++ org.bukkit.inventory.meta.Damageable damageable = (org.bukkit.inventory.meta.Damageable) getItemMeta(); ++ if (amount > 0) { ++ int unbreaking = getEnchantLevel(Enchantment.UNBREAKING); ++ int reduce = 0; ++ for (int i = 0; unbreaking > 0 && i < amount; ++i) { ++ if (reduceDamage(java.util.concurrent.ThreadLocalRandom.current(), unbreaking)) { ++ ++reduce; ++ } ++ } ++ amount -= reduce; ++ if (amount <= 0) { ++ return isBroke(damageable.getDamage()); ++ } ++ } ++ int damage = damageable.getDamage() + amount; ++ damageable.setDamage(damage); ++ setItemMeta((ItemMeta) damageable); ++ return isBroke(damage); ++ } ++ ++ private boolean isBroke(int damage) { ++ if (damage > getType().getMaxDurability()) { ++ if (getAmount() > 0) { ++ // ensure it "breaks" ++ setAmount(0); ++ } ++ return true; ++ } ++ return false; ++ } ++ ++ private boolean reduceDamage(java.util.Random random, int unbreaking) { ++ if (getType().isArmor()) { ++ return random.nextFloat() < 0.6F; ++ } ++ return random.nextInt(unbreaking + 1) > 0; ++ } ++ // Purpur end - ItemStack convenience methods + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java.patch new file mode 100644 index 000000000..b3042e1f5 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +@@ -36,6 +_,7 @@ + stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat))); + } else if (bukkit instanceof RecipeChoice.ExactChoice) { + stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> CraftItemStack.asNMSCopy(mat)).toList()); ++ stack.predicate = ((RecipeChoice.ExactChoice) bukkit).getPredicate(); // Purpur - Add predicate to recipe's ExactChoice ingredient + // Paper start - support "empty" choices - legacy method that spigot might incorrectly call + // Their impl of Ingredient.of() will error, ingredients need at least one entry. + // Callers running into this exception may have passed an incorrect empty() recipe choice to a non-empty slot or diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java.patch new file mode 100644 index 000000000..5d9234b7b --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java.patch @@ -0,0 +1,29 @@ +--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java +@@ -73,4 +_,26 @@ + this.setMaximumRepairCost(legacy.getMaximumRepairCost()); + } + } ++ ++ // Purpur start - Anvil API ++ @Override ++ public boolean canBypassCost() { ++ return this.container.bypassCost; ++ } ++ ++ @Override ++ public void setBypassCost(boolean bypassCost) { ++ this.container.bypassCost = bypassCost; ++ } ++ ++ @Override ++ public boolean canDoUnsafeEnchants() { ++ return this.container.canDoUnsafeEnchants; ++ } ++ ++ @Override ++ public void setDoUnsafeEnchants(boolean canDoUnsafeEnchants) { ++ this.container.canDoUnsafeEnchants = canDoUnsafeEnchants; ++ } ++ // Purpur end - Anvil API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java.patch new file mode 100644 index 000000000..beea25a6b --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java ++++ b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java +@@ -260,6 +_,7 @@ + } + + static { ++ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressInitLegacyMaterialError) // Purpur - Logger settings (suppressing pointless logs) + LOGGER.warn("Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug!"); // Paper - Improve logging and errors; doesn't need to be an error + if (MinecraftServer.getServer() != null && MinecraftServer.getServer().isDebugging()) { + new Exception().printStackTrace(); diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch new file mode 100644 index 000000000..990500141 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch @@ -0,0 +1,35 @@ +--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java ++++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +@@ -703,4 +_,32 @@ + meta.setCanPlaceOn(materials); + } + // Paper end ++ // Purpur start - Adopt MaterialRerouting ++ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570) ++ public static void addFuel(Server server, Material material, int burnTime) { ++ server.addFuel(material, burnTime); ++ } ++ ++ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570) ++ public static void removeFuel(Server server, Material material) { ++ server.removeFuel(material); ++ } ++ ++ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570) ++ @RerouteStatic("org/bukkit/Bukkit") ++ public static void addFuel(Material material, int burnTime) { ++ Bukkit.addFuel(material, burnTime); ++ } ++ ++ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/pull/570) ++ @RerouteStatic("org/bukkit/Bukkit") ++ public static void removeFuel(Material material) { ++ Bukkit.removeFuel(material); ++ } ++ ++ // Method added post 1.13, no-op (https://github.com/PurpurMC/Purpur/commit/607d909efba516893072b782c0393c53d048210e) ++ public static BlockData getBlockData(ItemStack itemStack, Material material) { ++ return itemStack.getBlockData(MaterialRerouting.transformToBlockType(material)); ++ } ++ // Purpur end - Adopt MaterialRerouting + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java.patch new file mode 100644 index 000000000..33ae859ed --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java.patch @@ -0,0 +1,13 @@ +--- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java ++++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java +@@ -46,4 +_,10 @@ + } + } + ++ // Purpur start - Explorer Map API ++ @Override ++ public boolean isExplorerMap() { ++ return this.worldMap.isExplorerMap; ++ } ++ // Purpur end - Explorer Map API + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java.patch new file mode 100644 index 000000000..46d23c417 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +@@ -491,7 +_,7 @@ + this.parsePending(); + } else { + // this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(this.currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper +- task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper ++ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Purpur"); // Paper // Purpur - Rebrand + // We don't need to parse pending + // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java.patch new file mode 100644 index 000000000..6106785b3 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java.patch @@ -0,0 +1,19 @@ +--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java +@@ -23,7 +_,15 @@ + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands); +- DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands); ++ // Purpur start - Gamemode extra permissions ++ Permission gamemodeVanilla = DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode", PermissionDefault.OP, commands); ++ for (net.minecraft.world.level.GameType gametype : net.minecraft.world.level.GameType.values()) { ++ Permission gamemodeSelf = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName(), "Allows the user to set " + gametype.getName() + " gamemode for self", PermissionDefault.OP); ++ Permission gamemodeOther = DefaultPermissions.registerPermission(PREFIX + "gamemode." + gametype.getName() + ".other", "Allows the user to set " + gametype.getName() + " gamemode for other players", PermissionDefault.OP); ++ gamemodeSelf.addParent(gamemodeOther, true); ++ gamemodeVanilla.addParent(gamemodeSelf, true); ++ } ++ // Purpur end - Gamemode extra permissions + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "experience", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); // Paper - wrong permission; redirects are de-redirected and the root literal name is used, so xp -> experience + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands); diff --git a/purpur-server/paper-patches/files/src/main/java/org/spigotmc/TicksPerSecondCommand.java.patch b/purpur-server/paper-patches/files/src/main/java/org/spigotmc/TicksPerSecondCommand.java.patch new file mode 100644 index 000000000..3b87a8487 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/spigotmc/TicksPerSecondCommand.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/spigotmc/TicksPerSecondCommand.java ++++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java +@@ -47,7 +_,7 @@ + } + + TextComponent.Builder builder = text(); +- builder.append(text("TPS from last 1m, 5m, 15m: ", NamedTextColor.GOLD)); ++ builder.append(text("TPS from last 5s, 1m, 5m, 15m: ", NamedTextColor.GOLD)); // Purpur - Add 5 second tps average in /tps + builder.append(Component.join(JoinConfiguration.commas(true), tpsAvg)); + sender.sendMessage(builder.asComponent()); + if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) { diff --git a/purpur-server/paper-patches/files/src/main/java/org/spigotmc/WatchdogThread.java.patch b/purpur-server/paper-patches/files/src/main/java/org/spigotmc/WatchdogThread.java.patch new file mode 100644 index 000000000..7dc409927 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/spigotmc/WatchdogThread.java.patch @@ -0,0 +1,53 @@ +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -25,7 +_,7 @@ + private volatile boolean stopping; + + private WatchdogThread(long timeoutTime, boolean restart) { +- super("Paper Watchdog Thread"); ++ super("Watchdog Thread"); // Purpur - use a generic name - Rebrand + this.timeoutTime = timeoutTime; + this.restart = restart; + this.earlyWarningEvery = Math.min(GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); +@@ -75,14 +_,14 @@ + this.lastEarlyWarning = currentTime; + if (isLongTimeout) { + logger.log(Level.SEVERE, "------------------------------"); +- logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug."); // Paper ++ logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug."); // Paper // Purpur - Rebrand + logger.log(Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author"); + logger.log(Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring"); + logger.log(Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once"); + logger.log(Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes"); +- logger.log(Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues"); ++ logger.log(Level.SEVERE, "If you are unsure or still think this is a Purpur bug, please report this to https://github.com/PurpurMC/Purpur/issues"); // Purpur - Rebrand + logger.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports"); +- logger.log(Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion()); ++ logger.log(Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion()); // Purpur - Rebrand + + if (net.minecraft.world.level.Level.lastPhysicsProblem != null) { + logger.log(Level.SEVERE, "------------------------------"); +@@ -102,12 +_,12 @@ + } + // Paper end + } else { +- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); ++ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur - Rebrand + logger.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump"); + } + // Paper end - Different message for short timeout + logger.log(Level.SEVERE, "------------------------------"); +- logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):"); // Paper ++ logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):"); // Paper // Purpur - Rebrand + FeatureHooks.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - log detailed tick information + WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.threadId(), Integer.MAX_VALUE), logger); + logger.log(Level.SEVERE, "------------------------------"); +@@ -120,7 +_,7 @@ + WatchdogThread.dumpThread(thread, logger); + } + } else { +- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); ++ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur - Rebrand + } + + logger.log(Level.SEVERE, "------------------------------"); diff --git a/purpur-server/paper-patches/files/src/main/resources/log4j2.xml.patch b/purpur-server/paper-patches/files/src/main/resources/log4j2.xml.patch new file mode 100644 index 000000000..8b4a31032 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/resources/log4j2.xml.patch @@ -0,0 +1,20 @@ +--- a/src/main/resources/log4j2.xml ++++ b/src/main/resources/log4j2.xml +@@ -2,7 +_,16 @@ + + + +- ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + diff --git a/patches/server/0252-Skip-junit-tests-for-purpur-commands.patch b/purpur-server/paper-patches/files/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java.patch similarity index 67% rename from patches/server/0252-Skip-junit-tests-for-purpur-commands.patch rename to purpur-server/paper-patches/files/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java.patch index 039ecea43..6fb471079 100644 --- a/patches/server/0252-Skip-junit-tests-for-purpur-commands.patch +++ b/purpur-server/paper-patches/files/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java.patch @@ -1,26 +1,18 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Thu, 8 Dec 2022 19:13:26 -0600 -Subject: [PATCH] Skip junit tests for purpur commands - - -diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -index 4f43882d930ab8816e75b216d9a61a06b79df265..40fc8b6579cc29e68720a99ac12f8adacc1d95be 100644 --- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java +++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -@@ -45,6 +45,7 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase { +@@ -46,6 +_,7 @@ Set foundPerms = new HashSet<>(); for (CommandNode child : root.getChildren()) { final String vanillaPerm = VanillaCommandWrapper.getPermission(child); -+ if (TO_SKIP.contains(vanillaPerm)) continue; // Purpur ++ if (TO_SKIP.contains(vanillaPerm)) continue; // Purpur - Skip junit tests for purpur commands if (!perms.contains(vanillaPerm)) { missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command"); } else { -@@ -57,6 +58,25 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase { +@@ -58,6 +_,25 @@ } private static final List TO_SKIP = List.of( -+ // Purpur start ++ // Purpur start - Skip junit tests for purpur commands + "minecraft.command.compass", + "minecraft.command.credits", + "minecraft.command.demo", @@ -38,7 +30,7 @@ index 4f43882d930ab8816e75b216d9a61a06b79df265..40fc8b6579cc29e68720a99ac12f8ada + "minecraft.command.gamemode.spectator.other", + "minecraft.command.gamemode.survival", + "minecraft.command.gamemode.survival.other", -+ // Purpur end ++ // Purpur end - Skip junit tests for purpur commands "minecraft.command.selector" ); diff --git a/purpur-server/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java b/purpur-server/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java new file mode 100644 index 000000000..ede7cb766 --- /dev/null +++ b/purpur-server/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java @@ -0,0 +1,85 @@ +package org.purpurmc.purpur.gui.util; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.pattern.ConverterKeys; +import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; +import org.apache.logging.log4j.core.pattern.PatternConverter; +import org.apache.logging.log4j.core.pattern.PatternFormatter; +import org.apache.logging.log4j.core.pattern.PatternParser; +import org.apache.logging.log4j.util.PerformanceSensitive; + +import java.util.List; + +@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY) +@ConverterKeys({"highlightGUIError"}) +@PerformanceSensitive("allocation") +public final class HighlightErrorConverter extends LogEventPatternConverter { + private static final String ERROR = "\u00A74\u00A7l"; // Bold Red + private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow + + private final List formatters; + + private HighlightErrorConverter(List formatters) { + super("highlightGUIError", null); + this.formatters = formatters; + } + + @Override + public void format(LogEvent event, StringBuilder toAppendTo) { + Level level = event.getLevel(); + if (level.isMoreSpecificThan(Level.ERROR)) { + format(ERROR, event, toAppendTo); + return; + } else if (level.isMoreSpecificThan(Level.WARN)) { + format(WARN, event, toAppendTo); + return; + } + for (PatternFormatter formatter : formatters) { + formatter.format(event, toAppendTo); + } + } + + private void format(String style, LogEvent event, StringBuilder toAppendTo) { + int start = toAppendTo.length(); + toAppendTo.append(style); + int end = toAppendTo.length(); + + for (PatternFormatter formatter : formatters) { + formatter.format(event, toAppendTo); + } + + if (toAppendTo.length() == end) { + toAppendTo.setLength(start); + } + } + + @Override + public boolean handlesThrowable() { + for (final PatternFormatter formatter : formatters) { + if (formatter.handlesThrowable()) { + return true; + } + } + return false; + } + + public static HighlightErrorConverter newInstance(Configuration config, String[] options) { + if (options.length != 1) { + LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length); + return null; + } + + if (options[0] == null) { + LOGGER.error("No pattern supplied on highlightGUIError"); + return null; + } + + PatternParser parser = PatternLayout.createPatternParser(config); + List formatters = parser.parse(options[0]); + return new HighlightErrorConverter(formatters); + } +} \ No newline at end of file diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java new file mode 100644 index 000000000..e076d4e84 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -0,0 +1,618 @@ +package org.purpurmc.purpur; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.EntityDimensions; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockBehaviour; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.purpurmc.purpur.command.PurpurCommand; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.purpurmc.purpur.task.TPSBarTask; + +@SuppressWarnings("unused") +public class PurpurConfig { + private static final String HEADER = "This is the main configuration file for Purpur.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "\n" + + "If you need help with the configuration or have any questions related to Purpur,\n" + + "join us in our Discord guild.\n" + + "\n" + + "Website: https://purpurmc.org \n" + + "Docs: https://purpurmc.org/docs \n"; + private static File CONFIG_FILE; + public static YamlConfiguration config; + + private static Map commands; + + public static int version; + static boolean verbose; + + public static void init(File configFile) { + CONFIG_FILE = configFile; + config = new YamlConfiguration(); + try { + config.load(CONFIG_FILE); + } catch (IOException ignore) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not load purpur.yml, please correct your syntax errors", ex); + throw Throwables.propagate(ex); + } + config.options().header(HEADER); + config.options().copyDefaults(true); + verbose = getBoolean("verbose", false); + + commands = new HashMap<>(); + commands.put("purpur", new PurpurCommand("purpur")); + + version = getInt("config-version", 46); + set("config-version", 46); + + readConfig(PurpurConfig.class, null); + + Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache); + } + + protected static void log(String s) { + if (verbose) { + log(Level.INFO, s); + } + } + + protected static void log(Level level, String s) { + Bukkit.getLogger().log(level, s); + } + + public static void registerCommands() { + for (Map.Entry entry : commands.entrySet()) { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue()); + } + } + + static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw Throwables.propagate(ex.getCause()); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); + } + } + } + } + + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); + } + } + + private static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); + } + + private static String getString(String path, String def) { + config.addDefault(path, def); + return config.getString(path, config.getString(path)); + } + + private static boolean getBoolean(String path, boolean def) { + config.addDefault(path, def); + return config.getBoolean(path, config.getBoolean(path)); + } + + private static double getDouble(String path, double def) { + config.addDefault(path, def); + return config.getDouble(path, config.getDouble(path)); + } + + private static int getInt(String path, int def) { + config.addDefault(path, def); + return config.getInt(path, config.getInt(path)); + } + + private static List getList(String path, T def) { + config.addDefault(path, def); + return config.getList(path, config.getList(path)); + } + + static Map getMap(String path, Map def) { + if (def != null && config.getConfigurationSection(path) == null) { + config.addDefault(path, def); + return def; + } + return toMap(config.getConfigurationSection(path)); + } + + private static Map toMap(ConfigurationSection section) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + if (section != null) { + for (String key : section.getKeys(false)) { + Object obj = section.get(key); + if (obj != null) { + builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj); + } + } + } + return builder.build(); + } + + public static String cannotRideMob = "You cannot mount that mob"; + public static String afkBroadcastAway = "%s is now AFK"; + public static String afkBroadcastBack = "%s is no longer AFK"; + public static boolean afkBroadcastUseDisplayName = false; + public static String afkTabListPrefix = "[AFK] "; + public static String afkTabListSuffix = ""; + public static String creditsCommandOutput = "%s has been shown the end credits"; + public static String demoCommandOutput = "%s has been shown the demo screen"; + public static String pingCommandOutput = "%s's ping is %sms"; + public static String ramCommandOutput = "Ram Usage: / ()"; + public static String rambarCommandOutput = "Rambar toggled for "; + public static String tpsbarCommandOutput = "Tpsbar toggled for "; + public static String dontRunWithScissors = "Don't run with scissors!"; + public static String uptimeCommandOutput = "Server uptime is "; + public static String unverifiedUsername = "default"; + public static String sleepSkippingNight = "default"; + public static String sleepingPlayersPercent = "default"; + public static String sleepNotPossible = "default"; + private static void messages() { + cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob); + afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway); + afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack); + afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName); + afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix))); + afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix))); + creditsCommandOutput = getString("settings.messages.credits-command-output", creditsCommandOutput); + demoCommandOutput = getString("settings.messages.demo-command-output", demoCommandOutput); + pingCommandOutput = getString("settings.messages.ping-command-output", pingCommandOutput); + ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput); + rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput); + tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); + dontRunWithScissors = getString("settings.messages.dont-run-with-scissors", dontRunWithScissors); + uptimeCommandOutput = getString("settings.messages.uptime-command-output", uptimeCommandOutput); + unverifiedUsername = getString("settings.messages.unverified-username", unverifiedUsername); + sleepSkippingNight = getString("settings.messages.sleep-skipping-night", sleepSkippingNight); + sleepingPlayersPercent = getString("settings.messages.sleeping-players-percent", sleepingPlayersPercent); + sleepNotPossible = getString("settings.messages.sleep-not-possible", sleepNotPossible); + } + + public static String deathMsgRunWithScissors = " slipped and fell on their shears"; + public static String deathMsgStonecutter = " has sawed themself in half"; + private static void deathMessages() { + deathMsgRunWithScissors = getString("settings.messages.death-message.run-with-scissors", deathMsgRunWithScissors); + deathMsgStonecutter = getString("settings.messages.death-message.stonecutter", deathMsgStonecutter); + } + + public static boolean advancementOnlyBroadcastToAffectedPlayer = false; + public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false; + private static void broadcastSettings() { + if (version < 13) { + boolean oldValue = getBoolean("settings.advancement.only-broadcast-to-affected-player", false); + set("settings.broadcasts.advancement.only-broadcast-to-affected-player", oldValue); + set("settings.advancement.only-broadcast-to-affected-player", null); + } + advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer); + deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer); + } + + public static String serverModName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); + private static void serverModName() { + serverModName = getString("settings.server-mod-name", serverModName); + } + + public static double laggingThreshold = 19.0D; + private static void tickLoopSettings() { + laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold); + } + + public static boolean useAlternateKeepAlive = false; + private static void useAlternateKeepAlive() { + useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); + } + + public static boolean disableGiveCommandDrops = false; + private static void disableGiveCommandDrops() { + disableGiveCommandDrops = getBoolean("settings.disable-give-dropping", disableGiveCommandDrops); + } + + public static String commandRamBarTitle = "Ram: / ()"; + public static BossBar.Overlay commandRamBarProgressOverlay = BossBar.Overlay.NOTCHED_20; + public static BossBar.Color commandRamBarProgressColorGood = BossBar.Color.GREEN; + public static BossBar.Color commandRamBarProgressColorMedium = BossBar.Color.YELLOW; + public static BossBar.Color commandRamBarProgressColorLow = BossBar.Color.RED; + public static String commandRamBarTextColorGood = ""; + public static String commandRamBarTextColorMedium = ""; + public static String commandRamBarTextColorLow = ""; + public static int commandRamBarTickInterval = 20; + public static String commandTPSBarTitle = "TPS: MSPT: Ping: ms"; + public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20; + public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT; + public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN; + public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW; + public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED; + public static String commandTPSBarTextColorGood = ""; + public static String commandTPSBarTextColorMedium = ""; + public static String commandTPSBarTextColorLow = ""; + public static int commandTPSBarTickInterval = 20; + public static String commandCompassBarTitle = "S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 S \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 W \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NW \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 N \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 NE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 E \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 SE \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 \u25C8 \u00B7 "; + public static BossBar.Overlay commandCompassBarProgressOverlay = BossBar.Overlay.PROGRESS; + public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE; + public static float commandCompassBarProgressPercent = 1.0F; + public static int commandCompassBarTickInterval = 5; + public static boolean commandGamemodeRequiresPermission = false; + public static boolean hideHiddenPlayersFromEntitySelector = false; + public static String uptimeFormat = ""; + public static String uptimeDay = "%02d day, "; + public static String uptimeDays = "%02d days, "; + public static String uptimeHour = "%02d hour, "; + public static String uptimeHours = "%02d hours, "; + public static String uptimeMinute = "%02d minute, and "; + public static String uptimeMinutes = "%02d minutes, and "; + public static String uptimeSecond = "%02d second"; + public static String uptimeSeconds = "%02d seconds"; + private static void commandSettings() { + commandRamBarTitle = getString("settings.command.rambar.title", commandRamBarTitle); + commandRamBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.rambar.overlay", commandRamBarProgressOverlay.name())); + commandRamBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.good", commandRamBarProgressColorGood.name())); + commandRamBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.medium", commandRamBarProgressColorMedium.name())); + commandRamBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.rambar.progress-color.low", commandRamBarProgressColorLow.name())); + commandRamBarTextColorGood = getString("settings.command.rambar.text-color.good", commandRamBarTextColorGood); + commandRamBarTextColorMedium = getString("settings.command.rambar.text-color.medium", commandRamBarTextColorMedium); + commandRamBarTextColorLow = getString("settings.command.rambar.text-color.low", commandRamBarTextColorLow); + commandRamBarTickInterval = getInt("settings.command.rambar.tick-interval", commandRamBarTickInterval); + + commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); + commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); + commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name())); + commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name())); + commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name())); + commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name())); + commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood); + commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium); + commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); + commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); + + commandCompassBarTitle = getString("settings.command.compass.title", commandCompassBarTitle); + commandCompassBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.compass.overlay", commandCompassBarProgressOverlay.name())); + commandCompassBarProgressColor = BossBar.Color.valueOf(getString("settings.command.compass.progress-color", commandCompassBarProgressColor.name())); + commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent); + commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval); + + commandGamemodeRequiresPermission = getBoolean("settings.command.gamemode.requires-specific-permission", commandGamemodeRequiresPermission); + hideHiddenPlayersFromEntitySelector = getBoolean("settings.command.hide-hidden-players-from-entity-selector", hideHiddenPlayersFromEntitySelector); + uptimeFormat = getString("settings.command.uptime.format", uptimeFormat); + uptimeDay = getString("settings.command.uptime.day", uptimeDay); + uptimeDays = getString("settings.command.uptime.days", uptimeDays); + uptimeHour = getString("settings.command.uptime.hour", uptimeHour); + uptimeHours = getString("settings.command.uptime.hours", uptimeHours); + uptimeMinute = getString("settings.command.uptime.minute", uptimeMinute); + uptimeMinutes = getString("settings.command.uptime.minutes", uptimeMinutes); + uptimeSecond = getString("settings.command.uptime.second", uptimeSecond); + uptimeSeconds = getString("settings.command.uptime.seconds", uptimeSeconds); + } + + public static int barrelRows = 3; + public static boolean enderChestSixRows = false; + public static boolean enderChestPermissionRows = false; + public static boolean cryingObsidianValidForPortalFrame = false; + public static int beeInsideBeeHive = 3; + public static boolean anvilCumulativeCost = true; + public static int smoothSnowAccumulationStep = 0; + public static int lightningRodRange = 128; + public static Set grindstoneIgnoredEnchants = new HashSet<>(); + public static boolean grindstoneRemoveAttributes = false; + public static boolean grindstoneRemoveDisplay = false; + public static int caveVinesMaxGrowthAge = 25; + public static int kelpMaxGrowthAge = 25; + public static int twistingVinesMaxGrowthAge = 25; + public static int weepingVinesMaxGrowthAge = 25; + public static boolean magmaBlockReverseBubbleColumnFlow = false; + public static boolean soulSandBlockReverseBubbleColumnFlow = false; + private static void blockSettings() { + if (version < 3) { + boolean oldValue = getBoolean("settings.barrel.packed-barrels", true); + set("settings.blocks.barrel.six-rows", oldValue); + set("settings.packed-barrels", null); + oldValue = getBoolean("settings.large-ender-chests", true); + set("settings.blocks.ender_chest.six-rows", oldValue); + set("settings.large-ender-chests", null); + } + if (version < 20) { + boolean oldValue = getBoolean("settings.blocks.barrel.six-rows", false); + set("settings.blocks.barrel.rows", oldValue ? 6 : 3); + set("settings.blocks.barrel.six-rows", null); + } + barrelRows = getInt("settings.blocks.barrel.rows", barrelRows); + if (barrelRows < 1 || barrelRows > 6) { + Bukkit.getLogger().severe("settings.blocks.barrel.rows must be 1-6, resetting to default"); + barrelRows = 3; + } + org.bukkit.event.inventory.InventoryType.BARREL.setDefaultSize(switch (barrelRows) { + case 6 -> 54; + case 5 -> 45; + case 4 -> 36; + case 2 -> 18; + case 1 -> 9; + default -> 27; + }); + enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); + org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); + enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); + cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); + beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); + anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); + smoothSnowAccumulationStep = getInt("settings.blocks.snow.smooth-accumulation-step", smoothSnowAccumulationStep); + if (smoothSnowAccumulationStep > 7) { + smoothSnowAccumulationStep = 7; + log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to above maximum allowed value of 7"); + log(Level.WARNING, "Using value of 7 to prevent issues"); + } else if (smoothSnowAccumulationStep < 0) { + smoothSnowAccumulationStep = 0; + log(Level.WARNING, "blocks.snow.smooth-accumulation-step is set to below minimum allowed value of 0"); + log(Level.WARNING, "Using value of 0 to prevent issues"); + } + lightningRodRange = getInt("settings.blocks.lightning_rod.range", lightningRodRange); + ArrayList defaultCurses = new ArrayList<>(){{ + add("minecraft:binding_curse"); + add("minecraft:vanishing_curse"); + }}; + if (version < 24 && !getBoolean("settings.blocks.grindstone.ignore-curses", true)) { + defaultCurses.clear(); + } + getList("settings.blocks.grindstone.ignored-enchants", defaultCurses).forEach(key -> { + Registry registry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + Enchantment enchantment = registry.getValue(Identifier.parse(key.toString())); + if (enchantment == null) return; + grindstoneIgnoredEnchants.add(enchantment); + }); + grindstoneRemoveAttributes = getBoolean("settings.blocks.grindstone.remove-attributes", grindstoneRemoveAttributes); + grindstoneRemoveDisplay = getBoolean("settings.blocks.grindstone.remove-name-and-lore", grindstoneRemoveDisplay); + caveVinesMaxGrowthAge = getInt("settings.blocks.cave_vines.max-growth-age", caveVinesMaxGrowthAge); + if (caveVinesMaxGrowthAge > 25) { + caveVinesMaxGrowthAge = 25; + log(Level.WARNING, "blocks.cave_vines.max-growth-age is set to above maximum allowed value of 25"); + log(Level.WARNING, "Using value of 25 to prevent issues"); + } + kelpMaxGrowthAge = getInt("settings.blocks.kelp.max-growth-age", kelpMaxGrowthAge); + if (kelpMaxGrowthAge > 25) { + kelpMaxGrowthAge = 25; + log(Level.WARNING, "blocks.kelp.max-growth-age is set to above maximum allowed value of 25"); + log(Level.WARNING, "Using value of 25 to prevent issues"); + } + twistingVinesMaxGrowthAge = getInt("settings.blocks.twisting_vines.max-growth-age", twistingVinesMaxGrowthAge); + if (twistingVinesMaxGrowthAge > 25) { + twistingVinesMaxGrowthAge = 25; + log(Level.WARNING, "blocks.twisting_vines.max-growth-age is set to above maximum allowed value of 25"); + log(Level.WARNING, "Using value of 25 to prevent issues"); + } + weepingVinesMaxGrowthAge = getInt("settings.blocks.weeping_vines.max-growth-age", weepingVinesMaxGrowthAge); + if (weepingVinesMaxGrowthAge > 25) { + weepingVinesMaxGrowthAge = 25; + log(Level.WARNING, "blocks.weeping_vines.max-growth-age is set to above maximum allowed value of 25"); + log(Level.WARNING, "Using value of 25 to prevent issues"); + } + magmaBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.magma-block.reverse-bubble-column-flow", magmaBlockReverseBubbleColumnFlow); + soulSandBlockReverseBubbleColumnFlow = getBoolean("settings.blocks.soul-sand.reverse-bubble-column-flow", soulSandBlockReverseBubbleColumnFlow); + } + + public static boolean allowInapplicableEnchants = false; + public static boolean allowIncompatibleEnchants = false; + public static boolean allowHigherEnchantsLevels = false; + public static boolean allowUnsafeEnchantCommand = false; + public static boolean replaceIncompatibleEnchants = false; + public static boolean clampEnchantLevels = true; + private static void enchantmentSettings() { + if (version < 30) { + boolean oldValue = getBoolean("settings.enchantment.allow-unsafe-enchants", false); + set("settings.enchantment.anvil.allow-unsafe-enchants", oldValue); + set("settings.enchantment.anvil.allow-inapplicable-enchants", true); + set("settings.enchantment.anvil.allow-incompatible-enchants", true); + set("settings.enchantment.anvil.allow-higher-enchants-levels", true); + set("settings.enchantment.allow-unsafe-enchants", null); + } + if (version < 37) { + boolean allowUnsafeEnchants = getBoolean("settings.enchantment.anvil.allow-unsafe-enchants", false); + if (!allowUnsafeEnchants) { + set("settings.enchantment.anvil.allow-inapplicable-enchants", false); + set("settings.enchantment.anvil.allow-incompatible-enchants", false); + set("settings.enchantment.anvil.allow-higher-enchants-levels", false); + } + set("settings.enchantment.anvil.allow-unsafe-enchants", null); + } + allowInapplicableEnchants = getBoolean("settings.enchantment.anvil.allow-inapplicable-enchants", allowInapplicableEnchants); + allowIncompatibleEnchants = getBoolean("settings.enchantment.anvil.allow-incompatible-enchants", allowIncompatibleEnchants); + allowHigherEnchantsLevels = getBoolean("settings.enchantment.anvil.allow-higher-enchants-levels", allowHigherEnchantsLevels); + allowUnsafeEnchantCommand = getBoolean("settings.enchantment.allow-unsafe-enchant-command", allowUnsafeEnchantCommand); + replaceIncompatibleEnchants = getBoolean("settings.enchantment.anvil.replace-incompatible-enchants", replaceIncompatibleEnchants); + clampEnchantLevels = getBoolean("settings.enchantment.clamp-levels", clampEnchantLevels); + } + + public static boolean endermanShortHeight = false; + private static void entitySettings() { + endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight); + if (endermanShortHeight) EntityType.ENDERMAN.dimensions = EntityDimensions.scalable(0.6F, 1.9F); + } + + public static boolean allowWaterPlacementInTheEnd = true; + private static void allowWaterPlacementInEnd() { + allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd); + } + + public static boolean beeCountPayload = false; + private static void beeCountPayload() { + beeCountPayload = getBoolean("settings.bee-count-payload", beeCountPayload); + } + + public static boolean loggerSuppressInitLegacyMaterialError = false; + public static boolean loggerSuppressIgnoredAdvancementWarnings = false; + public static boolean loggerSuppressUnrecognizedRecipeErrors = false; + public static boolean loggerSuppressSetBlockFarChunk = false; + public static boolean loggerSuppressLibraryLoader = false; + private static void loggerSettings() { + loggerSuppressInitLegacyMaterialError = getBoolean("settings.logger.suppress-init-legacy-material-errors", loggerSuppressInitLegacyMaterialError); + loggerSuppressIgnoredAdvancementWarnings = getBoolean("settings.logger.suppress-ignored-advancement-warnings", loggerSuppressIgnoredAdvancementWarnings); + loggerSuppressUnrecognizedRecipeErrors = getBoolean("settings.logger.suppress-unrecognized-recipe-errors", loggerSuppressUnrecognizedRecipeErrors); + loggerSuppressSetBlockFarChunk = getBoolean("settings.logger.suppress-setblock-in-far-chunk-errors", loggerSuppressSetBlockFarChunk); + loggerSuppressLibraryLoader = getBoolean("settings.logger.suppress-library-loader", loggerSuppressLibraryLoader); + org.bukkit.plugin.java.JavaPluginLoader.SuppressLibraryLoaderLogger = loggerSuppressLibraryLoader; + } + + public static boolean tpsCatchup = true; + private static void tpsCatchup() { + tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup); + } + + public static boolean useUPnP = false; + public static boolean maxJoinsPerSecond = false; + public static boolean kickForOutOfOrderChat = true; + private static void networkSettings() { + useUPnP = getBoolean("settings.network.upnp-port-forwarding", useUPnP); + maxJoinsPerSecond = getBoolean("settings.network.max-joins-per-second", maxJoinsPerSecond); + kickForOutOfOrderChat = getBoolean("settings.network.kick-for-out-of-order-chat", kickForOutOfOrderChat); + } + + public static Pattern usernameValidCharactersPattern; + private static void usernameValidationSettings() { + String defaultPattern = "^[a-zA-Z0-9_.]*$"; + String setPattern = getString("settings.username-valid-characters", defaultPattern); + usernameValidCharactersPattern = Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern); + } + + public static boolean fixProjectileLootingTransfer = false; + private static void fixProjectileLootingTransfer() { + fixProjectileLootingTransfer = getBoolean("settings.fix-projectile-looting-transfer", fixProjectileLootingTransfer); + } + + public static boolean clampAttributes = true; + private static void clampAttributes() { + clampAttributes = getBoolean("settings.clamp-attributes", clampAttributes); + } + + public static boolean limitArmor = true; + private static void limitArmor() { + limitArmor = getBoolean("settings.limit-armor", limitArmor); + } + + private static void blastResistanceSettings() { + getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { + log(Level.SEVERE, "Invalid block for `settings.blast-resistance-overrides`: " + blockId); + return; + } + if (!(value instanceof Number blastResistance)) { + log(Level.SEVERE, "Invalid blast resistance for `settings.blast-resistance-overrides." + blockId + "`: " + value); + return; + } + block.explosionResistance = blastResistance.floatValue(); + }); + } + private static void blockFallMultiplierSettings() { + getMap("settings.block-fall-multipliers", Map.ofEntries( + Map.entry("minecraft:hay_block", Map.of("damage", 0.2F)), + Map.entry("minecraft:white_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:light_gray_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:gray_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:black_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:brown_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:pink_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:red_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:orange_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:yellow_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:green_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:lime_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:cyan_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:light_blue_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:blue_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:purple_bed", Map.of("distance", 0.5F)), + Map.entry("minecraft:magenta_bed", Map.of("distance", 0.5F)) + )).forEach((blockId, value) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { + log(Level.SEVERE, "Invalid block for `settings.block-fall-multipliers`: " + blockId); + return; + } + if (!(value instanceof Map map)) { + log(Level.SEVERE, "Invalid fall multiplier for `settings.block-fall-multipliers." + blockId + "`: " + value + + ", expected a map with keys `damage` and `distance` to floats."); + return; + } + Object rawFallDamageMultiplier = map.get("damage"); + if (rawFallDamageMultiplier == null) rawFallDamageMultiplier = 1F; + if (!(rawFallDamageMultiplier instanceof Number fallDamageMultiplier)) { + log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".damage`: " + map.get("damage")); + return; + } + Object rawFallDistanceMultiplier = map.get("distance"); + if (rawFallDistanceMultiplier == null) rawFallDistanceMultiplier = 1F; + if (!(rawFallDistanceMultiplier instanceof Number fallDistanceMultiplier)) { + log(Level.SEVERE, "Invalid multiplier for `settings.block-fall-multipliers." + blockId + ".distance`: " + map.get("distance")); + return; + } + block.fallDamageMultiplier = fallDamageMultiplier.floatValue(); + block.fallDistanceMultiplier = fallDistanceMultiplier.floatValue(); + }); + } + + public static boolean playerDeathsAlwaysShowItem = false; + private static void playerDeathsAlwaysShowItem() { + playerDeathsAlwaysShowItem = getBoolean("settings.player-deaths-always-show-item", playerDeathsAlwaysShowItem); + } + + public static boolean registerMinecraftDebugCommands = false; + private static void registerMinecraftDebugCommands() { + registerMinecraftDebugCommands = getBoolean("settings.register-minecraft-debug-commands", registerMinecraftDebugCommands); + } + + public static boolean registerMinecraftDisabledCommands = false; + private static void registerMinecraftDisabledCommands() { + registerMinecraftDisabledCommands = getBoolean("settings.register-minecraft-disabled-commands", registerMinecraftDebugCommands); + } + + public static List startupCommands = new ArrayList<>(); + private static void startupCommands() { + startupCommands.clear(); + getList("settings.startup-commands", new ArrayList()).forEach(line -> { + String command = line.toString(); + if (command.startsWith("/")) { + command = command.substring(1); + } + startupCommands.add(command); + }); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java new file mode 100644 index 000000000..ed8131016 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -0,0 +1,3779 @@ +package org.purpurmc.purpur; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; +import java.util.logging.Level; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.properties.Tilt; +import org.apache.commons.lang3.BooleanUtils; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import java.util.List; +import java.util.Map; +import org.purpurmc.purpur.tool.Flattenable; +import org.purpurmc.purpur.tool.Strippable; +import org.purpurmc.purpur.tool.Tillable; +import org.purpurmc.purpur.tool.Waxable; +import org.purpurmc.purpur.tool.Weatherable; + +import static org.purpurmc.purpur.PurpurConfig.log; + +@SuppressWarnings("unused") +public class PurpurWorldConfig { + + private final String worldName; + private final World.Environment environment; + + public PurpurWorldConfig(String worldName, World.Environment environment) { + this.worldName = worldName; + this.environment = environment; + init(); + } + + public void init() { + log("-------- World Settings For [" + worldName + "] --------"); + PurpurConfig.readConfig(PurpurWorldConfig.class, this); + } + + private void set(String path, Object val) { + if (PurpurConfig.config.get("world-settings.default." + path) == null || val == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, val); + PurpurConfig.config.set("world-settings.default." + path, val); + } + if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) { + PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val); + PurpurConfig.config.set("world-settings." + worldName + "." + path, val); + } + } + + private ConfigurationSection getConfigurationSection(String path) { + ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path); + return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path); + } + + private String getString(String path, String def) { + if (PurpurConfig.config.get("world-settings.default." + path) == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, def); + } + return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path)); + } + + private boolean getBoolean(String path, boolean def) { + if (PurpurConfig.config.get("world-settings.default." + path) == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, def); + } + return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path)); + } + + private boolean getBoolean(String path, Predicate predicate) { + String val = getString(path, "default").toLowerCase(); + Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default"); + return predicate.test(bool); + } + + private Boolean getBooleanOrDefault(String path, Boolean def) { + String val = getString(path, BooleanUtils.toString(def, "true", "false", "default")).toLowerCase(); + return BooleanUtils.toBooleanObject(val, "true", "false", "default"); + } + + private double getDouble(String path, double def) { + if (PurpurConfig.config.get("world-settings.default." + path) == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, def); + } + return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path)); + } + + private int getInt(String path, int def) { + if (PurpurConfig.config.get("world-settings.default." + path) == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, def); + } + return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path)); + } + + private List getList(String path, T def) { + if (PurpurConfig.config.get("world-settings.default." + path) == null) { + PurpurConfig.config.addDefault("world-settings.default." + path, def); + } + return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path)); + } + + private Map getMap(String path, Map def) { + final Map fallback = PurpurConfig.getMap("world-settings.default." + path, def); + final Map value = PurpurConfig.getMap("world-settings." + worldName + "." + path, null); + return value.isEmpty() ? fallback : value; + } + + public float armorstandStepHeight = 0.0F; + public boolean armorstandSetNameVisible = false; + public boolean armorstandFixNametags = false; + public boolean armorstandMovement = true; + public boolean armorstandWaterMovement = true; + public boolean armorstandWaterFence = true; + public boolean armorstandPlaceWithArms = false; + private void armorstandSettings() { + armorstandStepHeight = (float) getDouble("gameplay-mechanics.armorstand.step-height", armorstandStepHeight); + armorstandSetNameVisible = getBoolean("gameplay-mechanics.armorstand.set-name-visible-when-placing-with-custom-name", armorstandSetNameVisible); + armorstandFixNametags = getBoolean("gameplay-mechanics.armorstand.fix-nametags", armorstandFixNametags); + armorstandMovement = getBoolean("gameplay-mechanics.armorstand.can-movement-tick", armorstandMovement); + armorstandWaterMovement = getBoolean("gameplay-mechanics.armorstand.can-move-in-water", armorstandWaterMovement); + armorstandWaterFence = getBoolean("gameplay-mechanics.armorstand.can-move-in-water-over-fence", armorstandWaterFence); + armorstandPlaceWithArms = getBoolean("gameplay-mechanics.armorstand.place-with-arms-visible", armorstandPlaceWithArms); + } + + public boolean arrowMovementResetsDespawnCounter = true; + private void arrowSettings() { + arrowMovementResetsDespawnCounter = getBoolean("gameplay-mechanics.arrow.movement-resets-despawn-counter", arrowMovementResetsDespawnCounter); + } + + public boolean useBetterMending = false; + public boolean alwaysTameInCreative = false; + public boolean boatEjectPlayersOnLand = false; + public boolean boatsDoFallDamage = false; + public boolean disableDropsOnCrammingDeath = false; + public boolean milkCuresBadOmen = true; + public double tridentLoyaltyVoidReturnHeight = 0.0D; + public boolean entitiesCanUsePortals = true; + public int raidCooldownSeconds = 0; + public int animalBreedingCooldownSeconds = 0; + public boolean persistentDroppableEntityDisplayNames = true; + public Boolean entitiesPickUpLootMobGriefingOverride = null; + public Boolean fireballsMobGriefingOverride = null; + public Boolean projectilesMobGriefingOverride = null; + public boolean noteBlockIgnoreAbove = false; + public boolean imposeTeleportRestrictionsOnGateways = false; + public boolean imposeTeleportRestrictionsOnNetherPortals = false; + public boolean imposeTeleportRestrictionsOnEndPortals = false; + public boolean tickFluids = true; + public double mobsBlindnessMultiplier = 1; + public boolean mobsIgnoreRails = false; + public boolean rainStopsAfterSleep = true; + public boolean thunderStopsAfterSleep = true; + public boolean persistentTileEntityLore = false; + public boolean persistentTileEntityDisplayName = true; + public int mobLastHurtByPlayerTime = 100; + public boolean milkClearsBeneficialEffects = true; + public boolean disableOxidationProximityPenalty = false; + public boolean lightningTransformsBlocks = false; + public boolean lightningTurnsNearbySandIntoGlass = true; + public int lightningTurnsNearbySandIntoGlassMaxDepth = 3; + public int lightningTurnsNearbySandIntoGlassMaxIterations = 6; + public boolean lightningTurnsSandIntoGlass = true; + public boolean lightningTurnsWaterIntoStone = true; + public boolean lightningTurnsStoneIntoObsidian = true; + private void miscGameplayMechanicsSettings() { + useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); + alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); + boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); + boatsDoFallDamage = getBoolean("gameplay-mechanics.boat.do-fall-damage", boatsDoFallDamage); + disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath); + milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); + tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); + entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); + raidCooldownSeconds = getInt("gameplay-mechanics.raid-cooldown-seconds", raidCooldownSeconds); + animalBreedingCooldownSeconds = getInt("gameplay-mechanics.animal-breeding-cooldown-seconds", animalBreedingCooldownSeconds); + persistentDroppableEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-droppable-entity-display-names", persistentDroppableEntityDisplayNames); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", false); + set("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", null); + set("gameplay-mechanics.entities-pick-up-loot-mob-griefing-override", oldVal ? true : "default"); + boolean oldVal2 = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", false); + set("gameplay-mechanics.fireballs-bypass-mob-griefing", null); + set("gameplay-mechanics.fireballs-mob-griefing-override", oldVal2 ? true : "default"); + boolean oldVal3 = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", false); + set("gameplay-mechanics.projectiles-bypass-mob-griefing", null); + set("gameplay-mechanics.projectiles-mob-griefing-override", oldVal3 ? true : "default"); + } + entitiesPickUpLootMobGriefingOverride = getBooleanOrDefault("gameplay-mechanics.entities-pick-up-loot-mob-griefing-override", entitiesPickUpLootMobGriefingOverride); + fireballsMobGriefingOverride = getBooleanOrDefault("gameplay-mechanics.fireballs-mob-griefing-override", fireballsMobGriefingOverride); + projectilesMobGriefingOverride = getBooleanOrDefault("gameplay-mechanics.projectiles-mob-griefing-override", projectilesMobGriefingOverride); + noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); + imposeTeleportRestrictionsOnGateways = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-gateways", imposeTeleportRestrictionsOnGateways); + imposeTeleportRestrictionsOnNetherPortals = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-nether-portals", imposeTeleportRestrictionsOnNetherPortals); + imposeTeleportRestrictionsOnEndPortals = getBoolean("gameplay-mechanics.impose-teleport-restrictions-on-end-portals", imposeTeleportRestrictionsOnEndPortals); + tickFluids = getBoolean("gameplay-mechanics.tick-fluids", tickFluids); + mobsBlindnessMultiplier = getDouble("gameplay-mechanics.entity-blindness-multiplier", mobsBlindnessMultiplier); + mobsIgnoreRails = getBoolean("gameplay-mechanics.mobs-ignore-rails", mobsIgnoreRails); + rainStopsAfterSleep = getBoolean("gameplay-mechanics.rain-stops-after-sleep", rainStopsAfterSleep); + thunderStopsAfterSleep = getBoolean("gameplay-mechanics.thunder-stops-after-sleep", thunderStopsAfterSleep); + if (PurpurConfig.version < 35) { + boolean oldVal = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityLore); + set("gameplay-mechanics.persistent-tileentity-display-names-and-lore", null); + set("gameplay-mechanics.persistent-tileentity-lore", oldVal); + set("gameplay-mechanics.persistent-tileentity-display-name", !oldVal); + } + persistentTileEntityLore = getBoolean("gameplay-mechanics.persistent-tileentity-lore", persistentTileEntityLore); + persistentTileEntityDisplayName = getBoolean("gameplay-mechanics.persistent-tileentity-display-name", persistentTileEntityDisplayName); + mobLastHurtByPlayerTime = getInt("gameplay-mechanics.mob-last-hurt-by-player-time", mobLastHurtByPlayerTime); + milkClearsBeneficialEffects = getBoolean("gameplay-mechanics.milk-clears-beneficial-effects", milkClearsBeneficialEffects); + disableOxidationProximityPenalty = getBoolean("gameplay-mechanics.disable-oxidation-proximity-penalty", disableOxidationProximityPenalty); + lightningTransformsBlocks = getBoolean("gameplay-mechanics.lightning-transforms-blocks.enabled", lightningTransformsBlocks); + lightningTurnsNearbySandIntoGlass = getBoolean("gameplay-mechanics.lightning-transforms-blocks.nearby-sand-into-glass.enabled", lightningTurnsNearbySandIntoGlass); + lightningTurnsNearbySandIntoGlassMaxDepth = getInt("gameplay-mechanics.lightning-transforms-blocks.nearby-sand-into-glass.max-depth", lightningTurnsNearbySandIntoGlassMaxDepth); + lightningTurnsNearbySandIntoGlassMaxIterations = getInt("gameplay-mechanics.lightning-transforms-blocks.nearby-sand-into-glass.max-iteration", lightningTurnsNearbySandIntoGlassMaxIterations); + lightningTurnsSandIntoGlass = getBoolean("gameplay-mechanics.lightning-transforms-blocks.sand-into-glass", lightningTurnsSandIntoGlass); + lightningTurnsWaterIntoStone = getBoolean("gameplay-mechanics.lightning-transforms-blocks.water-into-stone", lightningTurnsWaterIntoStone); + lightningTurnsStoneIntoObsidian = getBoolean("gameplay-mechanics.lightning-transforms-blocks.stone-into-obsidian", lightningTurnsStoneIntoObsidian); + } + + public int daytimeTicks = 12000; + public int nighttimeTicks = 12000; + private void daytimeCycleSettings() { + daytimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.daytime", daytimeTicks); + nighttimeTicks = getInt("gameplay-mechanics.daylight-cycle-ticks.nighttime", nighttimeTicks); + } + + public int drowningAirTicks = 300; + public int drowningDamageInterval = 20; + public double damageFromDrowning = 2.0F; + private void drowningSettings() { + drowningAirTicks = getInt("gameplay-mechanics.drowning.air-ticks", drowningAirTicks); + drowningDamageInterval = getInt("gameplay-mechanics.drowning.ticks-per-damage", drowningDamageInterval); + damageFromDrowning = getDouble("gameplay-mechanics.drowning.damage-from-drowning", damageFromDrowning); + } + + public int elytraDamagePerSecond = 1; + public double elytraDamageMultiplyBySpeed = 0; + public int elytraDamagePerFireworkBoost = 0; + public int elytraDamagePerTridentBoost = 0; + public boolean elytraKineticDamage = true; + private void elytraSettings() { + elytraDamagePerSecond = getInt("gameplay-mechanics.elytra.damage-per-second", elytraDamagePerSecond); + elytraDamageMultiplyBySpeed = getDouble("gameplay-mechanics.elytra.damage-multiplied-by-speed", elytraDamageMultiplyBySpeed); + elytraDamagePerFireworkBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.firework", elytraDamagePerFireworkBoost); + elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost); + elytraKineticDamage = getBoolean("gameplay-mechanics.elytra.kinetic-damage", elytraKineticDamage); + } + + public int entityLifeSpan = 0; + public float entityLeftHandedChance = 0.05f; + public boolean entitySharedRandom = true; + private void entitySettings() { + entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); + entityLeftHandedChance = (float) getDouble("gameplay-mechanics.entity-left-handed-chance", entityLeftHandedChance); + entitySharedRandom = getBoolean("settings.entity.shared-random", entitySharedRandom); + } + + public boolean infinityWorksWithoutArrows = false; + private void infinityArrowsSettings() { + infinityWorksWithoutArrows = getBoolean("gameplay-mechanics.infinity-bow.works-without-arrows", infinityWorksWithoutArrows); + } + + public List itemImmuneToCactus = new ArrayList<>(); + public List itemImmuneToExplosion = new ArrayList<>(); + public List itemImmuneToFire = new ArrayList<>(); + public List itemImmuneToLightning = new ArrayList<>(); + public boolean dontRunWithScissors = false; + public Identifier dontRunWithScissorsItemModelReference = Identifier.parse("purpurmc:scissors"); + public boolean ignoreScissorsInWater = false; + public boolean ignoreScissorsInLava = false; + public double scissorsRunningDamage = 1D; + public float enderPearlDamage = 5.0F; + public int enderPearlCooldown = 20; + public int enderPearlCooldownCreative = 20; + public float enderPearlEndermiteChance = 0.05F; + public int glowBerriesEatGlowDuration = 0; + public boolean shulkerBoxItemDropContentsWhenDestroyed = true; + public boolean compassItemShowsBossBar = false; + public boolean snowballExtinguishesFire = false; + public boolean snowballExtinguishesCandles = false; + public boolean snowballExtinguishesCampfires = false; + private void itemSettings() { + itemImmuneToCactus.clear(); + getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> { + if (key.toString().equals("*")) { + BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToCactus.add(item)); + return; + } + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(key.toString())); + if (item != Items.AIR) itemImmuneToCactus.add(item); + }); + itemImmuneToExplosion.clear(); + getList("gameplay-mechanics.item.immune.explosion", new ArrayList<>()).forEach(key -> { + if (key.toString().equals("*")) { + BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToExplosion.add(item)); + return; + } + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(key.toString())); + if (item != Items.AIR) itemImmuneToExplosion.add(item); + }); + itemImmuneToFire.clear(); + getList("gameplay-mechanics.item.immune.fire", new ArrayList<>()).forEach(key -> { + if (key.toString().equals("*")) { + BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToFire.add(item)); + return; + } + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(key.toString())); + if (item != Items.AIR) itemImmuneToFire.add(item); + }); + itemImmuneToLightning.clear(); + getList("gameplay-mechanics.item.immune.lightning", new ArrayList<>()).forEach(key -> { + if (key.toString().equals("*")) { + BuiltInRegistries.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToLightning.add(item)); + return; + } + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(key.toString())); + if (item != Items.AIR) itemImmuneToLightning.add(item); + }); + dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors); + dontRunWithScissorsItemModelReference = Identifier.parse(getString("gameplay-mechanics.item.shears.damage-if-sprinting-item-model", "purpurmc:scissors")); + ignoreScissorsInWater = getBoolean("gameplay-mechanics.item.shears.ignore-in-water", ignoreScissorsInWater); + ignoreScissorsInLava = getBoolean("gameplay-mechanics.item.shears.ignore-in-lava", ignoreScissorsInLava); + scissorsRunningDamage = getDouble("gameplay-mechanics.item.shears.sprinting-damage", scissorsRunningDamage); + enderPearlDamage = (float) getDouble("gameplay-mechanics.item.ender-pearl.damage", enderPearlDamage); + enderPearlCooldown = getInt("gameplay-mechanics.item.ender-pearl.cooldown", enderPearlCooldown); + enderPearlCooldownCreative = getInt("gameplay-mechanics.item.ender-pearl.creative-cooldown", enderPearlCooldownCreative); + enderPearlEndermiteChance = (float) getDouble("gameplay-mechanics.item.ender-pearl.endermite-spawn-chance", enderPearlEndermiteChance); + glowBerriesEatGlowDuration = getInt("gameplay-mechanics.item.glow_berries.eat-glow-duration", glowBerriesEatGlowDuration); + shulkerBoxItemDropContentsWhenDestroyed = getBoolean("gameplay-mechanics.item.shulker_box.drop-contents-when-destroyed", shulkerBoxItemDropContentsWhenDestroyed); + compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar); + snowballExtinguishesFire = getBoolean("gameplay-mechanics.item.snowball.extinguish.fire", snowballExtinguishesFire); + snowballExtinguishesCandles = getBoolean("gameplay-mechanics.item.snowball.extinguish.candles", snowballExtinguishesCandles); + snowballExtinguishesCampfires = getBoolean("gameplay-mechanics.item.snowball.extinguish.campfires", snowballExtinguishesCampfires); + } + + public double minecartMaxSpeed = 0.4D; + public boolean minecartPlaceAnywhere = false; + public boolean minecartControllable = false; + public float minecartControllableStepHeight = 1.0F; + public double minecartControllableHopBoost = 0.5D; + public boolean minecartControllableFallDamage = true; + public double minecartControllableBaseSpeed = 0.1D; + public Map minecartControllableBlockSpeeds = new HashMap<>(); + public double poweredRailBoostModifier = 0.06; + private void minecartSettings() { + if (PurpurConfig.version < 12) { + boolean oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.place-anywhere", minecartPlaceAnywhere); + set("gameplay-mechanics.controllable-minecarts.place-anywhere", null); + set("gameplay-mechanics.minecart.place-anywhere", oldBool); + oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.enabled", minecartControllable); + set("gameplay-mechanics.controllable-minecarts.enabled", null); + set("gameplay-mechanics.minecart.controllable.enabled", oldBool); + double oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.step-height", minecartControllableStepHeight); + set("gameplay-mechanics.controllable-minecarts.step-height", null); + set("gameplay-mechanics.minecart.controllable.step-height", oldDouble); + oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.hop-boost", minecartControllableHopBoost); + set("gameplay-mechanics.controllable-minecarts.hop-boost", null); + set("gameplay-mechanics.minecart.controllable.hop-boost", oldDouble); + oldBool = getBoolean("gameplay-mechanics.controllable-minecarts.fall-damage", minecartControllableFallDamage); + set("gameplay-mechanics.controllable-minecarts.fall-damage", null); + set("gameplay-mechanics.minecart.controllable.fall-damage", oldBool); + oldDouble = getDouble("gameplay-mechanics.controllable-minecarts.base-speed", minecartControllableBaseSpeed); + set("gameplay-mechanics.controllable-minecarts.base-speed", null); + set("gameplay-mechanics.minecart.controllable.base-speed", oldDouble); + ConfigurationSection section = getConfigurationSection("gameplay-mechanics.controllable-minecarts.block-speed"); + if (section != null) { + for (String key : section.getKeys(false)) { + if ("grass-block".equals(key)) key = "grass_block"; // oopsie + oldDouble = section.getDouble(key, minecartControllableBaseSpeed); + set("gameplay-mechanics.controllable-minecarts.block-speed." + key, null); + set("gameplay-mechanics.minecart.controllable.block-speed." + key, oldDouble); + } + set("gameplay-mechanics.controllable-minecarts.block-speed", null); + } + set("gameplay-mechanics.controllable-minecarts", null); + } + + minecartMaxSpeed = getDouble("gameplay-mechanics.minecart.max-speed", minecartMaxSpeed); + minecartPlaceAnywhere = getBoolean("gameplay-mechanics.minecart.place-anywhere", minecartPlaceAnywhere); + minecartControllable = getBoolean("gameplay-mechanics.minecart.controllable.enabled", minecartControllable); + minecartControllableStepHeight = (float) getDouble("gameplay-mechanics.minecart.controllable.step-height", minecartControllableStepHeight); + minecartControllableHopBoost = getDouble("gameplay-mechanics.minecart.controllable.hop-boost", minecartControllableHopBoost); + minecartControllableFallDamage = getBoolean("gameplay-mechanics.minecart.controllable.fall-damage", minecartControllableFallDamage); + minecartControllableBaseSpeed = getDouble("gameplay-mechanics.minecart.controllable.base-speed", minecartControllableBaseSpeed); + ConfigurationSection section = getConfigurationSection("gameplay-mechanics.minecart.controllable.block-speed"); + if (section != null) { + for (String key : section.getKeys(false)) { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(key)); + if (block != Blocks.AIR) { + minecartControllableBlockSpeeds.put(block, section.getDouble(key, minecartControllableBaseSpeed)); + } + } + } else { + set("gameplay-mechanics.minecart.controllable.block-speed.grass_block", 0.3D); + set("gameplay-mechanics.minecart.controllable.block-speed.stone", 0.5D); + } + poweredRailBoostModifier = getDouble("gameplay-mechanics.minecart.powered-rail.boost-modifier", poweredRailBoostModifier); + } + + public float entityHealthRegenAmount = 1.0F; + public float entityMinimalHealthPoison = 1.0F; + public float entityPoisonDegenerationAmount = 1.0F; + public float entityWitherDegenerationAmount = 1.0F; + public float humanHungerExhaustionAmount = 0.005F; + public float humanSaturationRegenAmount = 1.0F; + private void mobEffectSettings() { + entityHealthRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.health-regen-amount", entityHealthRegenAmount); + entityMinimalHealthPoison = (float) getDouble("gameplay-mechanics.mob-effects.minimal-health-poison-amount", entityMinimalHealthPoison); + entityPoisonDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.poison-degeneration-amount", entityPoisonDegenerationAmount); + entityWitherDegenerationAmount = (float) getDouble("gameplay-mechanics.mob-effects.wither-degeneration-amount", entityWitherDegenerationAmount); + humanHungerExhaustionAmount = (float) getDouble("gameplay-mechanics.mob-effects.hunger-exhaustion-amount", humanHungerExhaustionAmount); + humanSaturationRegenAmount = (float) getDouble("gameplay-mechanics.mob-effects.saturation-regen-amount", humanSaturationRegenAmount); + } + + public boolean catSpawning; + public boolean patrolSpawning; + public boolean phantomSpawning; + public boolean villagerTraderSpawning; + public boolean villageSiegeSpawning; + public boolean mobSpawningIgnoreCreativePlayers = false; + private void mobSpawnerSettings() { + // values of "default" or null will default to true only if the world environment is normal (aka overworld) + Predicate predicate = (bool) -> (bool != null && bool) || (bool == null && environment == World.Environment.NORMAL); + catSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-cats", predicate); + patrolSpawning = getBoolean("gameplay-mechanics.mob-spawning.raid-patrols", predicate); + phantomSpawning = getBoolean("gameplay-mechanics.mob-spawning.phantoms", predicate); + villagerTraderSpawning = getBoolean("gameplay-mechanics.mob-spawning.wandering-traders", predicate); + villageSiegeSpawning = getBoolean("gameplay-mechanics.mob-spawning.village-sieges", predicate); + mobSpawningIgnoreCreativePlayers = getBoolean("gameplay-mechanics.mob-spawning.ignore-creative-players", mobSpawningIgnoreCreativePlayers); + } + + public boolean disableObserverClocks = false; + private void observerSettings() { + disableObserverClocks = getBoolean("blocks.observer.disable-clock", disableObserverClocks); + } + + public int playerNetheriteFireResistanceDuration = 0; + public int playerNetheriteFireResistanceAmplifier = 0; + public boolean playerNetheriteFireResistanceAmbient = false; + public boolean playerNetheriteFireResistanceShowParticles = false; + public boolean playerNetheriteFireResistanceShowIcon = true; + private void playerNetheriteFireResistance() { + playerNetheriteFireResistanceDuration = getInt("gameplay-mechanics.player.netherite-fire-resistance.duration", playerNetheriteFireResistanceDuration); + playerNetheriteFireResistanceAmplifier = getInt("gameplay-mechanics.player.netherite-fire-resistance.amplifier", playerNetheriteFireResistanceAmplifier); + playerNetheriteFireResistanceAmbient = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.ambient", playerNetheriteFireResistanceAmbient); + playerNetheriteFireResistanceShowParticles = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-particles", playerNetheriteFireResistanceShowParticles); + playerNetheriteFireResistanceShowIcon = getBoolean("gameplay-mechanics.player.netherite-fire-resistance.show-icon", playerNetheriteFireResistanceShowIcon); + } + + public boolean idleTimeoutKick = true; + public boolean idleTimeoutTickNearbyEntities = true; + public boolean idleTimeoutCountAsSleeping = false; + public boolean idleTimeoutUpdateTabList = false; + public boolean idleTimeoutTargetPlayer = true; + public String playerDeathExpDropEquation = "expLevel * 7"; + public int playerDeathExpDropMax = 100; + public boolean teleportIfOutsideBorder = false; + public boolean teleportOnNetherCeilingDamage = false; + public boolean totemOfUndyingWorksInInventory = false; + public boolean playerFixStuckPortal = false; + public boolean creativeOnePunch = false; + public boolean playerSleepNearMonsters = false; + public boolean playersSkipNight = true; + public double playerCriticalDamageMultiplier = 1.5D; + public int playerBurpDelay = 10; + public boolean playerBurpWhenFull = false; + public boolean playerRidableInWater = false; + public boolean playerRemoveBindingWithWeakness = false; + public int shiftRightClickRepairsMendingPoints = 0; + public int playerExpPickupDelay = 2; + public boolean playerVoidTrading = false; + private void playerSettings() { + if (PurpurConfig.version < 19) { + boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); + set("gameplay-mechanics.player.idle-timeout.mods-target", null); + set("gameplay-mechanics.player.idle-timeout.mobs-target", oldVal); + } + idleTimeoutKick = System.getenv("PURPUR_FORCE_IDLE_KICK") == null ? getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick) : Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")); + idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities); + idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping); + idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList); + idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer); + playerDeathExpDropEquation = getString("gameplay-mechanics.player.exp-dropped-on-death.equation", playerDeathExpDropEquation); + playerDeathExpDropMax = getInt("gameplay-mechanics.player.exp-dropped-on-death.maximum", playerDeathExpDropMax); + teleportIfOutsideBorder = getBoolean("gameplay-mechanics.player.teleport-if-outside-border", teleportIfOutsideBorder); + teleportOnNetherCeilingDamage = getBoolean("gameplay-mechanics.player.teleport-on-nether-ceiling-damage", teleportOnNetherCeilingDamage); + totemOfUndyingWorksInInventory = getBoolean("gameplay-mechanics.player.totem-of-undying-works-in-inventory", totemOfUndyingWorksInInventory); + playerFixStuckPortal = getBoolean("gameplay-mechanics.player.fix-stuck-in-portal", playerFixStuckPortal); + creativeOnePunch = getBoolean("gameplay-mechanics.player.one-punch-in-creative", creativeOnePunch); + playerSleepNearMonsters = getBoolean("gameplay-mechanics.player.sleep-ignore-nearby-mobs", playerSleepNearMonsters); + playersSkipNight = getBoolean("gameplay-mechanics.player.can-skip-night", playersSkipNight); + playerCriticalDamageMultiplier = getDouble("gameplay-mechanics.player.critical-damage-multiplier", playerCriticalDamageMultiplier); + playerBurpDelay = getInt("gameplay-mechanics.player.burp-delay", playerBurpDelay); + playerBurpWhenFull = getBoolean("gameplay-mechanics.player.burp-when-full", playerBurpWhenFull); + playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); + playerRemoveBindingWithWeakness = getBoolean("gameplay-mechanics.player.curse-of-binding.remove-with-weakness", playerRemoveBindingWithWeakness); + shiftRightClickRepairsMendingPoints = getInt("gameplay-mechanics.player.shift-right-click-repairs-mending-points", shiftRightClickRepairsMendingPoints); + playerExpPickupDelay = getInt("gameplay-mechanics.player.exp-pickup-delay-ticks", playerExpPickupDelay); + playerVoidTrading = getBoolean("gameplay-mechanics.player.allow-void-trading", playerVoidTrading); + } + + public boolean silkTouchEnabled = false; + public String silkTouchSpawnerName = "Monster Spawner"; + public List silkTouchSpawnerLore = new ArrayList<>(); + public List silkTouchTools = new ArrayList<>(); + public int minimumSilkTouchSpawnerRequire = 1; + private void silkTouchSettings() { + if (PurpurConfig.version < 21) { + String oldName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName); + set("gameplay-mechanics.silk-touch.spawner-name", "" + ChatColor.toMM(oldName.replace("{mob}", ""))); + List list = new ArrayList<>(); + getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a ")) + .forEach(line -> list.add("" + ChatColor.toMM(line.toString().replace("{mob}", "")))); + set("gameplay-mechanics.silk-touch.spawner-lore", list); + } + silkTouchEnabled = getBoolean("gameplay-mechanics.silk-touch.enabled", silkTouchEnabled); + silkTouchSpawnerName = getString("gameplay-mechanics.silk-touch.spawner-name", silkTouchSpawnerName); + minimumSilkTouchSpawnerRequire = getInt("gameplay-mechanics.silk-touch.minimal-level", minimumSilkTouchSpawnerRequire); + silkTouchSpawnerLore.clear(); + getList("gameplay-mechanics.silk-touch.spawner-lore", List.of("Spawns a ")) + .forEach(line -> silkTouchSpawnerLore.add(line.toString())); + silkTouchTools.clear(); + getList("gameplay-mechanics.silk-touch.tools", List.of( + "minecraft:iron_pickaxe", + "minecraft:golden_pickaxe", + "minecraft:diamond_pickaxe", + "minecraft:netherite_pickaxe" + )).forEach(key -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(key.toString())); + if (item != Items.AIR) silkTouchTools.add(item); + }); + } + + public double bowProjectileOffset = 1.0D; + public double crossbowProjectileOffset = 1.0D; + public double eggProjectileOffset = 1.0D; + public double enderPearlProjectileOffset = 1.0D; + public double throwablePotionProjectileOffset = 1.0D; + public double tridentProjectileOffset = 1.0D; + public double snowballProjectileOffset = 1.0D; + private void projectileOffsetSettings() { + bowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.bow", bowProjectileOffset); + crossbowProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.crossbow", crossbowProjectileOffset); + eggProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.egg", eggProjectileOffset); + enderPearlProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.ender-pearl", enderPearlProjectileOffset); + throwablePotionProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.throwable-potion", throwablePotionProjectileOffset); + tridentProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.trident", tridentProjectileOffset); + snowballProjectileOffset = getDouble("gameplay-mechanics.projectile-offset.snowball", snowballProjectileOffset); + } + + public int snowballDamage = -1; + private void snowballSettings() { + snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage); + } + + public Map axeStrippables = new HashMap<>(); + public Map axeWaxables = new HashMap<>(); + public Map axeWeatherables = new HashMap<>(); + public Map hoeTillables = new HashMap<>(); + public Map shovelFlattenables = new HashMap<>(); + public boolean hoeReplantsCrops = false; + public boolean hoeReplantsNetherWarts = false; + private void toolSettings() { + axeStrippables.clear(); + axeWaxables.clear(); + axeWeatherables.clear(); + hoeTillables.clear(); + shovelFlattenables.clear(); + if (PurpurConfig.version < 18) { + ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + ".tools.hoe.tilling"); + if (section != null) { + PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tillables", section); + PurpurConfig.config.set("world-settings." + worldName + ".tools.hoe.tilling", null); + } + section = PurpurConfig.config.getConfigurationSection("world-settings.default.tools.hoe.tilling"); + if (section != null) { + PurpurConfig.config.set("world-settings.default.tools.hoe.tillables", section); + PurpurConfig.config.set("world-settings.default.tools.hoe.tilling", null); + } + } + if (PurpurConfig.version < 29) { + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap())); + } + if (PurpurConfig.version < 32) { + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap())); + } + if (PurpurConfig.version < 33) { + getList("gameplay-mechanics.shovel-turns-block-to-grass-path", new ArrayList(){{ + add("minecraft:coarse_dirt"); + add("minecraft:dirt"); + add("minecraft:grass_block"); + add("minecraft:mycelium"); + add("minecraft:podzol"); + add("minecraft:rooted_dirt"); + }}).forEach(key -> { + PurpurConfig.config.set("world-settings.default.tools.shovel.flattenables." + key.toString(), Map.of("into", "minecraft:dirt_path", "drops", new HashMap())); + }); + set("gameplay-mechanics.shovel-turns-block-to-grass-path", null); + } + if (PurpurConfig.version < 34) { + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_chiseled_copper", Map.of("into", "minecraft:oxidized_chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_door", Map.of("into", "minecraft:oxidized_copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_trapdoor", Map.of("into", "minecraft:oxidized_copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_grate", Map.of("into", "minecraft:oxidized_copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_bulb", Map.of("into", "minecraft:oxidized_copper_bulb", "drops", new HashMap())); + + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap())); + } + if (PurpurConfig.version < 39) { + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:pale_oak_wood", Map.of("into", "minecraft:stripped_pale_oak_wood", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.strippables.minecraft:pale_oak_log", Map.of("into", "minecraft:stripped_pale_oak_log", "drops", new HashMap())); + } + if (PurpurConfig.version < 44) { + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_chest", Map.of("into", "minecraft:copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_chest", Map.of("into", "minecraft:exposed_copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_chest", Map.of("into", "minecraft:weathered_copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_chest", Map.of("into", "minecraft:oxidized_copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_golem_statue", Map.of("into", "minecraft:copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_golem_statue", Map.of("into", "minecraft:exposed_copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_golem_statue", Map.of("into", "minecraft:weathered_copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_golem_statue", Map.of("into", "minecraft:oxidized_copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_lightning_rod", Map.of("into", "minecraft:lightning_rod", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_lightning_rod", Map.of("into", "minecraft:exposed_lightning_rod", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_lightning_rod", Map.of("into", "minecraft:weathered_lightning_rod", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_lightning_rod", Map.of("into", "minecraft:oxidized_lightning_rod", "drops", new HashMap())); + + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_chest", Map.of("into", "minecraft:copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_chest", Map.of("into", "minecraft:exposed_copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_chest", Map.of("into", "minecraft:weathered_copper_chest", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_golem_statue", Map.of("into", "minecraft:copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_golem_statue", Map.of("into", "minecraft:exposed_copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_golem_statue", Map.of("into", "minecraft:weathered_copper_golem_statue", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_lightning_rod", Map.of("into", "minecraft:lightning_rod", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_lightning_rod", Map.of("into", "minecraft:exposed_lightning_rod", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_lightning_rod", Map.of("into", "minecraft:weathered_lightning_rod", "drops", new HashMap())); + } + if (PurpurConfig.version < 45) { + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_bars", Map.of("into", "minecraft:copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_bars", Map.of("into", "minecraft:exposed_copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_bars", Map.of("into", "minecraft:weathered_copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_bars", Map.of("into", "minecraft:oxidized_copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_chain", Map.of("into", "minecraft:copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_chain", Map.of("into", "minecraft:exposed_copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_chain", Map.of("into", "minecraft:weathered_copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_chain", Map.of("into", "minecraft:oxidized_copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_copper_lantern", Map.of("into", "minecraft:copper_lantern", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_exposed_copper_lantern", Map.of("into", "minecraft:exposed_copper_lantern", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_weathered_copper_lantern", Map.of("into", "minecraft:weathered_copper_lantern", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.waxables.minecraft:waxed_oxidized_copper_lantern", Map.of("into", "minecraft:oxidized_copper_lantern", "drops", new HashMap())); + + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_bars", Map.of("into", "minecraft:copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_bars", Map.of("into", "minecraft:exposed_copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_bars", Map.of("into", "minecraft:weathered_copper_bars", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_chain", Map.of("into", "minecraft:copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_chain", Map.of("into", "minecraft:exposed_copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_chain", Map.of("into", "minecraft:weathered_copper_chain", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:exposed_copper_lantern", Map.of("into", "minecraft:copper_lantern", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:weathered_copper_lantern", Map.of("into", "minecraft:exposed_copper_lantern", "drops", new HashMap())); + PurpurConfig.config.set("world-settings.default.tools.axe.weatherables.minecraft:oxidized_copper_lantern", Map.of("into", "minecraft:weathered_copper_lantern", "drops", new HashMap())); + } + getMap("tools.axe.strippables", Map.ofEntries( + Map.entry("minecraft:oak_wood", Map.of("into", "minecraft:stripped_oak_wood", "drops", new HashMap())), + Map.entry("minecraft:oak_log", Map.of("into", "minecraft:stripped_oak_log", "drops", new HashMap())), + Map.entry("minecraft:dark_oak_wood", Map.of("into", "minecraft:stripped_dark_oak_wood", "drops", new HashMap())), + Map.entry("minecraft:dark_oak_log", Map.of("into", "minecraft:stripped_dark_oak_log", "drops", new HashMap())), + Map.entry("minecraft:pale_oak_wood", Map.of("into", "minecraft:stripped_pale_oak_wood", "drops", new HashMap())), + Map.entry("minecraft:pale_oak_log", Map.of("into", "minecraft:stripped_pale_oak_log", "drops", new HashMap())), + Map.entry("minecraft:acacia_wood", Map.of("into", "minecraft:stripped_acacia_wood", "drops", new HashMap())), + Map.entry("minecraft:acacia_log", Map.of("into", "minecraft:stripped_acacia_log", "drops", new HashMap())), + Map.entry("minecraft:cherry_wood", Map.of("into", "minecraft:stripped_cherry_wood", "drops", new HashMap())), + Map.entry("minecraft:cherry_log", Map.of("into", "minecraft:stripped_cherry_log", "drops", new HashMap())), + Map.entry("minecraft:birch_wood", Map.of("into", "minecraft:stripped_birch_wood", "drops", new HashMap())), + Map.entry("minecraft:birch_log", Map.of("into", "minecraft:stripped_birch_log", "drops", new HashMap())), + Map.entry("minecraft:jungle_wood", Map.of("into", "minecraft:stripped_jungle_wood", "drops", new HashMap())), + Map.entry("minecraft:jungle_log", Map.of("into", "minecraft:stripped_jungle_log", "drops", new HashMap())), + Map.entry("minecraft:spruce_wood", Map.of("into", "minecraft:stripped_spruce_wood", "drops", new HashMap())), + Map.entry("minecraft:spruce_log", Map.of("into", "minecraft:stripped_spruce_log", "drops", new HashMap())), + Map.entry("minecraft:warped_stem", Map.of("into", "minecraft:stripped_warped_stem", "drops", new HashMap())), + Map.entry("minecraft:warped_hyphae", Map.of("into", "minecraft:stripped_warped_hyphae", "drops", new HashMap())), + Map.entry("minecraft:crimson_stem", Map.of("into", "minecraft:stripped_crimson_stem", "drops", new HashMap())), + Map.entry("minecraft:crimson_hyphae", Map.of("into", "minecraft:stripped_crimson_hyphae", "drops", new HashMap())), + Map.entry("minecraft:mangrove_wood", Map.of("into", "minecraft:stripped_mangrove_wood", "drops", new HashMap())), + Map.entry("minecraft:mangrove_log", Map.of("into", "minecraft:stripped_mangrove_log", "drops", new HashMap())), + Map.entry("minecraft:bamboo_block", Map.of("into", "minecraft:stripped_bamboo_block", "drops", new HashMap())) + ) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables`: " + blockId); return; } + if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + "`"); return; } + String intoId = (String) map.get("into"); + Block into = BuiltInRegistries.BLOCK.getValue(Identifier.parse(intoId)); + if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.strippables." + blockId + ".into`: " + intoId); return; } + Object dropsObj = map.get("drops"); + if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.strippables." + blockId + ".drops`"); return; } + Map drops = new HashMap<>(); + dropsMap.forEach((itemId, chance) -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId.toString())); + if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.strippables." + blockId + ".drops`: " + itemId); return; } + drops.put(item, (double) chance); + }); + axeStrippables.put(block, new Strippable(into, drops)); + }); + getMap("tools.axe.waxables", Map.ofEntries( + Map.entry("minecraft:waxed_copper_block", Map.of("into", "minecraft:copper_block", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper", Map.of("into", "minecraft:oxidized_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_cut_copper", Map.of("into", "minecraft:oxidized_cut_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_cut_copper_slab", Map.of("into", "minecraft:oxidized_cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:waxed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_cut_copper_stairs", Map.of("into", "minecraft:oxidized_cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:waxed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_chiseled_copper", Map.of("into", "minecraft:oxidized_chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_door", Map.of("into", "minecraft:oxidized_copper_door", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_trapdoor", Map.of("into", "minecraft:oxidized_copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_grate", Map.of("into", "minecraft:oxidized_copper_grate", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_bulb", Map.of("into", "minecraft:oxidized_copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_chest", Map.of("into", "minecraft:copper_chest", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_chest", Map.of("into", "minecraft:exposed_copper_chest", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_chest", Map.of("into", "minecraft:weathered_copper_chest", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_chest", Map.of("into", "minecraft:oxidized_copper_chest", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_golem_statue", Map.of("into", "minecraft:copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_golem_statue", Map.of("into", "minecraft:exposed_copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_golem_statue", Map.of("into", "minecraft:weathered_copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_golem_statue", Map.of("into", "minecraft:oxidized_copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:waxed_lightning_rod", Map.of("into", "minecraft:lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_lightning_rod", Map.of("into", "minecraft:exposed_lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_lightning_rod", Map.of("into", "minecraft:weathered_lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_lightning_rod", Map.of("into", "minecraft:oxidized_lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_bars", Map.of("into", "minecraft:copper_bars", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_bars", Map.of("into", "minecraft:exposed_copper_bars", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_bars", Map.of("into", "minecraft:weathered_copper_bars", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_bars", Map.of("into", "minecraft:oxidized_copper_bars", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_chain", Map.of("into", "minecraft:copper_chain", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_chain", Map.of("into", "minecraft:exposed_copper_chain", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_chain", Map.of("into", "minecraft:weathered_copper_chain", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_chain", Map.of("into", "minecraft:oxidized_copper_chain", "drops", new HashMap())), + Map.entry("minecraft:waxed_copper_lantern", Map.of("into", "minecraft:copper_lantern", "drops", new HashMap())), + Map.entry("minecraft:waxed_exposed_copper_lantern", Map.of("into", "minecraft:exposed_copper_lantern", "drops", new HashMap())), + Map.entry("minecraft:waxed_weathered_copper_lantern", Map.of("into", "minecraft:weathered_copper_lantern", "drops", new HashMap())), + Map.entry("minecraft:waxed_oxidized_copper_lantern", Map.of("into", "minecraft:oxidized_copper_lantern", "drops", new HashMap()))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables`: " + blockId); return; } + if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + "`"); return; } + String intoId = (String) map.get("into"); + Block into = BuiltInRegistries.BLOCK.getValue(Identifier.parse(intoId)); + if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.waxables." + blockId + ".into`: " + intoId); return; } + Object dropsObj = map.get("drops"); + if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.waxables." + blockId + ".drops`"); return; } + Map drops = new HashMap<>(); + dropsMap.forEach((itemId, chance) -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId.toString())); + if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.waxables." + blockId + ".drops`: " + itemId); return; } + drops.put(item, (double) chance); + }); + axeWaxables.put(block, new Waxable(into, drops)); + }); + getMap("tools.axe.weatherables", Map.ofEntries( + Map.entry("minecraft:exposed_copper", Map.of("into", "minecraft:copper_block", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper", Map.of("into", "minecraft:exposed_copper", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper", Map.of("into", "minecraft:weathered_copper", "drops", new HashMap())), + Map.entry("minecraft:exposed_cut_copper", Map.of("into", "minecraft:cut_copper", "drops", new HashMap())), + Map.entry("minecraft:weathered_cut_copper", Map.of("into", "minecraft:exposed_cut_copper", "drops", new HashMap())), + Map.entry("minecraft:oxidized_cut_copper", Map.of("into", "minecraft:weathered_cut_copper", "drops", new HashMap())), + Map.entry("minecraft:exposed_chiseled_copper", Map.of("into", "minecraft:chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:weathered_chiseled_copper", Map.of("into", "minecraft:exposed_chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:oxidized_chiseled_copper", Map.of("into", "minecraft:weathered_chiseled_copper", "drops", new HashMap())), + Map.entry("minecraft:exposed_cut_copper_slab", Map.of("into", "minecraft:cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:weathered_cut_copper_slab", Map.of("into", "minecraft:exposed_cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:oxidized_cut_copper_slab", Map.of("into", "minecraft:weathered_cut_copper_slab", "drops", new HashMap())), + Map.entry("minecraft:exposed_cut_copper_stairs", Map.of("into", "minecraft:cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:weathered_cut_copper_stairs", Map.of("into", "minecraft:exposed_cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:oxidized_cut_copper_stairs", Map.of("into", "minecraft:weathered_cut_copper_stairs", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_door", Map.of("into", "minecraft:copper_door", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_door", Map.of("into", "minecraft:exposed_copper_door", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_door", Map.of("into", "minecraft:weathered_copper_door", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_trapdoor", Map.of("into", "minecraft:copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_trapdoor", Map.of("into", "minecraft:exposed_copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_trapdoor", Map.of("into", "minecraft:weathered_copper_trapdoor", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_grate", Map.of("into", "minecraft:copper_grate", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_grate", Map.of("into", "minecraft:exposed_copper_grate", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_grate", Map.of("into", "minecraft:weathered_copper_grate", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_bulb", Map.of("into", "minecraft:copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_bulb", Map.of("into", "minecraft:exposed_copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_bulb", Map.of("into", "minecraft:weathered_copper_bulb", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_chest", Map.of("into", "minecraft:copper_chest", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_chest", Map.of("into", "minecraft:exposed_copper_chest", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_chest", Map.of("into", "minecraft:weathered_copper_chest", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_golem_statue", Map.of("into", "minecraft:copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_golem_statue", Map.of("into", "minecraft:exposed_copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_golem_statue", Map.of("into", "minecraft:weathered_copper_golem_statue", "drops", new HashMap())), + Map.entry("minecraft:exposed_lightning_rod", Map.of("into", "minecraft:lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:weathered_lightning_rod", Map.of("into", "minecraft:exposed_lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:oxidized_lightning_rod", Map.of("into", "minecraft:weathered_lightning_rod", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_bars", Map.of("into", "minecraft:copper_bars", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_bars", Map.of("into", "minecraft:exposed_copper_bars", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_bars", Map.of("into", "minecraft:weathered_copper_bars", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_chain", Map.of("into", "minecraft:copper_chain", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_chain", Map.of("into", "minecraft:exposed_copper_chain", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_chain", Map.of("into", "minecraft:weathered_copper_chain", "drops", new HashMap())), + Map.entry("minecraft:exposed_copper_lantern", Map.of("into", "minecraft:copper_lantern", "drops", new HashMap())), + Map.entry("minecraft:weathered_copper_lantern", Map.of("into", "minecraft:exposed_copper_lantern", "drops", new HashMap())), + Map.entry("minecraft:oxidized_copper_lantern", Map.of("into", "minecraft:weathered_copper_lantern", "drops", new HashMap()))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables`: " + blockId); return; } + if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + "`"); return; } + String intoId = (String) map.get("into"); + Block into = BuiltInRegistries.BLOCK.getValue(Identifier.parse(intoId)); + if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.axe.weatherables." + blockId + ".into`: " + intoId); return; } + Object dropsObj = map.get("drops"); + if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.axe.weatherables." + blockId + ".drops`"); return; } + Map drops = new HashMap<>(); + dropsMap.forEach((itemId, chance) -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId.toString())); + if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.axe.weatherables." + blockId + ".drops`: " + itemId); return; } + drops.put(item, (double) chance); + }); + axeWeatherables.put(block, new Weatherable(into, drops)); + }); + getMap("tools.hoe.tillables", Map.ofEntries( + Map.entry("minecraft:grass_block", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), + Map.entry("minecraft:dirt_path", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), + Map.entry("minecraft:dirt", Map.of("condition", "air_above", "into", "minecraft:farmland", "drops", new HashMap())), + Map.entry("minecraft:coarse_dirt", Map.of("condition", "air_above", "into", "minecraft:dirt", "drops", new HashMap())), + Map.entry("minecraft:rooted_dirt", Map.of("condition", "always", "into", "minecraft:dirt", "drops", Map.of("minecraft:hanging_roots", 1.0D)))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables`: " + blockId); return; } + if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + "`"); return; } + String conditionId = (String) map.get("condition"); + Tillable.Condition condition = Tillable.Condition.get(conditionId); + if (condition == null) { PurpurConfig.log(Level.SEVERE, "Invalid condition for `tools.hoe.tillables." + blockId + ".condition`: " + conditionId); return; } + String intoId = (String) map.get("into"); + Block into = BuiltInRegistries.BLOCK.getValue(Identifier.parse(intoId)); + if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.hoe.tillables." + blockId + ".into`: " + intoId); return; } + Object dropsObj = map.get("drops"); + if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.hoe.tillables." + blockId + ".drops`"); return; } + Map drops = new HashMap<>(); + dropsMap.forEach((itemId, chance) -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId.toString())); + if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.hoe.tillables." + blockId + ".drops`: " + itemId); return; } + drops.put(item, (double) chance); + }); + hoeTillables.put(block, new Tillable(condition, into, drops)); + }); + getMap("tools.shovel.flattenables", Map.ofEntries( + Map.entry("minecraft:grass_block", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), + Map.entry("minecraft:dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), + Map.entry("minecraft:podzol", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), + Map.entry("minecraft:coarse_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), + Map.entry("minecraft:mycelium", Map.of("into", "minecraft:dirt_path", "drops", new HashMap())), + Map.entry("minecraft:rooted_dirt", Map.of("into", "minecraft:dirt_path", "drops", new HashMap()))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(blockId)); + if (block == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables`: " + blockId); return; } + if (!(obj instanceof Map map)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + "`"); return; } + String intoId = (String) map.get("into"); + Block into = BuiltInRegistries.BLOCK.getValue(Identifier.parse(intoId)); + if (into == Blocks.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid block for `tools.shovel.flattenables." + blockId + ".into`: " + intoId); return; } + Object dropsObj = map.get("drops"); + if (!(dropsObj instanceof Map dropsMap)) { PurpurConfig.log(Level.SEVERE, "Invalid yaml for `tools.shovel.flattenables." + blockId + ".drops`"); return; } + Map drops = new HashMap<>(); + dropsMap.forEach((itemId, chance) -> { + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(itemId.toString())); + if (item == Items.AIR) { PurpurConfig.log(Level.SEVERE, "Invalid item for `tools.shovel.flattenables." + blockId + ".drops`: " + itemId); return; } + drops.put(item, (double) chance); + }); + shovelFlattenables.put(block, new Flattenable(into, drops)); + }); + hoeReplantsCrops = getBoolean("tools.hoe.replant-crops", hoeReplantsCrops); + hoeReplantsNetherWarts = getBoolean("tools.hoe.replant-nether-warts", hoeReplantsNetherWarts); + } + + public boolean anvilAllowColors = false; + public boolean anvilColorsUseMiniMessage; + public int anvilRepairIngotsAmount = 0; + public int anvilDamageObsidianAmount = 0; + private void anvilSettings() { + anvilAllowColors = getBoolean("blocks.anvil.allow-colors", anvilAllowColors); + anvilColorsUseMiniMessage = getBoolean("blocks.anvil.use-mini-message", anvilColorsUseMiniMessage); + anvilRepairIngotsAmount = getInt("blocks.anvil.iron-ingots-used-for-repair", anvilRepairIngotsAmount); + anvilDamageObsidianAmount = getInt("blocks.anvil.obsidian-used-for-damage", anvilDamageObsidianAmount); + } + + public double azaleaGrowthChance = 0.0D; + private void azaleaSettings() { + azaleaGrowthChance = getDouble("blocks.azalea.growth-chance", azaleaGrowthChance); + } + + public int beaconLevelOne = 20; + public int beaconLevelTwo = 30; + public int beaconLevelThree = 40; + public int beaconLevelFour = 50; + public boolean beaconAllowEffectsWithTintedGlass = false; + private void beaconSettings() { + beaconLevelOne = getInt("blocks.beacon.effect-range.level-1", beaconLevelOne); + beaconLevelTwo = getInt("blocks.beacon.effect-range.level-2", beaconLevelTwo); + beaconLevelThree = getInt("blocks.beacon.effect-range.level-3", beaconLevelThree); + beaconLevelFour = getInt("blocks.beacon.effect-range.level-4", beaconLevelFour); + beaconAllowEffectsWithTintedGlass = getBoolean("blocks.beacon.allow-effects-with-tinted-glass", beaconAllowEffectsWithTintedGlass); + } + + public boolean bedExplode = true; + public boolean bedExplodeOnVillagerSleep = false; + public double bedExplosionPower = 5.0D; + public boolean bedExplosionFire = true; + public net.minecraft.world.level.Level.ExplosionInteraction bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + private void bedSettings() { + if (PurpurConfig.version < 31) { + if ("DESTROY".equals(getString("blocks.bed.explosion-effect", bedExplosionEffect.name()))) { + set("blocks.bed.explosion-effect", "BLOCK"); + } + } + bedExplode = getBoolean("blocks.bed.explode", bedExplode); + bedExplodeOnVillagerSleep = getBoolean("blocks.bed.explode-on-villager-sleep", bedExplodeOnVillagerSleep); + bedExplosionPower = getDouble("blocks.bed.explosion-power", bedExplosionPower); + bedExplosionFire = getBoolean("blocks.bed.explosion-fire", bedExplosionFire); + try { + bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.bed.explosion-effect", bedExplosionEffect.name())); + } catch (IllegalArgumentException e) { + log(Level.SEVERE, "Unknown value for `blocks.bed.explosion-effect`! Using default of `BLOCK`"); + bedExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + } + } + + public Map bigDripleafTiltDelay = new HashMap<>(); + private void bigDripleafSettings() { + bigDripleafTiltDelay.clear(); + getMap("blocks.big_dripleaf.tilt-delay", Map.ofEntries( + Map.entry("UNSTABLE", 10), + Map.entry("PARTIAL", 10), + Map.entry("FULL", 100)) + ).forEach((tilt, delay) -> { + try { + bigDripleafTiltDelay.put(Tilt.valueOf(tilt), (int) delay); + } catch (IllegalArgumentException e) { + PurpurConfig.log(Level.SEVERE, "Invalid big_dripleaf tilt key: " + tilt); + } + }); + } + + public boolean cactusBreaksFromSolidNeighbors = true; + public boolean cactusAffectedByBonemeal = false; + private void cactusSettings() { + cactusBreaksFromSolidNeighbors = getBoolean("blocks.cactus.breaks-from-solid-neighbors", cactusBreaksFromSolidNeighbors); + cactusAffectedByBonemeal = getBoolean("blocks.cactus.affected-by-bonemeal", cactusAffectedByBonemeal); + } + + public boolean sugarCanAffectedByBonemeal = false; + private void sugarCaneSettings() { + sugarCanAffectedByBonemeal = getBoolean("blocks.sugar_cane.affected-by-bonemeal", sugarCanAffectedByBonemeal); + } + + public boolean netherWartAffectedByBonemeal = false; + private void netherWartSettings() { + netherWartAffectedByBonemeal = getBoolean("blocks.nether_wart.affected-by-bonemeal", netherWartAffectedByBonemeal); + } + + public boolean campFireLitWhenPlaced = true; + private void campFireSettings() { + campFireLitWhenPlaced = getBoolean("blocks.campfire.lit-when-placed", campFireLitWhenPlaced); + } + + public boolean chestOpenWithBlockOnTop = false; + private void chestSettings() { + chestOpenWithBlockOnTop = getBoolean("blocks.chest.open-with-solid-block-on-top", chestOpenWithBlockOnTop); + } + + public boolean composterBulkProcess = false; + private void composterSettings() { + composterBulkProcess = getBoolean("blocks.composter.sneak-to-bulk-process", composterBulkProcess); + } + + public boolean coralDieOutsideWater = true; + private void coralSettings() { + coralDieOutsideWater = getBoolean("blocks.coral.die-outside-water", coralDieOutsideWater); + } + + public boolean dispenserApplyCursedArmor = true; + public boolean dispenserPlaceAnvils = false; + private void dispenserSettings() { + dispenserApplyCursedArmor = getBoolean("blocks.dispenser.apply-cursed-to-armor-slots", dispenserApplyCursedArmor); + dispenserPlaceAnvils = getBoolean("blocks.dispenser.place-anvils", dispenserPlaceAnvils); + } + + public List doorRequiresRedstone = new ArrayList<>(); + private void doorSettings() { + getList("blocks.door.requires-redstone", new ArrayList()).forEach(key -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(key.toString())); + if (!block.defaultBlockState().isAir()) { + doorRequiresRedstone.add(block); + } + }); + } + + public boolean dragonEggTeleport = true; + private void dragonEggSettings() { + dragonEggTeleport = getBoolean("blocks.dragon_egg.teleport", dragonEggTeleport); + } + + public boolean baselessEndCrystalExplode = true; + public double baselessEndCrystalExplosionPower = 6.0D; + public boolean baselessEndCrystalExplosionFire = false; + public net.minecraft.world.level.Level.ExplosionInteraction baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + public boolean basedEndCrystalExplode = true; + public double basedEndCrystalExplosionPower = 6.0D; + public boolean basedEndCrystalExplosionFire = false; + public net.minecraft.world.level.Level.ExplosionInteraction basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + public int endCrystalCramming = 0; + public boolean endCrystalPlaceAnywhere = false; + private void endCrystalSettings() { + if (PurpurConfig.version < 31) { + if ("DESTROY".equals(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name()))) { + set("blocks.end-crystal.baseless.explosion-effect", "BLOCK"); + } + if ("DESTROY".equals(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name()))) { + set("blocks.end-crystal.base.explosion-effect", "BLOCK"); + } + } + baselessEndCrystalExplode = getBoolean("blocks.end-crystal.baseless.explode", baselessEndCrystalExplode); + baselessEndCrystalExplosionPower = getDouble("blocks.end-crystal.baseless.explosion-power", baselessEndCrystalExplosionPower); + baselessEndCrystalExplosionFire = getBoolean("blocks.end-crystal.baseless.explosion-fire", baselessEndCrystalExplosionFire); + try { + baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.baseless.explosion-effect", baselessEndCrystalExplosionEffect.name())); + } catch (IllegalArgumentException e) { + log(Level.SEVERE, "Unknown value for `blocks.end-crystal.baseless.explosion-effect`! Using default of `BLOCK`"); + baselessEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + } + basedEndCrystalExplode = getBoolean("blocks.end-crystal.base.explode", basedEndCrystalExplode); + basedEndCrystalExplosionPower = getDouble("blocks.end-crystal.base.explosion-power", basedEndCrystalExplosionPower); + basedEndCrystalExplosionFire = getBoolean("blocks.end-crystal.base.explosion-fire", basedEndCrystalExplosionFire); + try { + basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.end-crystal.base.explosion-effect", basedEndCrystalExplosionEffect.name())); + } catch (IllegalArgumentException e) { + log(Level.SEVERE, "Unknown value for `blocks.end-crystal.base.explosion-effect`! Using default of `BLOCK`"); + basedEndCrystalExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + } + endCrystalCramming = getInt("blocks.end-crystal.cramming-amount", endCrystalCramming); + endCrystalPlaceAnywhere = getBoolean("gameplay-mechanics.item.end-crystal.place-anywhere", endCrystalPlaceAnywhere); + } + + public Boolean farmlandMobGriefingOverride = null; + public boolean farmlandGetsMoistFromBelow = false; + public boolean farmlandAlpha = false; + public boolean farmlandTramplingDisabled = false; + public boolean farmlandTramplingOnlyPlayers = false; + public boolean farmlandTramplingFeatherFalling = false; + public double farmlandTrampleHeight = -1D; + private void farmlandSettings() { + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("blocks.farmland.bypass-mob-griefing", false); + set("blocks.farmland.bypass-mob-griefing", null); + set("blocks.farmland.mob-griefing-override", oldVal ? true : "default"); + } + farmlandMobGriefingOverride = getBooleanOrDefault("blocks.farmland.mob-griefing-override", farmlandMobGriefingOverride); + farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); + farmlandAlpha = getBoolean("blocks.farmland.use-alpha-farmland", farmlandAlpha); + farmlandTramplingDisabled = getBoolean("blocks.farmland.disable-trampling", farmlandTramplingDisabled); + farmlandTramplingOnlyPlayers = getBoolean("blocks.farmland.only-players-trample", farmlandTramplingOnlyPlayers); + farmlandTramplingFeatherFalling = getBoolean("blocks.farmland.feather-fall-distance-affects-trampling", farmlandTramplingFeatherFalling); + farmlandTrampleHeight = getDouble("blocks.farmland.trample-height", farmlandTrampleHeight); + } + + public double floweringAzaleaGrowthChance = 0.0D; + private void floweringAzaleaSettings() { + floweringAzaleaGrowthChance = getDouble("blocks.flowering_azalea.growth-chance", floweringAzaleaGrowthChance); + } + + public boolean furnaceUseLavaFromUnderneath = false; + private void furnaceSettings() { + if (PurpurConfig.version < 17) { + furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath); + boolean oldValue = getBoolean("blocks.furnace.infinite-fuel", furnaceUseLavaFromUnderneath); + set("blocks.furnace.infinite-fuel", null); + set("blocks.furnace.use-lava-from-underneath", oldValue); + } + furnaceUseLavaFromUnderneath = getBoolean("blocks.furnace.use-lava-from-underneath", furnaceUseLavaFromUnderneath); + } + + public boolean mobsSpawnOnPackedIce = true; + public boolean mobsSpawnOnBlueIce = true; + public boolean snowOnBlueIce = true; + private void iceSettings() { + mobsSpawnOnPackedIce = getBoolean("blocks.packed_ice.allow-mob-spawns", mobsSpawnOnPackedIce); + mobsSpawnOnBlueIce = getBoolean("blocks.blue_ice.allow-mob-spawns", mobsSpawnOnBlueIce); + snowOnBlueIce = getBoolean("blocks.blue_ice.allow-snow-formation", snowOnBlueIce); + } + + public int lavaInfiniteRequiredSources = 2; + public int lavaSpeedNether = 10; + public int lavaSpeedNotNether = 30; + private void lavaSettings() { + lavaInfiniteRequiredSources = getInt("blocks.lava.infinite-required-sources", lavaInfiniteRequiredSources); + lavaSpeedNether = getInt("blocks.lava.speed.nether", lavaSpeedNether); + lavaSpeedNotNether = getInt("blocks.lava.speed.not-nether", lavaSpeedNotNether); + } + + public int pistonBlockPushLimit = 12; + private void pistonSettings() { + pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit); + } + + public boolean magmaBlockDamageWhenSneaking = false; + private void magmaBlockSettings() { + magmaBlockDamageWhenSneaking = getBoolean("blocks.magma-block.damage-when-sneaking", magmaBlockDamageWhenSneaking); + } + + public Boolean powderSnowMobGriefingOverride = null; + private void powderSnowSettings() { + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("blocks.powder_snow.bypass-mob-griefing", false); + set("blocks.powder_snow.bypass-mob-griefing", null); + set("blocks.powder_snow.mob-griefing-override", oldVal ? true : "default"); + } + powderSnowMobGriefingOverride = getBooleanOrDefault("blocks.powder_snow.mob-griefing-override", powderSnowMobGriefingOverride); + } + + public int railActivationRange = 8; + private void railSettings() { + railActivationRange = getInt("blocks.powered-rail.activation-range", railActivationRange); + } + + public boolean respawnAnchorExplode = true; + public double respawnAnchorExplosionPower = 5.0D; + public boolean respawnAnchorExplosionFire = true; + public net.minecraft.world.level.Level.ExplosionInteraction respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + private void respawnAnchorSettings() { + if (PurpurConfig.version < 31) { + if ("DESTROY".equals(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name()))) { + set("blocks.respawn_anchor.explosion-effect", "BLOCK"); + } + } + respawnAnchorExplode = getBoolean("blocks.respawn_anchor.explode", respawnAnchorExplode); + respawnAnchorExplosionPower = getDouble("blocks.respawn_anchor.explosion-power", respawnAnchorExplosionPower); + respawnAnchorExplosionFire = getBoolean("blocks.respawn_anchor.explosion-fire", respawnAnchorExplosionFire); + try { + respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.valueOf(getString("blocks.respawn_anchor.explosion-effect", respawnAnchorExplosionEffect.name())); + } catch (IllegalArgumentException e) { + log(Level.SEVERE, "Unknown value for `blocks.respawn_anchor.explosion-effect`! Using default of `BLOCK`"); + respawnAnchorExplosionEffect = net.minecraft.world.level.Level.ExplosionInteraction.BLOCK; + } + } + + public boolean sculkShriekerCanSummonDefault = false; + private void sculkShriekerSettings() { + sculkShriekerCanSummonDefault = getBoolean("blocks.sculk_shrieker.can-summon-default", sculkShriekerCanSummonDefault); + } + + public boolean signAllowColors = false; + private void signSettings() { + signAllowColors = getBoolean("blocks.sign.allow-colors", signAllowColors); + } + + public boolean slabHalfBreak = false; + private void slabSettings() { + slabHalfBreak = getBoolean("blocks.slab.break-individual-slabs-when-sneaking", slabHalfBreak); + } + + public boolean spawnerDeactivateByRedstone = false; + public boolean spawnerFixMC238526 = false; + private void spawnerSettings() { + spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); + spawnerFixMC238526 = getBoolean("blocks.spawner.fix-mc-238526", spawnerFixMC238526); + } + + public int spongeAbsorptionArea = 65; + public int spongeAbsorptionRadius = 6; + public boolean spongeAbsorbsLava = false; + public boolean spongeAbsorbsWaterFromMud = false; + private void spongeSettings() { + spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea); + spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius); + spongeAbsorbsLava = getBoolean("blocks.sponge.absorbs-lava", spongeAbsorbsLava); + spongeAbsorbsWaterFromMud = getBoolean("blocks.sponge.absorbs-water-from-mud", spongeAbsorbsWaterFromMud); + } + + public float stonecutterDamage = 0.0F; + private void stonecutterSettings() { + stonecutterDamage = (float) getDouble("blocks.stonecutter.damage", stonecutterDamage); + } + + public boolean turtleEggsBreakFromExpOrbs = false; + public boolean turtleEggsBreakFromItems = false; + public boolean turtleEggsBreakFromMinecarts = false; + public Boolean turtleEggsMobGriefingOverride = null; + public int turtleEggsRandomTickCrackChance = 500; + public boolean turtleEggsTramplingFeatherFalling = false; + private void turtleEggSettings() { + turtleEggsBreakFromExpOrbs = getBoolean("blocks.turtle_egg.break-from-exp-orbs", turtleEggsBreakFromExpOrbs); + turtleEggsBreakFromItems = getBoolean("blocks.turtle_egg.break-from-items", turtleEggsBreakFromItems); + turtleEggsBreakFromMinecarts = getBoolean("blocks.turtle_egg.break-from-minecarts", turtleEggsBreakFromMinecarts); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("blocks.turtle_egg.bypass-mob-griefing", false); + set("blocks.turtle_egg.bypass-mob-griefing", null); + set("blocks.turtle_egg.mob-griefing-override", oldVal ? true : "default"); + } + turtleEggsMobGriefingOverride = getBooleanOrDefault("blocks.turtle_egg.mob-griefing-override", turtleEggsMobGriefingOverride); + turtleEggsRandomTickCrackChance = getInt("blocks.turtle_egg.random-tick-crack-chance", turtleEggsRandomTickCrackChance); + turtleEggsTramplingFeatherFalling = getBoolean("blocks.turtle_egg.feather-fall-distance-affects-trampling", turtleEggsTramplingFeatherFalling); + } + + public int waterInfiniteRequiredSources = 2; + private void waterSources() { + waterInfiniteRequiredSources = getInt("blocks.water.infinite-required-sources", waterInfiniteRequiredSources); + } + + public boolean babiesAreRidable = true; + public boolean untamedTamablesAreRidable = true; + public boolean useNightVisionWhenRiding = false; + public boolean useDismountsUnderwaterTag = true; + private void ridableSettings() { + babiesAreRidable = getBoolean("ridable-settings.babies-are-ridable", babiesAreRidable); + untamedTamablesAreRidable = getBoolean("ridable-settings.untamed-tamables-are-ridable", untamedTamablesAreRidable); + useNightVisionWhenRiding = getBoolean("ridable-settings.use-night-vision", useNightVisionWhenRiding); + useDismountsUnderwaterTag = getBoolean("ridable-settings.use-dismounts-underwater-tag", useDismountsUnderwaterTag); + } + + public boolean allayRidable = false; + public boolean allayRidableInWater = true; + public boolean allayControllable = true; + public double allayMaxHealth = 20.0D; + public double allayScale = 1.0D; + private void allaySettings() { + allayRidable = getBoolean("mobs.allay.ridable", allayRidable); + allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater); + allayControllable = getBoolean("mobs.allay.controllable", allayControllable); + allayMaxHealth = getDouble("mobs.allay.attributes.max_health", allayMaxHealth); + allayScale = Mth.clamp(getDouble("mobs.allay.attributes.scale", allayScale), 0.0625D, 16.0D); + } + + public boolean armadilloRidable = false; + public boolean armadilloRidableInWater = true; + public boolean armadilloControllable = true; + public double armadilloMaxHealth = 12.0D; + public double armadilloScale = 1.0D; + public int armadilloBreedingTicks = 6000; + private void armadilloSettings() { + armadilloRidable = getBoolean("mobs.armadillo.ridable", armadilloRidable); + armadilloRidableInWater = getBoolean("mobs.armadillo.ridable-in-water", armadilloRidableInWater); + armadilloControllable = getBoolean("mobs.armadillo.controllable", armadilloControllable); + armadilloMaxHealth = getDouble("mobs.armadillo.attributes.max_health", armadilloMaxHealth); + armadilloScale = Mth.clamp(getDouble("mobs.armadillo.attributes.scale", armadilloScale), 0.0625D, 16.0D); + armadilloBreedingTicks = getInt("mobs.armadillo.breeding-delay-ticks", armadilloBreedingTicks); + } + + public boolean axolotlRidable = false; + public boolean axolotlControllable = true; + public double axolotlMaxHealth = 14.0D; + public double axolotlScale = 1.0D; + public int axolotlBreedingTicks = 6000; + public boolean axolotlTakeDamageFromWater = false; + public boolean axolotlAlwaysDropExp = false; + private void axolotlSettings() { + axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable); + axolotlControllable = getBoolean("mobs.axolotl.controllable", axolotlControllable); + axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth); + axolotlScale = Mth.clamp(getDouble("mobs.axolotl.attributes.scale", axolotlScale), 0.0625D, 16.0D); + axolotlBreedingTicks = getInt("mobs.axolotl.breeding-delay-ticks", axolotlBreedingTicks); + axolotlTakeDamageFromWater = getBoolean("mobs.axolotl.takes-damage-from-water", axolotlTakeDamageFromWater); + axolotlAlwaysDropExp = getBoolean("mobs.axolotl.always-drop-exp", axolotlAlwaysDropExp); + } + + public boolean batRidable = false; + public boolean batRidableInWater = true; + public boolean batControllable = true; + public double batMaxY = 320D; + public double batMaxHealth = 6.0D; + public double batScale = 1.0D; + public double batFollowRange = 16.0D; + public double batKnockbackResistance = 0.0D; + public double batMovementSpeed = 0.6D; + public double batFlyingSpeed = 0.6D; + public double batArmor = 0.0D; + public double batArmorToughness = 0.0D; + public double batAttackKnockback = 0.0D; + public boolean batTakeDamageFromWater = false; + public boolean batAlwaysDropExp = false; + private void batSettings() { + batRidable = getBoolean("mobs.bat.ridable", batRidable); + batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater); + batControllable = getBoolean("mobs.bat.controllable", batControllable); + batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.bat.attributes.max-health", batMaxHealth); + set("mobs.bat.attributes.max-health", null); + set("mobs.bat.attributes.max_health", oldValue); + } + batMaxHealth = getDouble("mobs.bat.attributes.max_health", batMaxHealth); + batScale = Mth.clamp(getDouble("mobs.bat.attributes.scale", batScale), 0.0625D, 16.0D); + batFollowRange = getDouble("mobs.bat.attributes.follow_range", batFollowRange); + batKnockbackResistance = getDouble("mobs.bat.attributes.knockback_resistance", batKnockbackResistance); + batMovementSpeed = getDouble("mobs.bat.attributes.movement_speed", batMovementSpeed); + batFlyingSpeed = getDouble("mobs.bat.attributes.flying_speed", batFlyingSpeed); + batArmor = getDouble("mobs.bat.attributes.armor", batArmor); + batArmorToughness = getDouble("mobs.bat.attributes.armor_toughness", batArmorToughness); + batAttackKnockback = getDouble("mobs.bat.attributes.attack_knockback", batAttackKnockback); + batTakeDamageFromWater = getBoolean("mobs.bat.takes-damage-from-water", batTakeDamageFromWater); + batAlwaysDropExp = getBoolean("mobs.bat.always-drop-exp", batAlwaysDropExp); + } + + public boolean beeRidable = false; + public boolean beeRidableInWater = true; + public boolean beeControllable = true; + public double beeMaxY = 320D; + public double beeMaxHealth = 10.0D; + public double beeScale = 1.0D; + public int beeBreedingTicks = 6000; + public boolean beeTakeDamageFromWater = false; + public boolean beeCanInstantlyStartDrowning = true; + public boolean beeCanWorkAtNight = false; + public boolean beeCanWorkInRain = false; + public boolean beeAlwaysDropExp = false; + public boolean beeDiesAfterSting = true; + private void beeSettings() { + beeRidable = getBoolean("mobs.bee.ridable", beeRidable); + beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); + beeControllable = getBoolean("mobs.bee.controllable", beeControllable); + beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.bee.attributes.max-health", beeMaxHealth); + set("mobs.bee.attributes.max-health", null); + set("mobs.bee.attributes.max_health", oldValue); + } + beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth); + beeScale = Mth.clamp(getDouble("mobs.bee.attributes.scale", beeScale), 0.0625D, 16.0D); + beeBreedingTicks = getInt("mobs.bee.breeding-delay-ticks", beeBreedingTicks); + if (PurpurConfig.version < 40) { + set("mobs.bee.takes-damage-from-water", false); + } + beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater); + beeCanWorkAtNight = getBoolean("mobs.bee.can-work-at-night", beeCanWorkAtNight); + beeCanWorkInRain = getBoolean("mobs.bee.can-work-in-rain", beeCanWorkInRain); + beeCanInstantlyStartDrowning = getBoolean("mobs.bee.can-instantly-start-drowning", beeCanInstantlyStartDrowning); + beeAlwaysDropExp = getBoolean("mobs.bee.always-drop-exp", beeAlwaysDropExp); + beeDiesAfterSting = getBoolean("mobs.bee.dies-after-sting", beeDiesAfterSting); + } + + public boolean blazeRidable = false; + public boolean blazeRidableInWater = true; + public boolean blazeControllable = true; + public double blazeMaxY = 320D; + public double blazeMaxHealth = 20.0D; + public double blazeScale = 1.0D; + public boolean blazeTakeDamageFromWater = true; + public boolean blazeAlwaysDropExp = false; + private void blazeSettings() { + blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); + blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); + blazeControllable = getBoolean("mobs.blaze.controllable", blazeControllable); + blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.blaze.attributes.max-health", blazeMaxHealth); + set("mobs.blaze.attributes.max-health", null); + set("mobs.blaze.attributes.max_health", oldValue); + } + blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth); + blazeScale = Mth.clamp(getDouble("mobs.blaze.attributes.scale", blazeScale), 0.0625D, 16.0D); + blazeTakeDamageFromWater = getBoolean("mobs.blaze.takes-damage-from-water", blazeTakeDamageFromWater); + blazeAlwaysDropExp = getBoolean("mobs.blaze.always-drop-exp", blazeAlwaysDropExp); + } + + public boolean boggedRidable = false; + public boolean boggedRidableInWater = true; + public boolean boggedControllable = true; + public double boggedMaxHealth = 16.0D; + public double boggedScale = 1.0D; + private void boggedSettings() { + boggedRidable = getBoolean("mobs.bogged.ridable", boggedRidable); + boggedRidableInWater = getBoolean("mobs.bogged.ridable-in-water", boggedRidableInWater); + boggedControllable = getBoolean("mobs.bogged.controllable", boggedControllable); + boggedMaxHealth = getDouble("mobs.bogged.attributes.max_health", boggedMaxHealth); + boggedScale = Mth.clamp(getDouble("mobs.bogged.attributes.scale", boggedScale), 0.0625D, 16.0D); + } + + public boolean camelRidableInWater = false; + public double camelMaxHealthMin = 32.0D; + public double camelMaxHealthMax = 32.0D; + public double camelJumpStrengthMin = 0.42D; + public double camelJumpStrengthMax = 0.42D; + public double camelMovementSpeedMin = 0.09D; + public double camelMovementSpeedMax = 0.09D; + public int camelBreedingTicks = 6000; + private void camelSettings() { + camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); + camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); + camelMaxHealthMax = getDouble("mobs.camel.attributes.max_health.max", camelMaxHealthMax); + camelJumpStrengthMin = getDouble("mobs.camel.attributes.jump_strength.min", camelJumpStrengthMin); + camelJumpStrengthMax = getDouble("mobs.camel.attributes.jump_strength.max", camelJumpStrengthMax); + camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin); + camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax); + camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks); + } + + public boolean catRidable = false; + public boolean catRidableInWater = true; + public boolean catControllable = true; + public double catMaxHealth = 10.0D; + public double catScale = 1.0D; + public int catSpawnDelay = 1200; + public int catSpawnSwampHutScanRange = 16; + public int catSpawnVillageScanRange = 48; + public int catBreedingTicks = 6000; + public DyeColor catDefaultCollarColor = DyeColor.RED; + public boolean catTakeDamageFromWater = false; + public boolean catAlwaysDropExp = false; + private void catSettings() { + catRidable = getBoolean("mobs.cat.ridable", catRidable); + catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); + catControllable = getBoolean("mobs.cat.controllable", catControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.cat.attributes.max-health", catMaxHealth); + set("mobs.cat.attributes.max-health", null); + set("mobs.cat.attributes.max_health", oldValue); + } + catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth); + catScale = Mth.clamp(getDouble("mobs.cat.attributes.scale", catScale), 0.0625D, 16.0D); + catSpawnDelay = getInt("mobs.cat.spawn-delay", catSpawnDelay); + catSpawnSwampHutScanRange = getInt("mobs.cat.scan-range-for-other-cats.swamp-hut", catSpawnSwampHutScanRange); + catSpawnVillageScanRange = getInt("mobs.cat.scan-range-for-other-cats.village", catSpawnVillageScanRange); + catBreedingTicks = getInt("mobs.cat.breeding-delay-ticks", catBreedingTicks); + try { + catDefaultCollarColor = DyeColor.valueOf(getString("mobs.cat.default-collar-color", catDefaultCollarColor.name())); + } catch (IllegalArgumentException ignore) { + catDefaultCollarColor = DyeColor.RED; + } + catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater); + catAlwaysDropExp = getBoolean("mobs.cat.always-drop-exp", catAlwaysDropExp); + } + + public boolean caveSpiderRidable = false; + public boolean caveSpiderRidableInWater = true; + public boolean caveSpiderControllable = true; + public double caveSpiderMaxHealth = 12.0D; + public double caveSpiderScale = 1.0D; + public boolean caveSpiderTakeDamageFromWater = false; + public boolean caveSpiderAlwaysDropExp = false; + private void caveSpiderSettings() { + caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); + caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); + caveSpiderControllable = getBoolean("mobs.cave_spider.controllable", caveSpiderControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.cave_spider.attributes.max-health", caveSpiderMaxHealth); + set("mobs.cave_spider.attributes.max-health", null); + set("mobs.cave_spider.attributes.max_health", oldValue); + } + caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth); + caveSpiderScale = Mth.clamp(getDouble("mobs.cave_spider.attributes.scale", caveSpiderScale), 0.0625D, 16.0D); + caveSpiderTakeDamageFromWater = getBoolean("mobs.cave_spider.takes-damage-from-water", caveSpiderTakeDamageFromWater); + caveSpiderAlwaysDropExp = getBoolean("mobs.cave_spider.always-drop-exp", caveSpiderAlwaysDropExp); + } + + public boolean chickenRidable = false; + public boolean chickenRidableInWater = false; + public boolean chickenControllable = true; + public double chickenMaxHealth = 4.0D; + public double chickenScale = 1.0D; + public boolean chickenRetaliate = false; + public int chickenBreedingTicks = 6000; + public boolean chickenTakeDamageFromWater = false; + public boolean chickenAlwaysDropExp = false; + private void chickenSettings() { + chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); + chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); + chickenControllable = getBoolean("mobs.chicken.controllable", chickenControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.chicken.attributes.max-health", chickenMaxHealth); + set("mobs.chicken.attributes.max-health", null); + set("mobs.chicken.attributes.max_health", oldValue); + } + chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth); + chickenScale = Mth.clamp(getDouble("mobs.chicken.attributes.scale", chickenScale), 0.0625D, 16.0D); + chickenRetaliate = getBoolean("mobs.chicken.retaliate", chickenRetaliate); + chickenBreedingTicks = getInt("mobs.chicken.breeding-delay-ticks", chickenBreedingTicks); + chickenTakeDamageFromWater = getBoolean("mobs.chicken.takes-damage-from-water", chickenTakeDamageFromWater); + chickenAlwaysDropExp = getBoolean("mobs.chicken.always-drop-exp", chickenAlwaysDropExp); + } + + public boolean codRidable = false; + public boolean codControllable = true; + public double codMaxHealth = 3.0D; + public double codScale = 1.0D; + public boolean codTakeDamageFromWater = false; + public boolean codAlwaysDropExp = false; + private void codSettings() { + codRidable = getBoolean("mobs.cod.ridable", codRidable); + codControllable = getBoolean("mobs.cod.controllable", codControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.cod.attributes.max-health", codMaxHealth); + set("mobs.cod.attributes.max-health", null); + set("mobs.cod.attributes.max_health", oldValue); + } + codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth); + codScale = Mth.clamp(getDouble("mobs.cod.attributes.scale", codScale), 0.0625D, 16.0D); + codTakeDamageFromWater = getBoolean("mobs.cod.takes-damage-from-water", codTakeDamageFromWater); + codAlwaysDropExp = getBoolean("mobs.cod.always-drop-exp", codAlwaysDropExp); + } + + public boolean copperGolemRidable = false; + public boolean copperGolemRidableInWater = true; + public boolean copperGolemControllable = true; + public boolean copperGolemCanSwim = false; + public double copperGolemMaxHealth = 12.0D; + public double copperGolemStepHeight = 1.0D; + public double copperGolemMovementSpeed = 0.2D; + public double copperGolemScale = 1.0D; + public boolean copperGolemTakeDamageFromWater = false; + public boolean copperGolemAlwaysDropExp = false; + public boolean copperGolemCanOpenBarrel = false; + public boolean copperGolemCanOpenShulker = false; + private void copperGolemSettings() { + copperGolemRidable = getBoolean("mobs.copper_golem.ridable", copperGolemRidable); + copperGolemRidableInWater = getBoolean("mobs.copper_golem.ridable-in-water", copperGolemRidableInWater); + copperGolemControllable = getBoolean("mobs.copper_golem.controllable", copperGolemControllable); + copperGolemCanSwim = getBoolean("mobs.copper_golem.can-swim", copperGolemCanSwim); + copperGolemMaxHealth = getDouble("mobs.copper_golem.attributes.max_health", copperGolemMaxHealth); + copperGolemStepHeight = getDouble("mobs.copper_golem.attributes.step_height", copperGolemStepHeight); + copperGolemMovementSpeed = getDouble("mobs.copper_golem.attributes.movement_speed", copperGolemMovementSpeed); + copperGolemScale = Mth.clamp(getDouble("mobs.copper_golem.attributes.scale", copperGolemScale), 0.0625D, 16.0D); + copperGolemTakeDamageFromWater = getBoolean("mobs.copper_golem.takes-damage-from-water", copperGolemTakeDamageFromWater); + copperGolemAlwaysDropExp = getBoolean("mobs.copper_golem.always-drop-exp", copperGolemAlwaysDropExp); + copperGolemCanOpenBarrel = getBoolean("mobs.copper_golem.can-open-barrel", copperGolemCanOpenBarrel); + copperGolemCanOpenShulker = getBoolean("mobs.copper_golem.can-open-shulker", copperGolemCanOpenShulker); + } + + public boolean cowRidable = false; + public boolean cowRidableInWater = true; + public boolean cowControllable = true; + public double cowMaxHealth = 10.0D; + public double cowScale = 1.0D; + public int cowFeedMushrooms = 0; + public int cowBreedingTicks = 6000; + public boolean cowTakeDamageFromWater = false; + public double cowNaturallyAggressiveToPlayersChance = 0.0D; + public double cowNaturallyAggressiveToPlayersDamage = 2.0D; + public boolean cowAlwaysDropExp = false; + private void cowSettings() { + if (PurpurConfig.version < 22) { + double oldValue = getDouble("mobs.cow.naturally-aggressive-to-players-chance", cowNaturallyAggressiveToPlayersChance); + set("mobs.cow.naturally-aggressive-to-players-chance", null); + set("mobs.cow.naturally-aggressive-to-players.chance", oldValue); + } + cowRidable = getBoolean("mobs.cow.ridable", cowRidable); + cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); + cowControllable = getBoolean("mobs.cow.controllable", cowControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.cow.attributes.max-health", cowMaxHealth); + set("mobs.cow.attributes.max-health", null); + set("mobs.cow.attributes.max_health", oldValue); + } + cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth); + cowScale = Mth.clamp(getDouble("mobs.cow.attributes.scale", cowScale), 0.0625D, 16.0D); + cowFeedMushrooms = getInt("mobs.cow.feed-mushrooms-for-mooshroom", cowFeedMushrooms); + cowBreedingTicks = getInt("mobs.cow.breeding-delay-ticks", cowBreedingTicks); + cowTakeDamageFromWater = getBoolean("mobs.cow.takes-damage-from-water", cowTakeDamageFromWater); + cowNaturallyAggressiveToPlayersChance = getDouble("mobs.cow.naturally-aggressive-to-players.chance", cowNaturallyAggressiveToPlayersChance); + cowNaturallyAggressiveToPlayersDamage = getDouble("mobs.cow.naturally-aggressive-to-players.damage", cowNaturallyAggressiveToPlayersDamage); + cowAlwaysDropExp = getBoolean("mobs.cow.always-drop-exp", cowAlwaysDropExp); + } + + public boolean creakingRidable = false; + public boolean creakingRidableInWater = true; + public boolean creakingControllable = true; + public double creakingMaxHealth = 1.0D; + public double creakingScale = 1.0D; + private void creakingSettings() { + creakingRidable = getBoolean("mobs.creaking.ridable", creakingRidable); + creakingRidableInWater = getBoolean("mobs.creaking.ridable-in-water", creakingRidableInWater); + creakingControllable = getBoolean("mobs.creaking.controllable", creakingControllable); + creakingMaxHealth = getDouble("mobs.creaking.attributes.max_health", creakingMaxHealth); + creakingScale = Mth.clamp(getDouble("mobs.creaking.attributes.scale", creakingScale), 0.0625D, 16.0D); + } + + public boolean creeperRidable = false; + public boolean creeperRidableInWater = true; + public boolean creeperControllable = true; + public double creeperMaxHealth = 20.0D; + public double creeperScale = 1.0D; + public double creeperChargedChance = 0.0D; + public boolean creeperAllowGriefing = true; + public Boolean creeperMobGriefingOverride = null; + public boolean creeperTakeDamageFromWater = false; + public boolean creeperExplodeWhenKilled = false; + public boolean creeperHealthRadius = false; + public boolean creeperAlwaysDropExp = false; + public double creeperHeadVisibilityPercent = 0.5D; + public boolean creeperEncircleTarget = false; + private void creeperSettings() { + creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); + creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); + creeperControllable = getBoolean("mobs.creeper.controllable", creeperControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.creeper.attributes.max-health", creeperMaxHealth); + set("mobs.creeper.attributes.max-health", null); + set("mobs.creeper.attributes.max_health", oldValue); + } + creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth); + creeperScale = Mth.clamp(getDouble("mobs.creeper.attributes.scale", creeperScale), 0.0625D, 16.0D); + creeperChargedChance = getDouble("mobs.creeper.naturally-charged-chance", creeperChargedChance); + creeperAllowGriefing = getBoolean("mobs.creeper.allow-griefing", creeperAllowGriefing); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.creeper.bypass-mob-griefing", false); + set("mobs.creeper.bypass-mob-griefing", null); + set("mobs.creeper.mob-griefing-override", oldVal ? true : "default"); + } + creeperMobGriefingOverride = getBooleanOrDefault("mobs.creeper.mob-griefing-override", creeperMobGriefingOverride); + creeperTakeDamageFromWater = getBoolean("mobs.creeper.takes-damage-from-water", creeperTakeDamageFromWater); + creeperExplodeWhenKilled = getBoolean("mobs.creeper.explode-when-killed", creeperExplodeWhenKilled); + creeperHealthRadius = getBoolean("mobs.creeper.health-impacts-explosion", creeperHealthRadius); + creeperAlwaysDropExp = getBoolean("mobs.creeper.always-drop-exp", creeperAlwaysDropExp); + creeperHeadVisibilityPercent = getDouble("mobs.creeper.head-visibility-percent", creeperHeadVisibilityPercent); + creeperEncircleTarget = getBoolean("mobs.creeper.encircle-target", creeperEncircleTarget); + } + + public boolean dolphinRidable = false; + public boolean dolphinControllable = true; + public int dolphinSpitCooldown = 20; + public float dolphinSpitSpeed = 1.0F; + public float dolphinSpitDamage = 2.0F; + public double dolphinMaxHealth = 10.0D; + public double dolphinScale = 1.0D; + public boolean dolphinDisableTreasureSearching = false; + public boolean dolphinTakeDamageFromWater = false; + public double dolphinNaturallyAggressiveToPlayersChance = 0.0D; + public boolean dolphinAlwaysDropExp = false; + private void dolphinSettings() { + dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); + dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); + dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown); + dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed); + dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.dolphin.attributes.max-health", dolphinMaxHealth); + set("mobs.dolphin.attributes.max-health", null); + set("mobs.dolphin.attributes.max_health", oldValue); + } + dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth); + dolphinScale = Mth.clamp(getDouble("mobs.dolphin.attributes.scale", dolphinScale), 0.0625D, 16.0D); + dolphinDisableTreasureSearching = getBoolean("mobs.dolphin.disable-treasure-searching", dolphinDisableTreasureSearching); + dolphinTakeDamageFromWater = getBoolean("mobs.dolphin.takes-damage-from-water", dolphinTakeDamageFromWater); + dolphinNaturallyAggressiveToPlayersChance = getDouble("mobs.dolphin.naturally-aggressive-to-players-chance", dolphinNaturallyAggressiveToPlayersChance); + dolphinAlwaysDropExp = getBoolean("mobs.dolphin.always-drop-exp", dolphinAlwaysDropExp); + } + + public boolean donkeyRidableInWater = false; + public double donkeyMaxHealthMin = 15.0D; + public double donkeyMaxHealthMax = 30.0D; + public double donkeyJumpStrengthMin = 0.5D; + public double donkeyJumpStrengthMax = 0.5D; + public double donkeyMovementSpeedMin = 0.175D; + public double donkeyMovementSpeedMax = 0.175D; + public int donkeyBreedingTicks = 6000; + public boolean donkeyTakeDamageFromWater = false; + public boolean donkeyAlwaysDropExp = false; + private void donkeySettings() { + donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater); + if (PurpurConfig.version < 10) { + double oldMin = getDouble("mobs.donkey.attributes.max-health.min", donkeyMaxHealthMin); + double oldMax = getDouble("mobs.donkey.attributes.max-health.max", donkeyMaxHealthMax); + set("mobs.donkey.attributes.max-health", null); + set("mobs.donkey.attributes.max_health.min", oldMin); + set("mobs.donkey.attributes.max_health.max", oldMax); + } + donkeyMaxHealthMin = getDouble("mobs.donkey.attributes.max_health.min", donkeyMaxHealthMin); + donkeyMaxHealthMax = getDouble("mobs.donkey.attributes.max_health.max", donkeyMaxHealthMax); + donkeyJumpStrengthMin = getDouble("mobs.donkey.attributes.jump_strength.min", donkeyJumpStrengthMin); + donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax); + donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin); + donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax); + donkeyBreedingTicks = getInt("mobs.donkey.breeding-delay-ticks", donkeyBreedingTicks); + donkeyTakeDamageFromWater = getBoolean("mobs.donkey.takes-damage-from-water", donkeyTakeDamageFromWater); + donkeyAlwaysDropExp = getBoolean("mobs.donkey.always-drop-exp", donkeyAlwaysDropExp); + } + + public boolean drownedRidable = false; + public boolean drownedRidableInWater = true; + public boolean drownedControllable = true; + public double drownedMaxHealth = 20.0D; + public double drownedScale = 1.0D; + public double drownedSpawnReinforcements = 0.1D; + public boolean drownedJockeyOnlyBaby = true; + public double drownedJockeyChance = 0.05D; + public boolean drownedJockeyTryExistingChickens = true; + public boolean drownedTakeDamageFromWater = false; + public boolean drownedBreakDoors = false; + public boolean drownedAlwaysDropExp = false; + private void drownedSettings() { + drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); + drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); + drownedControllable = getBoolean("mobs.drowned.controllable", drownedControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.drowned.attributes.max-health", drownedMaxHealth); + set("mobs.drowned.attributes.max-health", null); + set("mobs.drowned.attributes.max_health", oldValue); + } + drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth); + drownedScale = Mth.clamp(getDouble("mobs.drowned.attributes.scale", drownedScale), 0.0625D, 16.0D); + drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements); + drownedJockeyOnlyBaby = getBoolean("mobs.drowned.jockey.only-babies", drownedJockeyOnlyBaby); + drownedJockeyChance = getDouble("mobs.drowned.jockey.chance", drownedJockeyChance); + drownedJockeyTryExistingChickens = getBoolean("mobs.drowned.jockey.try-existing-chickens", drownedJockeyTryExistingChickens); + drownedTakeDamageFromWater = getBoolean("mobs.drowned.takes-damage-from-water", drownedTakeDamageFromWater); + drownedBreakDoors = getBoolean("mobs.drowned.can-break-doors", drownedBreakDoors); + drownedAlwaysDropExp = getBoolean("mobs.drowned.always-drop-exp", drownedAlwaysDropExp); + } + + public boolean elderGuardianRidable = false; + public boolean elderGuardianControllable = true; + public double elderGuardianMaxHealth = 80.0D; + public double elderGuardianScale = 1.0D; + public boolean elderGuardianTakeDamageFromWater = false; + public boolean elderGuardianAlwaysDropExp = false; + private void elderGuardianSettings() { + elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); + elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.elder_guardian.attributes.max-health", elderGuardianMaxHealth); + set("mobs.elder_guardian.attributes.max-health", null); + set("mobs.elder_guardian.attributes.max_health", oldValue); + } + elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth); + elderGuardianScale = Mth.clamp(getDouble("mobs.elder_guardian.attributes.scale", elderGuardianScale), 0.0625D, 16.0D); + elderGuardianTakeDamageFromWater = getBoolean("mobs.elder_guardian.takes-damage-from-water", elderGuardianTakeDamageFromWater); + elderGuardianAlwaysDropExp = getBoolean("mobs.elder_guardian.always-drop-exp", elderGuardianAlwaysDropExp); + } + + public boolean enchantmentTableLapisPersists = false; + private void enchantmentTableSettings() { + enchantmentTableLapisPersists = getBoolean("blocks.enchantment-table.lapis-persists", enchantmentTableLapisPersists); + } + + public boolean enderDragonRidable = false; + public boolean enderDragonRidableInWater = true; + public boolean enderDragonControllable = true; + public double enderDragonMaxY = 320D; + public double enderDragonMaxHealth = 200.0D; + public boolean enderDragonAlwaysDropsFullExp = false; + public Boolean enderDragonMobGriefingOverride = null; + public boolean enderDragonTakeDamageFromWater = false; + public boolean enderDragonCanRideVehicles = false; + private void enderDragonSettings() { + enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable); + enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater); + enderDragonControllable = getBoolean("mobs.ender_dragon.controllable", enderDragonControllable); + enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY); + if (PurpurConfig.version < 8) { + double oldValue = getDouble("mobs.ender_dragon.max-health", enderDragonMaxHealth); + set("mobs.ender_dragon.max-health", null); + set("mobs.ender_dragon.attributes.max_health", oldValue); + } else if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.ender_dragon.attributes.max-health", enderDragonMaxHealth); + set("mobs.ender_dragon.attributes.max-health", null); + set("mobs.ender_dragon.attributes.max_health", oldValue); + } + enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth); + enderDragonAlwaysDropsFullExp = getBoolean("mobs.ender_dragon.always-drop-full-exp", enderDragonAlwaysDropsFullExp); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.ender_dragon.bypass-mob-griefing", false); + set("mobs.ender_dragon.bypass-mob-griefing", null); + set("mobs.ender_dragon.mob-griefing-override", oldVal ? true : "default"); + } + enderDragonMobGriefingOverride = getBooleanOrDefault("mobs.ender_dragon.mob-griefing-override", enderDragonMobGriefingOverride); + enderDragonTakeDamageFromWater = getBoolean("mobs.ender_dragon.takes-damage-from-water", enderDragonTakeDamageFromWater); + enderDragonCanRideVehicles = getBoolean("mobs.ender_dragon.can-ride-vehicles", enderDragonCanRideVehicles); + } + + public boolean endermanRidable = false; + public boolean endermanRidableInWater = true; + public boolean endermanControllable = true; + public double endermanMaxHealth = 40.0D; + public double endermanScale = 1.0D; + public boolean endermanAllowGriefing = true; + public boolean endermanDespawnEvenWithBlock = false; + public Boolean endermanMobGriefingOverride = null; + public boolean endermanTakeDamageFromWater = true; + public boolean endermanAggroEndermites = true; + public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false; + public boolean endermanDisableStareAggro = false; + public boolean endermanIgnoreProjectiles = false; + public boolean endermanAlwaysDropExp = false; + private void endermanSettings() { + endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); + endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); + endermanControllable = getBoolean("mobs.enderman.controllable", endermanControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.enderman.attributes.max-health", endermanMaxHealth); + set("mobs.enderman.attributes.max-health", null); + set("mobs.enderman.attributes.max_health", oldValue); + } + if (PurpurConfig.version < 15) { + // remove old option + set("mobs.enderman.aggressive-towards-spawned-endermites", null); + } + endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth); + endermanScale = Mth.clamp(getDouble("mobs.enderman.attributes.scale", endermanScale), 0.0625D, 16.0D); + endermanAllowGriefing = getBoolean("mobs.enderman.allow-griefing", endermanAllowGriefing); + endermanDespawnEvenWithBlock = getBoolean("mobs.enderman.can-despawn-with-held-block", endermanDespawnEvenWithBlock); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.enderman.bypass-mob-griefing", false); + set("mobs.enderman.bypass-mob-griefing", null); + set("mobs.enderman.mob-griefing-override", oldVal ? true : "default"); + } + endermanMobGriefingOverride = getBooleanOrDefault("mobs.enderman.mob-griefing-override", endermanMobGriefingOverride); + endermanTakeDamageFromWater = getBoolean("mobs.enderman.takes-damage-from-water", endermanTakeDamageFromWater); + endermanAggroEndermites = getBoolean("mobs.enderman.aggressive-towards-endermites", endermanAggroEndermites); + endermanAggroEndermitesOnlyIfPlayerSpawned = getBoolean("mobs.enderman.aggressive-towards-endermites-only-spawned-by-player-thrown-ender-pearls", endermanAggroEndermitesOnlyIfPlayerSpawned); + endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro); + endermanIgnoreProjectiles = getBoolean("mobs.enderman.ignore-projectiles", endermanIgnoreProjectiles); + endermanAlwaysDropExp = getBoolean("mobs.enderman.always-drop-exp", endermanAlwaysDropExp); + } + + public boolean endermiteRidable = false; + public boolean endermiteRidableInWater = true; + public boolean endermiteControllable = true; + public double endermiteMaxHealth = 8.0D; + public double endermiteScale = 1.0D; + public boolean endermiteTakeDamageFromWater = false; + public boolean endermiteAlwaysDropExp = false; + private void endermiteSettings() { + endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); + endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); + endermiteControllable = getBoolean("mobs.endermite.controllable", endermiteControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.endermite.attributes.max-health", endermiteMaxHealth); + set("mobs.endermite.attributes.max-health", null); + set("mobs.endermite.attributes.max_health", oldValue); + } + endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth); + endermiteScale = Mth.clamp(getDouble("mobs.endermite.attributes.scale", endermiteScale), 0.0625D, 16.0D); + endermiteTakeDamageFromWater = getBoolean("mobs.endermite.takes-damage-from-water", endermiteTakeDamageFromWater); + endermiteAlwaysDropExp = getBoolean("mobs.endermite.always-drop-exp", endermiteAlwaysDropExp); + } + + public boolean evokerRidable = false; + public boolean evokerRidableInWater = true; + public boolean evokerControllable = true; + public double evokerMaxHealth = 24.0D; + public double evokerScale = 1.0D; + public Boolean evokerMobGriefingOverride = null; + public boolean evokerTakeDamageFromWater = false; + public boolean evokerAlwaysDropExp = false; + private void evokerSettings() { + evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); + evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); + evokerControllable = getBoolean("mobs.evoker.controllable", evokerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.evoker.attributes.max-health", evokerMaxHealth); + set("mobs.evoker.attributes.max-health", null); + set("mobs.evoker.attributes.max_health", oldValue); + } + evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); + evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.evoker.bypass-mob-griefing", false); + set("mobs.evoker.bypass-mob-griefing", null); + set("mobs.evoker.mob-griefing-override", oldVal ? true : "default"); + } + evokerMobGriefingOverride = getBooleanOrDefault("mobs.evoker.mob-griefing-override", evokerMobGriefingOverride); + evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater); + evokerAlwaysDropExp = getBoolean("mobs.evoker.always-drop-exp", evokerAlwaysDropExp); + } + + public boolean foxRidable = false; + public boolean foxRidableInWater = true; + public boolean foxControllable = true; + public double foxMaxHealth = 10.0D; + public double foxScale = 1.0D; + public boolean foxTypeChangesWithTulips = false; + public int foxBreedingTicks = 6000; + public Boolean foxMobGriefingOverride = null; + public boolean foxTakeDamageFromWater = false; + public boolean foxAlwaysDropExp = false; + private void foxSettings() { + foxRidable = getBoolean("mobs.fox.ridable", foxRidable); + foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); + foxControllable = getBoolean("mobs.fox.controllable", foxControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.fox.attributes.max-health", foxMaxHealth); + set("mobs.fox.attributes.max-health", null); + set("mobs.fox.attributes.max_health", oldValue); + } + foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth); + foxScale = Mth.clamp(getDouble("mobs.fox.attributes.scale", foxScale), 0.0625D, 16.0D); + foxTypeChangesWithTulips = getBoolean("mobs.fox.tulips-change-type", foxTypeChangesWithTulips); + foxBreedingTicks = getInt("mobs.fox.breeding-delay-ticks", foxBreedingTicks); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.fox.bypass-mob-griefing", false); + set("mobs.fox.bypass-mob-griefing", null); + set("mobs.fox.mob-griefing-override", oldVal ? true : "default"); + } + foxMobGriefingOverride = getBooleanOrDefault("mobs.fox.mob-griefing-override", foxMobGriefingOverride); + foxTakeDamageFromWater = getBoolean("mobs.fox.takes-damage-from-water", foxTakeDamageFromWater); + foxAlwaysDropExp = getBoolean("mobs.fox.always-drop-exp", foxAlwaysDropExp); + } + + public boolean frogRidable = false; + public boolean frogRidableInWater = true; + public boolean frogControllable = true; + public float frogRidableJumpHeight = 0.65F; + public int frogBreedingTicks = 6000; + private void frogSettings() { + frogRidable = getBoolean("mobs.frog.ridable", frogRidable); + frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater); + frogControllable = getBoolean("mobs.frog.controllable", frogControllable); + frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight); + frogBreedingTicks = getInt("mobs.frog.breeding-delay-ticks", frogBreedingTicks); + } + + public boolean ghastRidable = false; + public boolean ghastRidableInWater = true; + public boolean ghastControllable = true; + public double ghastMaxY = 320D; + public double ghastMaxHealth = 10.0D; + public double ghastScale = 1.0D; + public boolean ghastTakeDamageFromWater = false; + public boolean ghastAlwaysDropExp = false; + private void ghastSettings() { + ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); + ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); + ghastControllable = getBoolean("mobs.ghast.controllable", ghastControllable); + ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.ghast.attributes.max-health", ghastMaxHealth); + set("mobs.ghast.attributes.max-health", null); + set("mobs.ghast.attributes.max_health", oldValue); + } + ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth); + ghastScale = Mth.clamp(getDouble("mobs.ghast.attributes.scale", ghastScale), 0.0625D, 16.0D); + ghastTakeDamageFromWater = getBoolean("mobs.ghast.takes-damage-from-water", ghastTakeDamageFromWater); + ghastAlwaysDropExp = getBoolean("mobs.ghast.always-drop-exp", ghastAlwaysDropExp); + } + + public boolean giantRidable = false; + public boolean giantRidableInWater = true; + public boolean giantControllable = true; + public double giantMovementSpeed = 0.5D; + public double giantAttackDamage = 50.0D; + public double giantMaxHealth = 100.0D; + public double giantScale = 1.0D; + public float giantStepHeight = 2.0F; + public float giantJumpHeight = 1.0F; + public boolean giantHaveAI = false; + public boolean giantHaveHostileAI = false; + public boolean giantTakeDamageFromWater = false; + public boolean giantAlwaysDropExp = false; + private void giantSettings() { + giantRidable = getBoolean("mobs.giant.ridable", giantRidable); + giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); + giantControllable = getBoolean("mobs.giant.controllable", giantControllable); + giantMovementSpeed = getDouble("mobs.giant.movement-speed", giantMovementSpeed); + giantAttackDamage = getDouble("mobs.giant.attack-damage", giantAttackDamage); + if (PurpurConfig.version < 8) { + double oldValue = getDouble("mobs.giant.max-health", giantMaxHealth); + set("mobs.giant.max-health", null); + set("mobs.giant.attributes.max_health", oldValue); + } else if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.giant.attributes.max-health", giantMaxHealth); + set("mobs.giant.attributes.max-health", null); + set("mobs.giant.attributes.max_health", oldValue); + } + giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth); + giantScale = Mth.clamp(getDouble("mobs.giant.attributes.scale", giantScale), 0.0625D, 16.0D); + giantStepHeight = (float) getDouble("mobs.giant.step-height", giantStepHeight); + giantJumpHeight = (float) getDouble("mobs.giant.jump-height", giantJumpHeight); + giantHaveAI = getBoolean("mobs.giant.have-ai", giantHaveAI); + giantHaveHostileAI = getBoolean("mobs.giant.have-hostile-ai", giantHaveHostileAI); + giantTakeDamageFromWater = getBoolean("mobs.giant.takes-damage-from-water", giantTakeDamageFromWater); + giantAlwaysDropExp = getBoolean("mobs.giant.always-drop-exp", giantAlwaysDropExp); + } + + public boolean glowSquidRidable = false; + public boolean glowSquidControllable = true; + public double glowSquidMaxHealth = 10.0D; + public double glowSquidScale = 1.0D; + public boolean glowSquidsCanFly = false; + public boolean glowSquidTakeDamageFromWater = false; + public boolean glowSquidAlwaysDropExp = false; + private void glowSquidSettings() { + glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable); + glowSquidControllable = getBoolean("mobs.glow_squid.controllable", glowSquidControllable); + glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth); + glowSquidScale = Mth.clamp(getDouble("mobs.glow_squid.attributes.scale", glowSquidScale), 0.0625D, 16.0D); + glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly); + glowSquidTakeDamageFromWater = getBoolean("mobs.glow_squid.takes-damage-from-water", glowSquidTakeDamageFromWater); + glowSquidAlwaysDropExp = getBoolean("mobs.glow_squid.always-drop-exp", glowSquidAlwaysDropExp); + } + + public boolean goatRidable = false; + public boolean goatRidableInWater = true; + public boolean goatControllable = true; + public double goatMaxHealth = 10.0D; + public double goatScale = 1.0D; + public int goatBreedingTicks = 6000; + public boolean goatTakeDamageFromWater = false; + public boolean goatAlwaysDropExp = false; + private void goatSettings() { + goatRidable = getBoolean("mobs.goat.ridable", goatRidable); + goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater); + goatControllable = getBoolean("mobs.goat.controllable", goatControllable); + goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth); + goatScale = Mth.clamp(getDouble("mobs.goat.attributes.scale", goatScale), 0.0625D, 16.0D); + goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks); + goatTakeDamageFromWater = getBoolean("mobs.goat.takes-damage-from-water", goatTakeDamageFromWater); + goatAlwaysDropExp = getBoolean("mobs.goat.always-drop-exp", goatAlwaysDropExp); + } + + public boolean guardianRidable = false; + public boolean guardianControllable = true; + public double guardianMaxHealth = 30.0D; + public double guardianScale = 1.0D; + public boolean guardianTakeDamageFromWater = false; + public boolean guardianAlwaysDropExp = false; + private void guardianSettings() { + guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); + guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.guardian.attributes.max-health", guardianMaxHealth); + set("mobs.guardian.attributes.max-health", null); + set("mobs.guardian.attributes.max_health", oldValue); + } + guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth); + guardianScale = Mth.clamp(getDouble("mobs.guardian.attributes.scale", guardianScale), 0.0625D, 16.0D); + guardianTakeDamageFromWater = getBoolean("mobs.guardian.takes-damage-from-water", guardianTakeDamageFromWater); + guardianAlwaysDropExp = getBoolean("mobs.guardian.always-drop-exp", guardianAlwaysDropExp); + } + + public boolean forceHalloweenSeason = false; + public float chanceHeadHalloweenOnEntity = 0.25F; + private void halloweenSetting() { + forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason); + chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity); + } + + public boolean happyGhastRidableInWater = false; + public double happyGhastMaxHealth = 20.0D; + public double happyGhastTemptRange = 16.0D; + public double happyGhastFlyingSpeed = 0.05D; + public double happyGhastMovementSpeed = 0.05D; + public double happyGhastFollowRange = 16.0D; + public double happyGhastCameraDistance = 8.0D; + public double happyGhastScale = 1.0D; + public boolean happyGhastTakeDamageFromWater = false; + public boolean happyGhastAlwaysDropExp = false; + private void happyGhastSettings() { + happyGhastRidableInWater = getBoolean("mobs.happy_ghast.ridable-in-water", happyGhastRidableInWater); + happyGhastMaxHealth = getDouble("mobs.happy_ghast.attributes.max_health", happyGhastMaxHealth); + happyGhastTemptRange = getDouble("mobs.happy_ghast.attributes.tempt_range", happyGhastTemptRange); + happyGhastFlyingSpeed = getDouble("mobs.happy_ghast.attributes.flying_speed", happyGhastFlyingSpeed); + happyGhastMovementSpeed = getDouble("mobs.happy_ghast.attributes.movement_speed", happyGhastMovementSpeed); + happyGhastFollowRange = getDouble("mobs.happy_ghast.attributes.follow_range", happyGhastFollowRange); + happyGhastCameraDistance = getDouble("mobs.happy_ghast.attributes.camera_distance", happyGhastCameraDistance); + happyGhastScale = Mth.clamp(getDouble("mobs.happy_ghast.attributes.scale", happyGhastScale), 0.0625D, 1.0D); + happyGhastTakeDamageFromWater = getBoolean("mobs.happy_ghast.takes-damage-from-water", happyGhastTakeDamageFromWater); + happyGhastAlwaysDropExp = getBoolean("mobs.happy_ghast.always-drop-exp", happyGhastAlwaysDropExp); + } + + public boolean hoglinRidable = false; + public boolean hoglinRidableInWater = true; + public boolean hoglinControllable = true; + public double hoglinMaxHealth = 40.0D; + public double hoglinScale = 1.0D; + public int hoglinBreedingTicks = 6000; + public boolean hoglinTakeDamageFromWater = false; + public boolean hoglinAlwaysDropExp = false; + private void hoglinSettings() { + hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); + hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); + hoglinControllable = getBoolean("mobs.hoglin.controllable", hoglinControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.hoglin.attributes.max-health", hoglinMaxHealth); + set("mobs.hoglin.attributes.max-health", null); + set("mobs.hoglin.attributes.max_health", oldValue); + } + hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth); + hoglinScale = Mth.clamp(getDouble("mobs.hoglin.attributes.scale", hoglinScale), 0.0625D, 16.0D); + hoglinBreedingTicks = getInt("mobs.hoglin.breeding-delay-ticks", hoglinBreedingTicks); + hoglinTakeDamageFromWater = getBoolean("mobs.hoglin.takes-damage-from-water", hoglinTakeDamageFromWater); + hoglinAlwaysDropExp = getBoolean("mobs.hoglin.always-drop-exp", hoglinAlwaysDropExp); + } + + public boolean horseRidableInWater = false; + public double horseMaxHealthMin = 15.0D; + public double horseMaxHealthMax = 30.0D; + public double horseJumpStrengthMin = 0.4D; + public double horseJumpStrengthMax = 1.0D; + public double horseMovementSpeedMin = 0.1125D; + public double horseMovementSpeedMax = 0.3375D; + public int horseBreedingTicks = 6000; + public boolean horseTakeDamageFromWater = false; + public boolean horseAlwaysDropExp = false; + private void horseSettings() { + horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater); + if (PurpurConfig.version < 10) { + double oldMin = getDouble("mobs.horse.attributes.max-health.min", horseMaxHealthMin); + double oldMax = getDouble("mobs.horse.attributes.max-health.max", horseMaxHealthMax); + set("mobs.horse.attributes.max-health", null); + set("mobs.horse.attributes.max_health.min", oldMin); + set("mobs.horse.attributes.max_health.max", oldMax); + } + horseMaxHealthMin = getDouble("mobs.horse.attributes.max_health.min", horseMaxHealthMin); + horseMaxHealthMax = getDouble("mobs.horse.attributes.max_health.max", horseMaxHealthMax); + horseJumpStrengthMin = getDouble("mobs.horse.attributes.jump_strength.min", horseJumpStrengthMin); + horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax); + horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin); + horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax); + horseBreedingTicks = getInt("mobs.horse.breeding-delay-ticks", horseBreedingTicks); + horseTakeDamageFromWater = getBoolean("mobs.horse.takes-damage-from-water", horseTakeDamageFromWater); + horseAlwaysDropExp = getBoolean("mobs.horse.always-drop-exp", horseAlwaysDropExp); + } + + public boolean huskRidable = false; + public boolean huskRidableInWater = true; + public boolean huskControllable = true; + public double huskMaxHealth = 20.0D; + public double huskScale = 1.0D; + public double huskSpawnReinforcements = 0.1D; + public boolean huskJockeyOnlyBaby = true; + public double huskJockeyChance = 0.05D; + public boolean huskJockeyTryExistingChickens = true; + public boolean huskTakeDamageFromWater = false; + public boolean huskAlwaysDropExp = false; + private void huskSettings() { + huskRidable = getBoolean("mobs.husk.ridable", huskRidable); + huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); + huskControllable = getBoolean("mobs.husk.controllable", huskControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.husk.attributes.max-health", huskMaxHealth); + set("mobs.husk.attributes.max-health", null); + set("mobs.husk.attributes.max_health", oldValue); + } + huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth); + huskScale = Mth.clamp(getDouble("mobs.husk.attributes.scale", huskScale), 0.0625D, 16.0D); + huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements); + huskJockeyOnlyBaby = getBoolean("mobs.husk.jockey.only-babies", huskJockeyOnlyBaby); + huskJockeyChance = getDouble("mobs.husk.jockey.chance", huskJockeyChance); + huskJockeyTryExistingChickens = getBoolean("mobs.husk.jockey.try-existing-chickens", huskJockeyTryExistingChickens); + huskTakeDamageFromWater = getBoolean("mobs.husk.takes-damage-from-water", huskTakeDamageFromWater); + huskAlwaysDropExp = getBoolean("mobs.husk.always-drop-exp", huskAlwaysDropExp); + } + + public boolean illusionerRidable = false; + public boolean illusionerRidableInWater = true; + public boolean illusionerControllable = true; + public double illusionerMovementSpeed = 0.5D; + public double illusionerFollowRange = 18.0D; + public double illusionerMaxHealth = 32.0D; + public double illusionerScale = 1.0D; + public boolean illusionerTakeDamageFromWater = false; + public boolean illusionerAlwaysDropExp = false; + private void illusionerSettings() { + illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); + illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); + illusionerControllable = getBoolean("mobs.illusioner.controllable", illusionerControllable); + illusionerMovementSpeed = getDouble("mobs.illusioner.movement-speed", illusionerMovementSpeed); + illusionerFollowRange = getDouble("mobs.illusioner.follow-range", illusionerFollowRange); + if (PurpurConfig.version < 8) { + double oldValue = getDouble("mobs.illusioner.max-health", illusionerMaxHealth); + set("mobs.illusioner.max-health", null); + set("mobs.illusioner.attributes.max_health", oldValue); + } else if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.illusioner.attributes.max-health", illusionerMaxHealth); + set("mobs.illusioner.attributes.max-health", null); + set("mobs.illusioner.attributes.max_health", oldValue); + } + illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth); + illusionerScale = Mth.clamp(getDouble("mobs.illusioner.attributes.scale", illusionerScale), 0.0625D, 16.0D); + illusionerTakeDamageFromWater = getBoolean("mobs.illusioner.takes-damage-from-water", illusionerTakeDamageFromWater); + illusionerAlwaysDropExp = getBoolean("mobs.illusioner.always-drop-exp", illusionerAlwaysDropExp); + } + + public boolean ironGolemRidable = false; + public boolean ironGolemRidableInWater = true; + public boolean ironGolemControllable = true; + public boolean ironGolemCanSwim = false; + public double ironGolemMaxHealth = 100.0D; + public double ironGolemScale = 1.0D; + public boolean ironGolemTakeDamageFromWater = false; + public boolean ironGolemPoppyCalm = false; + public boolean ironGolemHealCalm = false; + public boolean ironGolemAlwaysDropExp = false; + private void ironGolemSettings() { + ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); + ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); + ironGolemControllable = getBoolean("mobs.iron_golem.controllable", ironGolemControllable); + ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.iron_golem.attributes.max-health", ironGolemMaxHealth); + set("mobs.iron_golem.attributes.max-health", null); + set("mobs.iron_golem.attributes.max_health", oldValue); + } + ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth); + ironGolemScale = Mth.clamp(getDouble("mobs.iron_golem.attributes.scale", ironGolemScale), 0.0625D, 16.0D); + ironGolemTakeDamageFromWater = getBoolean("mobs.iron_golem.takes-damage-from-water", ironGolemTakeDamageFromWater); + ironGolemPoppyCalm = getBoolean("mobs.iron_golem.poppy-calms-anger", ironGolemPoppyCalm); + ironGolemHealCalm = getBoolean("mobs.iron_golem.healing-calms-anger", ironGolemHealCalm); + ironGolemAlwaysDropExp = getBoolean("mobs.iron_golem.always-drop-exp", ironGolemAlwaysDropExp); + } + + public boolean llamaRidable = false; + public boolean llamaRidableInWater = false; + public boolean llamaControllable = true; + public double llamaMaxHealthMin = 15.0D; + public double llamaMaxHealthMax = 30.0D; + public double llamaJumpStrengthMin = 0.5D; + public double llamaJumpStrengthMax = 0.5D; + public double llamaMovementSpeedMin = 0.175D; + public double llamaMovementSpeedMax = 0.175D; + public int llamaBreedingTicks = 6000; + public boolean llamaTakeDamageFromWater = false; + public boolean llamaJoinCaravans = true; + public boolean llamaAlwaysDropExp = false; + private void llamaSettings() { + llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable); + llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater); + llamaControllable = getBoolean("mobs.llama.controllable", llamaControllable); + if (PurpurConfig.version < 10) { + double oldMin = getDouble("mobs.llama.attributes.max-health.min", llamaMaxHealthMin); + double oldMax = getDouble("mobs.llama.attributes.max-health.max", llamaMaxHealthMax); + set("mobs.llama.attributes.max-health", null); + set("mobs.llama.attributes.max_health.min", oldMin); + set("mobs.llama.attributes.max_health.max", oldMax); + } + llamaMaxHealthMin = getDouble("mobs.llama.attributes.max_health.min", llamaMaxHealthMin); + llamaMaxHealthMax = getDouble("mobs.llama.attributes.max_health.max", llamaMaxHealthMax); + llamaJumpStrengthMin = getDouble("mobs.llama.attributes.jump_strength.min", llamaJumpStrengthMin); + llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax); + llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin); + llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax); + llamaBreedingTicks = getInt("mobs.llama.breeding-delay-ticks", llamaBreedingTicks); + llamaTakeDamageFromWater = getBoolean("mobs.llama.takes-damage-from-water", llamaTakeDamageFromWater); + llamaJoinCaravans = getBoolean("mobs.llama.join-caravans", llamaJoinCaravans); + llamaAlwaysDropExp = getBoolean("mobs.llama.always-drop-exp", llamaAlwaysDropExp); + } + + public boolean magmaCubeRidable = false; + public boolean magmaCubeRidableInWater = true; + public boolean magmaCubeControllable = true; + public String magmaCubeMaxHealth = "size * size"; + public String magmaCubeAttackDamage = "size"; + public Map magmaCubeMaxHealthCache = new HashMap<>(); + public Map magmaCubeAttackDamageCache = new HashMap<>(); + public boolean magmaCubeTakeDamageFromWater = false; + public boolean magmaCubeAlwaysDropExp = false; + private void magmaCubeSettings() { + magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable); + magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater); + magmaCubeControllable = getBoolean("mobs.magma_cube.controllable", magmaCubeControllable); + if (PurpurConfig.version < 10) { + String oldValue = getString("mobs.magma_cube.attributes.max-health", magmaCubeMaxHealth); + set("mobs.magma_cube.attributes.max-health", null); + set("mobs.magma_cube.attributes.max_health", oldValue); + } + magmaCubeMaxHealth = getString("mobs.magma_cube.attributes.max_health", magmaCubeMaxHealth); + magmaCubeAttackDamage = getString("mobs.magma_cube.attributes.attack_damage", magmaCubeAttackDamage); + magmaCubeMaxHealthCache.clear(); + magmaCubeAttackDamageCache.clear(); + magmaCubeTakeDamageFromWater = getBoolean("mobs.magma_cube.takes-damage-from-water", magmaCubeTakeDamageFromWater); + magmaCubeAlwaysDropExp = getBoolean("mobs.magma_cube.always-drop-exp", magmaCubeAlwaysDropExp); + } + + public boolean mooshroomRidable = false; + public boolean mooshroomRidableInWater = true; + public boolean mooshroomControllable = true; + public double mooshroomMaxHealth = 10.0D; + public double mooshroomScale = 1.0D; + public int mooshroomBreedingTicks = 6000; + public boolean mooshroomTakeDamageFromWater = false; + public boolean mooshroomAlwaysDropExp = false; + private void mooshroomSettings() { + mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); + mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); + mooshroomControllable = getBoolean("mobs.mooshroom.controllable", mooshroomControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.mooshroom.attributes.max-health", mooshroomMaxHealth); + set("mobs.mooshroom.attributes.max-health", null); + set("mobs.mooshroom.attributes.max_health", oldValue); + } + mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth); + mooshroomScale = Mth.clamp(getDouble("mobs.mooshroom.attributes.scale", mooshroomScale), 0.0625D, 16.0D); + mooshroomBreedingTicks = getInt("mobs.mooshroom.breeding-delay-ticks", mooshroomBreedingTicks); + mooshroomTakeDamageFromWater = getBoolean("mobs.mooshroom.takes-damage-from-water", mooshroomTakeDamageFromWater); + mooshroomAlwaysDropExp = getBoolean("mobs.mooshroom.always-drop-exp", mooshroomAlwaysDropExp); + } + + public boolean muleRidableInWater = false; + public double muleMaxHealthMin = 15.0D; + public double muleMaxHealthMax = 30.0D; + public double muleJumpStrengthMin = 0.5D; + public double muleJumpStrengthMax = 0.5D; + public double muleMovementSpeedMin = 0.175D; + public double muleMovementSpeedMax = 0.175D; + public int muleBreedingTicks = 6000; + public boolean muleTakeDamageFromWater = false; + public boolean muleAlwaysDropExp = false; + private void muleSettings() { + muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater); + if (PurpurConfig.version < 10) { + double oldMin = getDouble("mobs.mule.attributes.max-health.min", muleMaxHealthMin); + double oldMax = getDouble("mobs.mule.attributes.max-health.max", muleMaxHealthMax); + set("mobs.mule.attributes.max-health", null); + set("mobs.mule.attributes.max_health.min", oldMin); + set("mobs.mule.attributes.max_health.max", oldMax); + } + muleMaxHealthMin = getDouble("mobs.mule.attributes.max_health.min", muleMaxHealthMin); + muleMaxHealthMax = getDouble("mobs.mule.attributes.max_health.max", muleMaxHealthMax); + muleJumpStrengthMin = getDouble("mobs.mule.attributes.jump_strength.min", muleJumpStrengthMin); + muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax); + muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin); + muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax); + muleBreedingTicks = getInt("mobs.mule.breeding-delay-ticks", muleBreedingTicks); + muleTakeDamageFromWater = getBoolean("mobs.mule.takes-damage-from-water", muleTakeDamageFromWater); + muleAlwaysDropExp = getBoolean("mobs.mule.always-drop-exp", muleAlwaysDropExp); + } + + public double nautilusMaxHealth = 15.0D; + public double nautilusMovementSpeed = 1.0D; + public double nautilusAttackDamage = 3.0D; + public double nautilusKnockbackResistance = 0.3D; + public double nautilusScale = 1.0D; + public int nautilusBreedingTicks = 6000; + public boolean nautilusAlwaysDropExp = false; + private void nautilusSettings() { + nautilusMaxHealth = getDouble("mobs.nautilus.attributes.max_health", nautilusMaxHealth); + nautilusMovementSpeed = getDouble("mobs.nautilus.attributes.movement_speed", nautilusMovementSpeed); + nautilusAttackDamage = getDouble("mobs.nautilus.attributes.attack_damage", nautilusAttackDamage); + nautilusKnockbackResistance = getDouble("mobs.nautilus.attributes.knockback_resistance", nautilusKnockbackResistance); + nautilusScale = Mth.clamp(getDouble("mobs.nautilus.attributes.scale", nautilusScale), 0.0625D, 16.0D); + nautilusBreedingTicks = getInt("mobs.nautilus.breeding-delay-ticks", nautilusBreedingTicks); + nautilusAlwaysDropExp = getBoolean("mobs.nautilus.always-drop-exp", nautilusAlwaysDropExp); + } + + public boolean ocelotRidable = false; + public boolean ocelotRidableInWater = true; + public boolean ocelotControllable = true; + public double ocelotMaxHealth = 10.0D; + public double ocelotScale = 1.0D; + public int ocelotBreedingTicks = 6000; + public boolean ocelotTakeDamageFromWater = false; + public boolean ocelotAlwaysDropExp = false; + public boolean ocelotSpawnUnderSeaLevel = false; + private void ocelotSettings() { + ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); + ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); + ocelotControllable = getBoolean("mobs.ocelot.controllable", ocelotControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.ocelot.attributes.max-health", ocelotMaxHealth); + set("mobs.ocelot.attributes.max-health", null); + set("mobs.ocelot.attributes.max_health", oldValue); + } + ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth); + ocelotScale = Mth.clamp(getDouble("mobs.ocelot.attributes.scale", ocelotScale), 0.0625D, 16.0D); + ocelotBreedingTicks = getInt("mobs.ocelot.breeding-delay-ticks", ocelotBreedingTicks); + ocelotTakeDamageFromWater = getBoolean("mobs.ocelot.takes-damage-from-water", ocelotTakeDamageFromWater); + ocelotAlwaysDropExp = getBoolean("mobs.ocelot.always-drop-exp", ocelotAlwaysDropExp); + ocelotSpawnUnderSeaLevel = getBoolean("mobs.ocelot.spawn-below-sea-level", ocelotSpawnUnderSeaLevel); + } + + public boolean pandaRidable = false; + public boolean pandaRidableInWater = true; + public boolean pandaControllable = true; + public double pandaMaxHealth = 20.0D; + public double pandaScale = 1.0D; + public int pandaBreedingTicks = 6000; + public boolean pandaTakeDamageFromWater = false; + public boolean pandaAlwaysDropExp = false; + private void pandaSettings() { + pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); + pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); + pandaControllable = getBoolean("mobs.panda.controllable", pandaControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.panda.attributes.max-health", pandaMaxHealth); + set("mobs.panda.attributes.max-health", null); + set("mobs.panda.attributes.max_health", oldValue); + } + pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth); + pandaScale = Mth.clamp(getDouble("mobs.panda.attributes.scale", pandaScale), 0.0625D, 16.0D); + pandaBreedingTicks = getInt("mobs.panda.breeding-delay-ticks", pandaBreedingTicks); + pandaTakeDamageFromWater = getBoolean("mobs.panda.takes-damage-from-water", pandaTakeDamageFromWater); + pandaAlwaysDropExp = getBoolean("mobs.panda.always-drop-exp", pandaAlwaysDropExp); + } + + public boolean parrotRidable = false; + public boolean parrotRidableInWater = true; + public boolean parrotControllable = true; + public double parrotMaxY = 320D; + public double parrotMaxHealth = 6.0D; + public double parrotScale = 1.0D; + public boolean parrotTakeDamageFromWater = false; + public boolean parrotBreedable = false; + public boolean parrotAlwaysDropExp = false; + private void parrotSettings() { + parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); + parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); + parrotControllable = getBoolean("mobs.parrot.controllable", parrotControllable); + parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.parrot.attributes.max-health", parrotMaxHealth); + set("mobs.parrot.attributes.max-health", null); + set("mobs.parrot.attributes.max_health", oldValue); + } + parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth); + parrotScale = Mth.clamp(getDouble("mobs.parrot.attributes.scale", parrotScale), 0.0625D, 16.0D); + parrotTakeDamageFromWater = getBoolean("mobs.parrot.takes-damage-from-water", parrotTakeDamageFromWater); + parrotBreedable = getBoolean("mobs.parrot.can-breed", parrotBreedable); + parrotAlwaysDropExp = getBoolean("mobs.parrot.always-drop-exp", parrotAlwaysDropExp); + } + + public boolean phantomRidable = false; + public boolean phantomRidableInWater = true; + public boolean phantomControllable = true; + public double phantomMaxY = 320D; + public float phantomFlameDamage = 1.0F; + public int phantomFlameFireTime = 8; + public boolean phantomAllowGriefing = false; + public String phantomMaxHealth = "20.0"; + public String phantomAttackDamage = "6 + size"; + public Map phantomMaxHealthCache = new HashMap<>(); + public Map phantomAttackDamageCache = new HashMap<>(); + public double phantomAttackedByCrystalRadius = 0.0D; + public float phantomAttackedByCrystalDamage = 1.0F; + public double phantomOrbitCrystalRadius = 0.0D; + public int phantomSpawnMinSkyDarkness = 5; + public boolean phantomSpawnOnlyAboveSeaLevel = true; + public boolean phantomSpawnOnlyWithVisibleSky = true; + public double phantomSpawnLocalDifficultyChance = 3.0D; + public int phantomSpawnMinPerAttempt = 1; + public int phantomSpawnMaxPerAttempt = -1; + public int phantomBurnInLight = 0; + public boolean phantomIgnorePlayersWithTorch = false; + public boolean phantomBurnInDaylight = true; + public boolean phantomFlamesOnSwoop = false; + public boolean phantomTakeDamageFromWater = false; + public boolean phantomAlwaysDropExp = false; + public int phantomMinSize = 0; + public int phantomMaxSize = 0; + private void phantomSettings() { + phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable); + phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater); + phantomControllable = getBoolean("mobs.phantom.controllable", phantomControllable); + phantomMaxY = getDouble("mobs.phantom.ridable-max-y", phantomMaxY); + phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage); + phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime); + phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.phantom.attributes.max-health", Double.parseDouble(phantomMaxHealth)); + set("mobs.phantom.attributes.max-health", null); + set("mobs.phantom.attributes.max_health", String.valueOf(oldValue)); + } + if (PurpurConfig.version < 25) { + double oldValue = getDouble("mobs.phantom.attributes.max_health", Double.parseDouble(phantomMaxHealth)); + set("mobs.phantom.attributes.max_health", String.valueOf(oldValue)); + } + phantomMaxHealth = getString("mobs.phantom.attributes.max_health", phantomMaxHealth); + phantomAttackDamage = getString("mobs.phantom.attributes.attack_damage", phantomAttackDamage); + phantomMaxHealthCache.clear(); + phantomAttackDamageCache.clear(); + phantomAttackedByCrystalRadius = getDouble("mobs.phantom.attacked-by-crystal-range", phantomAttackedByCrystalRadius); + phantomAttackedByCrystalDamage = (float) getDouble("mobs.phantom.attacked-by-crystal-damage", phantomAttackedByCrystalDamage); + phantomOrbitCrystalRadius = getDouble("mobs.phantom.orbit-crystal-radius", phantomOrbitCrystalRadius); + phantomSpawnMinSkyDarkness = getInt("mobs.phantom.spawn.min-sky-darkness", phantomSpawnMinSkyDarkness); + phantomSpawnOnlyAboveSeaLevel = getBoolean("mobs.phantom.spawn.only-above-sea-level", phantomSpawnOnlyAboveSeaLevel); + phantomSpawnOnlyWithVisibleSky = getBoolean("mobs.phantom.spawn.only-with-visible-sky", phantomSpawnOnlyWithVisibleSky); + phantomSpawnLocalDifficultyChance = getDouble("mobs.phantom.spawn.local-difficulty-chance", phantomSpawnLocalDifficultyChance); + phantomSpawnMinPerAttempt = getInt("mobs.phantom.spawn.per-attempt.min", phantomSpawnMinPerAttempt); + phantomSpawnMaxPerAttempt = getInt("mobs.phantom.spawn.per-attempt.max", phantomSpawnMaxPerAttempt); + phantomBurnInLight = getInt("mobs.phantom.burn-in-light", phantomBurnInLight); + phantomBurnInDaylight = getBoolean("mobs.phantom.burn-in-daylight", phantomBurnInDaylight); + phantomIgnorePlayersWithTorch = getBoolean("mobs.phantom.ignore-players-with-torch", phantomIgnorePlayersWithTorch); + phantomFlamesOnSwoop = getBoolean("mobs.phantom.flames-on-swoop", phantomFlamesOnSwoop); + phantomTakeDamageFromWater = getBoolean("mobs.phantom.takes-damage-from-water", phantomTakeDamageFromWater); + phantomAlwaysDropExp = getBoolean("mobs.phantom.always-drop-exp", phantomAlwaysDropExp); + phantomMinSize = Mth.clamp(getInt("mobs.phantom.size.min", phantomMinSize), 0, 64); + phantomMaxSize = Mth.clamp(getInt("mobs.phantom.size.max", phantomMaxSize), 0, 64); + if (phantomMinSize > phantomMaxSize) { + phantomMinSize = phantomMinSize ^ phantomMaxSize; + phantomMaxSize = phantomMinSize ^ phantomMaxSize; + phantomMinSize = phantomMinSize ^ phantomMaxSize; + } + } + + public boolean pigRidable = false; + public boolean pigRidableInWater = false; + public boolean pigControllable = true; + public double pigMaxHealth = 10.0D; + public double pigScale = 1.0D; + public boolean pigGiveSaddleBack = false; + public int pigBreedingTicks = 6000; + public boolean pigTakeDamageFromWater = false; + public boolean pigAlwaysDropExp = false; + private void pigSettings() { + pigRidable = getBoolean("mobs.pig.ridable", pigRidable); + pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); + pigControllable = getBoolean("mobs.pig.controllable", pigControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.pig.attributes.max-health", pigMaxHealth); + set("mobs.pig.attributes.max-health", null); + set("mobs.pig.attributes.max_health", oldValue); + } + pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth); + pigScale = Mth.clamp(getDouble("mobs.pig.attributes.scale", pigScale), 0.0625D, 16.0D); + pigGiveSaddleBack = getBoolean("mobs.pig.give-saddle-back", pigGiveSaddleBack); + pigBreedingTicks = getInt("mobs.pig.breeding-delay-ticks", pigBreedingTicks); + pigTakeDamageFromWater = getBoolean("mobs.pig.takes-damage-from-water", pigTakeDamageFromWater); + pigAlwaysDropExp = getBoolean("mobs.pig.always-drop-exp", pigAlwaysDropExp); + } + + public boolean piglinRidable = false; + public boolean piglinRidableInWater = true; + public boolean piglinControllable = true; + public double piglinMaxHealth = 16.0D; + public double piglinScale = 1.0D; + public Boolean piglinMobGriefingOverride = null; + public boolean piglinTakeDamageFromWater = false; + public int piglinPortalSpawnModifier = 2000; + public boolean piglinAlwaysDropExp = false; + public double piglinHeadVisibilityPercent = 0.5D; + public boolean piglinIgnoresArmorWithGoldTrim = false; + private void piglinSettings() { + piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); + piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); + piglinControllable = getBoolean("mobs.piglin.controllable", piglinControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.piglin.attributes.max-health", piglinMaxHealth); + set("mobs.piglin.attributes.max-health", null); + set("mobs.piglin.attributes.max_health", oldValue); + } + piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); + piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.piglin.bypass-mob-griefing", false); + set("mobs.piglin.bypass-mob-griefing", null); + set("mobs.piglin.mob-griefing-override", oldVal ? true : "default"); + } + piglinMobGriefingOverride = getBooleanOrDefault("mobs.piglin.mob-griefing-override", piglinMobGriefingOverride); + piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); + piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier); + piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp); + piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent); + piglinIgnoresArmorWithGoldTrim = getBoolean("mobs.piglin.ignores-armor-with-gold-trim", piglinIgnoresArmorWithGoldTrim); + } + + public boolean piglinBruteRidable = false; + public boolean piglinBruteRidableInWater = true; + public boolean piglinBruteControllable = true; + public double piglinBruteMaxHealth = 50.0D; + public double piglinBruteScale = 1.0D; + public boolean piglinBruteTakeDamageFromWater = false; + public boolean piglinBruteAlwaysDropExp = false; + private void piglinBruteSettings() { + piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); + piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); + piglinBruteControllable = getBoolean("mobs.piglin_brute.controllable", piglinBruteControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.piglin_brute.attributes.max-health", piglinBruteMaxHealth); + set("mobs.piglin_brute.attributes.max-health", null); + set("mobs.piglin_brute.attributes.max_health", oldValue); + } + piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth); + piglinBruteScale = Mth.clamp(getDouble("mobs.piglin_brute.attributes.scale", piglinBruteScale), 0.0625D, 16.0D); + piglinBruteTakeDamageFromWater = getBoolean("mobs.piglin_brute.takes-damage-from-water", piglinBruteTakeDamageFromWater); + piglinBruteAlwaysDropExp = getBoolean("mobs.piglin_brute.always-drop-exp", piglinBruteAlwaysDropExp); + } + + public boolean pillagerRidable = false; + public boolean pillagerRidableInWater = true; + public boolean pillagerControllable = true; + public double pillagerMaxHealth = 24.0D; + public double pillagerScale = 1.0D; + public Boolean pillagerMobGriefingOverride = null; + public boolean pillagerTakeDamageFromWater = false; + public boolean pillagerAlwaysDropExp = false; + private void pillagerSettings() { + pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); + pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); + pillagerControllable = getBoolean("mobs.pillager.controllable", pillagerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.pillager.attributes.max-health", pillagerMaxHealth); + set("mobs.pillager.attributes.max-health", null); + set("mobs.pillager.attributes.max_health", oldValue); + } + pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); + pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.pillager.bypass-mob-griefing", false); + set("mobs.pillager.bypass-mob-griefing", null); + set("mobs.pillager.mob-griefing-override", oldVal ? true : "default"); + } + pillagerMobGriefingOverride = getBooleanOrDefault("mobs.pillager.mob-griefing-override", pillagerMobGriefingOverride); + pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater); + pillagerAlwaysDropExp = getBoolean("mobs.pillager.always-drop-exp", pillagerAlwaysDropExp); + } + + public boolean polarBearRidable = false; + public boolean polarBearRidableInWater = true; + public boolean polarBearControllable = true; + public double polarBearMaxHealth = 30.0D; + public double polarBearScale = 1.0D; + public String polarBearBreedableItemString = ""; + public Item polarBearBreedableItem = null; + public int polarBearBreedingTicks = 6000; + public boolean polarBearTakeDamageFromWater = false; + public boolean polarBearAlwaysDropExp = false; + private void polarBearSettings() { + polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable); + polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater); + polarBearControllable = getBoolean("mobs.polar_bear.controllable", polarBearControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.polar_bear.attributes.max-health", polarBearMaxHealth); + set("mobs.polar_bear.attributes.max-health", null); + set("mobs.polar_bear.attributes.max_health", oldValue); + } + polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth); + polarBearScale = Mth.clamp(getDouble("mobs.polar_bear.attributes.scale", polarBearScale), 0.0625D, 16.0D); + polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString); + Item item = BuiltInRegistries.ITEM.getValue(Identifier.parse(polarBearBreedableItemString)); + if (item != Items.AIR) polarBearBreedableItem = item; + polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks); + polarBearTakeDamageFromWater = getBoolean("mobs.polar_bear.takes-damage-from-water", polarBearTakeDamageFromWater); + polarBearAlwaysDropExp = getBoolean("mobs.polar_bear.always-drop-exp", polarBearAlwaysDropExp); + } + + public boolean pufferfishRidable = false; + public boolean pufferfishControllable = true; + public double pufferfishMaxHealth = 3.0D; + public double pufferfishScale = 1.0D; + public boolean pufferfishTakeDamageFromWater = false; + public boolean pufferfishAlwaysDropExp = false; + private void pufferfishSettings() { + pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); + pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.pufferfish.attributes.max-health", pufferfishMaxHealth); + set("mobs.pufferfish.attributes.max-health", null); + set("mobs.pufferfish.attributes.max_health", oldValue); + } + pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth); + pufferfishScale = Mth.clamp(getDouble("mobs.pufferfish.attributes.scale", pufferfishScale), 0.0625D, 16.0D); + pufferfishTakeDamageFromWater = getBoolean("mobs.pufferfish.takes-damage-from-water", pufferfishTakeDamageFromWater); + pufferfishAlwaysDropExp = getBoolean("mobs.pufferfish.always-drop-exp", pufferfishAlwaysDropExp); + } + + public boolean rabbitRidable = false; + public boolean rabbitRidableInWater = true; + public boolean rabbitControllable = true; + public double rabbitMaxHealth = 3.0D; + public double rabbitScale = 1.0D; + public double rabbitNaturalToast = 0.0D; + public double rabbitNaturalKiller = 0.0D; + public int rabbitBreedingTicks = 6000; + public Boolean rabbitMobGriefingOverride = null; + public boolean rabbitTakeDamageFromWater = false; + public boolean rabbitAlwaysDropExp = false; + private void rabbitSettings() { + rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable); + rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater); + rabbitControllable = getBoolean("mobs.rabbit.controllable", rabbitControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.rabbit.attributes.max-health", rabbitMaxHealth); + set("mobs.rabbit.attributes.max-health", null); + set("mobs.rabbit.attributes.max_health", oldValue); + } + rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth); + rabbitScale = Mth.clamp(getDouble("mobs.rabbit.attributes.scale", rabbitScale), 0.0625D, 16.0D); + rabbitNaturalToast = getDouble("mobs.rabbit.spawn-toast-chance", rabbitNaturalToast); + rabbitNaturalKiller = getDouble("mobs.rabbit.spawn-killer-rabbit-chance", rabbitNaturalKiller); + rabbitBreedingTicks = getInt("mobs.rabbit.breeding-delay-ticks", rabbitBreedingTicks); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.rabbit.bypass-mob-griefing", false); + set("mobs.rabbit.bypass-mob-griefing", null); + set("mobs.rabbit.mob-griefing-override", oldVal ? true : "default"); + } + rabbitMobGriefingOverride = getBooleanOrDefault("mobs.rabbit.mob-griefing-override", rabbitMobGriefingOverride); + rabbitTakeDamageFromWater = getBoolean("mobs.rabbit.takes-damage-from-water", rabbitTakeDamageFromWater); + rabbitAlwaysDropExp = getBoolean("mobs.rabbit.always-drop-exp", rabbitAlwaysDropExp); + } + + public boolean ravagerRidable = false; + public boolean ravagerRidableInWater = false; + public boolean ravagerControllable = true; + public double ravagerMaxHealth = 100.0D; + public double ravagerScale = 1.0D; + public Boolean ravagerMobGriefingOverride = null; + public boolean ravagerTakeDamageFromWater = false; + public List ravagerGriefableBlocks = new ArrayList<>(); + public boolean ravagerAlwaysDropExp = false; + public boolean ravagerAvoidRabbits = false; + private void ravagerSettings() { + ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); + ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); + ravagerControllable = getBoolean("mobs.ravager.controllable", ravagerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.ravager.attributes.max-health", ravagerMaxHealth); + set("mobs.ravager.attributes.max-health", null); + set("mobs.ravager.attributes.max_health", oldValue); + } + ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); + ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.ravager.bypass-mob-griefing", false); + set("mobs.ravager.bypass-mob-griefing", null); + set("mobs.ravager.mob-griefing-override", oldVal ? true : "default"); + } + ravagerMobGriefingOverride = getBooleanOrDefault("mobs.ravager.mob-griefing-override", ravagerMobGriefingOverride); + ravagerTakeDamageFromWater = getBoolean("mobs.ravager.takes-damage-from-water", ravagerTakeDamageFromWater); + List defaultRavagerGriefableBlocks = List.of( + "minecraft:oak_leaves", + "minecraft:spruce_leaves", + "minecraft:birch_leaves", + "minecraft:jungle_leaves", + "minecraft:acacia_leaves", + "minecraft:cherry_leaves", + "minecraft:dark_oak_leaves", + "minecraft:pale_oak_leaves", + "minecraft:mangrove_leaves", + "minecraft:azalea_leaves", + "minecraft:flowering_azalea_leaves", + "minecraft:wheat", + "minecraft:carrots", + "minecraft:potatoes", + "minecraft:torchflower_crop", + "minecraft:pitcher_crop", + "minecraft:beetroots" + ); + if (PurpurConfig.version < 41) { + Set set = new HashSet<>(); + getList("mobs.ravager.griefable-blocks", defaultRavagerGriefableBlocks) + .forEach(key -> set.add(key.toString())); + set.add("minecraft:cherry_leaves"); + set.add("minecraft:pale_oak_leaves"); + set.add("minecraft:mangrove_leaves"); + set.add("minecraft:azalea_leaves"); + set.add("minecraft:flowering_azalea_leaves"); + set.add("minecraft:torchflower_crop"); + set.add("minecraft:pitcher_crop"); + set("mobs.ravager.griefable-blocks", new ArrayList<>(set)); + } + getList("mobs.ravager.griefable-blocks", defaultRavagerGriefableBlocks).forEach(key -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(key.toString())); + if (!block.defaultBlockState().isAir()) { + ravagerGriefableBlocks.add(block); + } + }); + ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp); + ravagerAvoidRabbits = getBoolean("mobs.ravager.avoid-rabbits", ravagerAvoidRabbits); + } + + public boolean salmonRidable = false; + public boolean salmonControllable = true; + public double salmonMaxHealth = 3.0D; + public double salmonScale = 1.0D; + public boolean salmonTakeDamageFromWater = false; + public boolean salmonAlwaysDropExp = false; + private void salmonSettings() { + salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); + salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.salmon.attributes.max-health", salmonMaxHealth); + set("mobs.salmon.attributes.max-health", null); + set("mobs.salmon.attributes.max_health", oldValue); + } + salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth); + salmonScale = Mth.clamp(getDouble("mobs.salmon.attributes.scale", salmonScale), 0.0625D, 16.0D); + salmonTakeDamageFromWater = getBoolean("mobs.salmon.takes-damage-from-water", salmonTakeDamageFromWater); + salmonAlwaysDropExp = getBoolean("mobs.salmon.always-drop-exp", salmonAlwaysDropExp); + } + + public boolean sheepRidable = false; + public boolean sheepRidableInWater = true; + public boolean sheepControllable = true; + public double sheepMaxHealth = 8.0D; + public double sheepScale = 1.0D; + public int sheepBreedingTicks = 6000; + public Boolean sheepMobGriefingOverride = null; + public boolean sheepTakeDamageFromWater = false; + public boolean sheepAlwaysDropExp = false; + private void sheepSettings() { + sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); + sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); + sheepControllable = getBoolean("mobs.sheep.controllable", sheepControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.sheep.attributes.max-health", sheepMaxHealth); + set("mobs.sheep.attributes.max-health", null); + set("mobs.sheep.attributes.max_health", oldValue); + } + sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth); + sheepScale = Mth.clamp(getDouble("mobs.sheep.attributes.scale", sheepScale), 0.0625D, 16.0D); + sheepBreedingTicks = getInt("mobs.sheep.breeding-delay-ticks", sheepBreedingTicks); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.sheep.bypass-mob-griefing", false); + set("mobs.sheep.bypass-mob-griefing", null); + set("mobs.sheep.mob-griefing-override", oldVal ? true : "default"); + } + sheepMobGriefingOverride = getBooleanOrDefault("mobs.sheep.mob-griefing-override", sheepMobGriefingOverride); + sheepTakeDamageFromWater = getBoolean("mobs.sheep.takes-damage-from-water", sheepTakeDamageFromWater); + sheepAlwaysDropExp = getBoolean("mobs.sheep.always-drop-exp", sheepAlwaysDropExp); + } + + public boolean shulkerRidable = false; + public boolean shulkerRidableInWater = true; + public boolean shulkerControllable = true; + public double shulkerMaxHealth = 30.0D; + public double shulkerScale = 1.0D; + public boolean shulkerTakeDamageFromWater = false; + public float shulkerSpawnFromBulletBaseChance = 1.0F; + public boolean shulkerSpawnFromBulletRequireOpenLid = true; + public double shulkerSpawnFromBulletNearbyRange = 8.0D; + public String shulkerSpawnFromBulletNearbyEquation = "(nearby - 1) / 5.0"; + public boolean shulkerSpawnFromBulletRandomColor = false; + public boolean shulkerChangeColorWithDye = false; + public boolean shulkerAlwaysDropExp = false; + private void shulkerSettings() { + shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); + shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); + shulkerControllable = getBoolean("mobs.shulker.controllable", shulkerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.shulker.attributes.max-health", shulkerMaxHealth); + set("mobs.shulker.attributes.max-health", null); + set("mobs.shulker.attributes.max_health", oldValue); + } + shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth); + shulkerScale = Mth.clamp(getDouble("mobs.shulker.attributes.scale", shulkerScale), 0.0625D, Shulker.MAX_SCALE); + shulkerTakeDamageFromWater = getBoolean("mobs.shulker.takes-damage-from-water", shulkerTakeDamageFromWater); + shulkerSpawnFromBulletBaseChance = (float) getDouble("mobs.shulker.spawn-from-bullet.base-chance", shulkerSpawnFromBulletBaseChance); + shulkerSpawnFromBulletRequireOpenLid = getBoolean("mobs.shulker.spawn-from-bullet.require-open-lid", shulkerSpawnFromBulletRequireOpenLid); + shulkerSpawnFromBulletNearbyRange = getDouble("mobs.shulker.spawn-from-bullet.nearby-range", shulkerSpawnFromBulletNearbyRange); + shulkerSpawnFromBulletNearbyEquation = getString("mobs.shulker.spawn-from-bullet.nearby-equation", shulkerSpawnFromBulletNearbyEquation); + shulkerSpawnFromBulletRandomColor = getBoolean("mobs.shulker.spawn-from-bullet.random-color", shulkerSpawnFromBulletRandomColor); + shulkerChangeColorWithDye = getBoolean("mobs.shulker.change-color-with-dye", shulkerChangeColorWithDye); + shulkerAlwaysDropExp = getBoolean("mobs.shulker.always-drop-exp", shulkerAlwaysDropExp); + } + + public boolean silverfishRidable = false; + public boolean silverfishRidableInWater = true; + public boolean silverfishControllable = true; + public double silverfishMaxHealth = 8.0D; + public double silverfishScale = 1.0D; + public double silverfishMovementSpeed = 0.25D; + public double silverfishAttackDamage = 1.0D; + public Boolean silverfishMobGriefingOverride = null; + public boolean silverfishTakeDamageFromWater = false; + public boolean silverfishAlwaysDropExp = false; + private void silverfishSettings() { + silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); + silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); + silverfishControllable = getBoolean("mobs.silverfish.controllable", silverfishControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.silverfish.attributes.max-health", silverfishMaxHealth); + set("mobs.silverfish.attributes.max-health", null); + set("mobs.silverfish.attributes.max_health", oldValue); + } + silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth); + silverfishScale = Mth.clamp(getDouble("mobs.silverfish.attributes.scale", silverfishScale), 0.0625D, 16.0D); + silverfishMovementSpeed = getDouble("mobs.silverfish.attributes.movement_speed", silverfishMovementSpeed); + silverfishAttackDamage = getDouble("mobs.silverfish.attributes.attack_damage", silverfishAttackDamage); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.silverfish.bypass-mob-griefing", false); + set("mobs.silverfish.bypass-mob-griefing", null); + set("mobs.silverfish.mob-griefing-override", oldVal ? true : "default"); + } + silverfishMobGriefingOverride = getBooleanOrDefault("mobs.silverfish.mob-griefing-override", silverfishMobGriefingOverride); + silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater); + silverfishAlwaysDropExp = getBoolean("mobs.silverfish.always-drop-exp", silverfishAlwaysDropExp); + } + + public boolean skeletonRidable = false; + public boolean skeletonRidableInWater = true; + public boolean skeletonControllable = true; + public double skeletonMaxHealth = 20.0D; + public double skeletonScale = 1.0D; + public boolean skeletonTakeDamageFromWater = false; + public boolean skeletonAlwaysDropExp = false; + public double skeletonHeadVisibilityPercent = 0.5D; + public int skeletonFeedWitherRoses = 0; + public String skeletonBowAccuracy = "14 - difficulty * 4"; + public Map skeletonBowAccuracyMap = new HashMap<>(); + private void skeletonSettings() { + skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); + skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); + skeletonControllable = getBoolean("mobs.skeleton.controllable", skeletonControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.skeleton.attributes.max-health", skeletonMaxHealth); + set("mobs.skeleton.attributes.max-health", null); + set("mobs.skeleton.attributes.max_health", oldValue); + } + skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth); + skeletonScale = Mth.clamp(getDouble("mobs.skeleton.attributes.scale", skeletonScale), 0.0625D, 16.0D); + skeletonTakeDamageFromWater = getBoolean("mobs.skeleton.takes-damage-from-water", skeletonTakeDamageFromWater); + skeletonAlwaysDropExp = getBoolean("mobs.skeleton.always-drop-exp", skeletonAlwaysDropExp); + skeletonHeadVisibilityPercent = getDouble("mobs.skeleton.head-visibility-percent", skeletonHeadVisibilityPercent); + skeletonFeedWitherRoses = getInt("mobs.skeleton.feed-wither-roses", skeletonFeedWitherRoses); + final String defaultSkeletonBowAccuracy = skeletonBowAccuracy; + skeletonBowAccuracy = getString("mobs.skeleton.bow-accuracy", skeletonBowAccuracy); + for (int i = 1; i < 4; i++) { + final float divergence; + try { + divergence = ((Number) Entity.scriptEngine.eval("let difficulty = " + i + "; " + skeletonBowAccuracy)).floatValue(); + } catch (javax.script.ScriptException e) { + e.printStackTrace(); + break; + } + skeletonBowAccuracyMap.put(i, divergence); + } + } + + public boolean skeletonHorseRidable = false; + public boolean skeletonHorseRidableInWater = true; + public boolean skeletonHorseCanSwim = false; + public double skeletonHorseMaxHealthMin = 15.0D; + public double skeletonHorseMaxHealthMax = 15.0D; + public double skeletonHorseJumpStrengthMin = 0.4D; + public double skeletonHorseJumpStrengthMax = 1.0D; + public double skeletonHorseMovementSpeedMin = 0.2D; + public double skeletonHorseMovementSpeedMax = 0.2D; + public boolean skeletonHorseTakeDamageFromWater = false; + public boolean skeletonHorseAlwaysDropExp = false; + private void skeletonHorseSettings() { + skeletonHorseRidable = getBoolean("mobs.skeleton_horse.ridable", skeletonHorseRidable); + skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); + skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.skeleton_horse.attributes.max-health", skeletonHorseMaxHealthMin); + set("mobs.skeleton_horse.attributes.max-health", null); + set("mobs.skeleton_horse.attributes.max_health.min", oldValue); + set("mobs.skeleton_horse.attributes.max_health.max", oldValue); + } + skeletonHorseMaxHealthMin = getDouble("mobs.skeleton_horse.attributes.max_health.min", skeletonHorseMaxHealthMin); + skeletonHorseMaxHealthMax = getDouble("mobs.skeleton_horse.attributes.max_health.max", skeletonHorseMaxHealthMax); + skeletonHorseJumpStrengthMin = getDouble("mobs.skeleton_horse.attributes.jump_strength.min", skeletonHorseJumpStrengthMin); + skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax); + skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin); + skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax); + skeletonHorseTakeDamageFromWater = getBoolean("mobs.skeleton_horse.takes-damage-from-water", skeletonHorseTakeDamageFromWater); + skeletonHorseAlwaysDropExp = getBoolean("mobs.skeleton_horse.always-drop-exp", skeletonHorseAlwaysDropExp); + } + + public boolean slimeRidable = false; + public boolean slimeRidableInWater = true; + public boolean slimeControllable = true; + public String slimeMaxHealth = "size * size"; + public String slimeAttackDamage = "size"; + public Map slimeMaxHealthCache = new HashMap<>(); + public Map slimeAttackDamageCache = new HashMap<>(); + public boolean slimeTakeDamageFromWater = false; + public boolean slimeAlwaysDropExp = false; + private void slimeSettings() { + slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable); + slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater); + slimeControllable = getBoolean("mobs.slime.controllable", slimeControllable); + if (PurpurConfig.version < 10) { + String oldValue = getString("mobs.slime.attributes.max-health", slimeMaxHealth); + set("mobs.slime.attributes.max-health", null); + set("mobs.slime.attributes.max_health", oldValue); + } + slimeMaxHealth = getString("mobs.slime.attributes.max_health", slimeMaxHealth); + slimeAttackDamage = getString("mobs.slime.attributes.attack_damage", slimeAttackDamage); + slimeMaxHealthCache.clear(); + slimeAttackDamageCache.clear(); + slimeTakeDamageFromWater = getBoolean("mobs.slime.takes-damage-from-water", slimeTakeDamageFromWater); + slimeAlwaysDropExp = getBoolean("mobs.slime.always-drop-exp", slimeAlwaysDropExp); + } + + public boolean snowGolemRidable = false; + public boolean snowGolemRidableInWater = true; + public boolean snowGolemControllable = true; + public boolean snowGolemLeaveTrailWhenRidden = false; + public double snowGolemMaxHealth = 4.0D; + public double snowGolemScale = 1.0D; + public boolean snowGolemPutPumpkinBack = false; + public int snowGolemSnowBallMin = 20; + public int snowGolemSnowBallMax = 20; + public float snowGolemSnowBallModifier = 10.0F; + public double snowGolemAttackDistance = 1.25D; + public Boolean snowGolemMobGriefingOverride = null; + public boolean snowGolemTakeDamageFromWater = true; + public boolean snowGolemAlwaysDropExp = false; + private void snowGolemSettings() { + snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); + snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); + snowGolemControllable = getBoolean("mobs.snow_golem.controllable", snowGolemControllable); + snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.snow_golem.attributes.max-health", snowGolemMaxHealth); + set("mobs.snow_golem.attributes.max-health", null); + set("mobs.snow_golem.attributes.max_health", oldValue); + } + snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth); + snowGolemScale = Mth.clamp(getDouble("mobs.snow_golem.attributes.scale", snowGolemScale), 0.0625D, 16.0D); + snowGolemPutPumpkinBack = getBoolean("mobs.snow_golem.pumpkin-can-be-added-back", snowGolemPutPumpkinBack); + snowGolemSnowBallMin = getInt("mobs.snow_golem.min-shoot-interval-ticks", snowGolemSnowBallMin); + snowGolemSnowBallMax = getInt("mobs.snow_golem.max-shoot-interval-ticks", snowGolemSnowBallMax); + snowGolemSnowBallModifier = (float) getDouble("mobs.snow_golem.snow-ball-modifier", snowGolemSnowBallModifier); + snowGolemAttackDistance = getDouble("mobs.snow_golem.attack-distance", snowGolemAttackDistance); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.snow_golem.bypass-mob-griefing", false); + set("mobs.snow_golem.bypass-mob-griefing", null); + set("mobs.snow_golem.mob-griefing-override", oldVal ? true : "default"); + } + snowGolemMobGriefingOverride = getBooleanOrDefault("mobs.snow_golem.mob-griefing-override", snowGolemMobGriefingOverride); + snowGolemTakeDamageFromWater = getBoolean("mobs.snow_golem.takes-damage-from-water", snowGolemTakeDamageFromWater); + snowGolemAlwaysDropExp = getBoolean("mobs.snow_golem.always-drop-exp", snowGolemAlwaysDropExp); + } + + public boolean snifferRidable = false; + public boolean snifferRidableInWater = true; + public boolean snifferControllable = true; + public double snifferMaxHealth = 14.0D; + public double snifferScale = 1.0D; + public int snifferBreedingTicks = 6000; + private void snifferSettings() { + snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); + snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); + snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable); + snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth); + snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D); + snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", snifferBreedingTicks); + } + + public boolean squidRidable = false; + public boolean squidControllable = true; + public double squidMaxHealth = 10.0D; + public double squidScale = 1.0D; + public boolean squidImmuneToEAR = true; + public double squidOffsetWaterCheck = 0.0D; + public boolean squidsCanFly = false; + public boolean squidTakeDamageFromWater = false; + public boolean squidAlwaysDropExp = false; + private void squidSettings() { + squidRidable = getBoolean("mobs.squid.ridable", squidRidable); + squidControllable = getBoolean("mobs.squid.controllable", squidControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.squid.attributes.max-health", squidMaxHealth); + set("mobs.squid.attributes.max-health", null); + set("mobs.squid.attributes.max_health", oldValue); + } + squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth); + squidScale = Mth.clamp(getDouble("mobs.squid.attributes.scale", squidScale), 0.0625D, 16.0D); + squidImmuneToEAR = getBoolean("mobs.squid.immune-to-EAR", squidImmuneToEAR); + squidOffsetWaterCheck = getDouble("mobs.squid.water-offset-check", squidOffsetWaterCheck); + squidsCanFly = getBoolean("mobs.squid.can-fly", squidsCanFly); + squidTakeDamageFromWater = getBoolean("mobs.squid.takes-damage-from-water", squidTakeDamageFromWater); + squidAlwaysDropExp = getBoolean("mobs.squid.always-drop-exp", squidAlwaysDropExp); + } + + public boolean spiderRidable = false; + public boolean spiderRidableInWater = false; + public boolean spiderControllable = true; + public double spiderMaxHealth = 16.0D; + public double spiderScale = 1.0D; + public boolean spiderTakeDamageFromWater = false; + public boolean spiderAlwaysDropExp = false; + private void spiderSettings() { + spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); + spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); + spiderControllable = getBoolean("mobs.spider.controllable", spiderControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.spider.attributes.max-health", spiderMaxHealth); + set("mobs.spider.attributes.max-health", null); + set("mobs.spider.attributes.max_health", oldValue); + } + spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth); + spiderScale = Mth.clamp(getDouble("mobs.spider.attributes.scale", spiderScale), 0.0625D, 16.0D); + spiderTakeDamageFromWater = getBoolean("mobs.spider.takes-damage-from-water", spiderTakeDamageFromWater); + spiderAlwaysDropExp = getBoolean("mobs.spider.always-drop-exp", spiderAlwaysDropExp); + } + + public boolean strayRidable = false; + public boolean strayRidableInWater = true; + public boolean strayControllable = true; + public double strayMaxHealth = 20.0D; + public double strayScale = 1.0D; + public boolean strayTakeDamageFromWater = false; + public boolean strayAlwaysDropExp = false; + private void straySettings() { + strayRidable = getBoolean("mobs.stray.ridable", strayRidable); + strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); + strayControllable = getBoolean("mobs.stray.controllable", strayControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.stray.attributes.max-health", strayMaxHealth); + set("mobs.stray.attributes.max-health", null); + set("mobs.stray.attributes.max_health", oldValue); + } + strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth); + strayScale = Mth.clamp(getDouble("mobs.stray.attributes.scale", strayScale), 0.0625D, 16.0D); + strayTakeDamageFromWater = getBoolean("mobs.stray.takes-damage-from-water", strayTakeDamageFromWater); + strayAlwaysDropExp = getBoolean("mobs.stray.always-drop-exp", strayAlwaysDropExp); + } + + public boolean striderRidable = false; + public boolean striderRidableInWater = false; + public boolean striderControllable = true; + public double striderMaxHealth = 20.0D; + public double striderScale = 1.0D; + public int striderBreedingTicks = 6000; + public boolean striderGiveSaddleBack = false; + public boolean striderTakeDamageFromWater = true; + public boolean striderAlwaysDropExp = false; + private void striderSettings() { + striderRidable = getBoolean("mobs.strider.ridable", striderRidable); + striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); + striderControllable = getBoolean("mobs.strider.controllable", striderControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.strider.attributes.max-health", striderMaxHealth); + set("mobs.strider.attributes.max-health", null); + set("mobs.strider.attributes.max_health", oldValue); + } + striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth); + striderScale = Mth.clamp(getDouble("mobs.strider.attributes.scale", striderScale), 0.0625D, 16.0D); + striderBreedingTicks = getInt("mobs.strider.breeding-delay-ticks", striderBreedingTicks); + striderGiveSaddleBack = getBoolean("mobs.strider.give-saddle-back", striderGiveSaddleBack); + striderTakeDamageFromWater = getBoolean("mobs.strider.takes-damage-from-water", striderTakeDamageFromWater); + striderAlwaysDropExp = getBoolean("mobs.strider.always-drop-exp", striderAlwaysDropExp); + } + + public boolean tadpoleRidable = false; + public boolean tadpoleRidableInWater = true; + public boolean tadpoleControllable = true; + private void tadpoleSettings() { + tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable); + tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater); + tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable); + } + + public boolean traderLlamaRidable = false; + public boolean traderLlamaRidableInWater = false; + public boolean traderLlamaControllable = true; + public double traderLlamaMaxHealthMin = 15.0D; + public double traderLlamaMaxHealthMax = 30.0D; + public double traderLlamaJumpStrengthMin = 0.5D; + public double traderLlamaJumpStrengthMax = 0.5D; + public double traderLlamaMovementSpeedMin = 0.175D; + public double traderLlamaMovementSpeedMax = 0.175D; + public int traderLlamaBreedingTicks = 6000; + public boolean traderLlamaTakeDamageFromWater = false; + public boolean traderLlamaAlwaysDropExp = false; + private void traderLlamaSettings() { + traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable); + traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater); + traderLlamaControllable = getBoolean("mobs.trader_llama.controllable", traderLlamaControllable); + if (PurpurConfig.version < 10) { + double oldMin = getDouble("mobs.trader_llama.attributes.max-health.min", traderLlamaMaxHealthMin); + double oldMax = getDouble("mobs.trader_llama.attributes.max-health.max", traderLlamaMaxHealthMax); + set("mobs.trader_llama.attributes.max-health", null); + set("mobs.trader_llama.attributes.max_health.min", oldMin); + set("mobs.trader_llama.attributes.max_health.max", oldMax); + } + traderLlamaMaxHealthMin = getDouble("mobs.trader_llama.attributes.max_health.min", traderLlamaMaxHealthMin); + traderLlamaMaxHealthMax = getDouble("mobs.trader_llama.attributes.max_health.max", traderLlamaMaxHealthMax); + traderLlamaJumpStrengthMin = getDouble("mobs.trader_llama.attributes.jump_strength.min", traderLlamaJumpStrengthMin); + traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax); + traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin); + traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax); + traderLlamaBreedingTicks = getInt("mobs.trader_llama.breeding-delay-ticks", traderLlamaBreedingTicks); + traderLlamaTakeDamageFromWater = getBoolean("mobs.trader_llama.takes-damage-from-water", traderLlamaTakeDamageFromWater); + traderLlamaAlwaysDropExp = getBoolean("mobs.trader_llama.always-drop-exp", traderLlamaAlwaysDropExp); + } + + public boolean tropicalFishRidable = false; + public boolean tropicalFishControllable = true; + public double tropicalFishMaxHealth = 3.0D; + public double tropicalFishScale = 1.0D; + public boolean tropicalFishTakeDamageFromWater = false; + public boolean tropicalFishAlwaysDropExp = false; + private void tropicalFishSettings() { + tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); + tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.tropical_fish.attributes.max-health", tropicalFishMaxHealth); + set("mobs.tropical_fish.attributes.max-health", null); + set("mobs.tropical_fish.attributes.max_health", oldValue); + } + tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth); + tropicalFishScale = Mth.clamp(getDouble("mobs.tropical_fish.attributes.scale", tropicalFishScale), 0.0625D, 16.0D); + tropicalFishTakeDamageFromWater = getBoolean("mobs.tropical_fish.takes-damage-from-water", tropicalFishTakeDamageFromWater); + tropicalFishAlwaysDropExp = getBoolean("mobs.tropical_fish.always-drop-exp", tropicalFishAlwaysDropExp); + } + + public boolean turtleRidable = false; + public boolean turtleRidableInWater = true; + public boolean turtleControllable = true; + public double turtleMaxHealth = 30.0D; + public double turtleScale = 1.0D; + public int turtleBreedingTicks = 6000; + public boolean turtleTakeDamageFromWater = false; + public boolean turtleAlwaysDropExp = false; + private void turtleSettings() { + turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); + turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); + turtleControllable = getBoolean("mobs.turtle.controllable", turtleControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.turtle.attributes.max-health", turtleMaxHealth); + set("mobs.turtle.attributes.max-health", null); + set("mobs.turtle.attributes.max_health", oldValue); + } + turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth); + turtleScale = Mth.clamp(getDouble("mobs.turtle.attributes.scale", turtleScale), 0.0625D, 16.0D); + turtleBreedingTicks = getInt("mobs.turtle.breeding-delay-ticks", turtleBreedingTicks); + turtleTakeDamageFromWater = getBoolean("mobs.turtle.takes-damage-from-water", turtleTakeDamageFromWater); + turtleAlwaysDropExp = getBoolean("mobs.turtle.always-drop-exp", turtleAlwaysDropExp); + } + + public boolean vexRidable = false; + public boolean vexRidableInWater = true; + public boolean vexControllable = true; + public double vexMaxY = 320D; + public double vexMaxHealth = 14.0D; + public double vexScale = 1.0D; + public boolean vexTakeDamageFromWater = false; + public boolean vexAlwaysDropExp = false; + private void vexSettings() { + vexRidable = getBoolean("mobs.vex.ridable", vexRidable); + vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); + vexControllable = getBoolean("mobs.vex.controllable", vexControllable); + vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.vex.attributes.max-health", vexMaxHealth); + set("mobs.vex.attributes.max-health", null); + set("mobs.vex.attributes.max_health", oldValue); + } + vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth); + vexScale = Mth.clamp(getDouble("mobs.vex.attributes.scale", vexScale), 0.0625D, 16.0D); + vexTakeDamageFromWater = getBoolean("mobs.vex.takes-damage-from-water", vexTakeDamageFromWater); + vexAlwaysDropExp = getBoolean("mobs.vex.always-drop-exp", vexAlwaysDropExp); + } + + public boolean villagerRidable = false; + public boolean villagerRidableInWater = true; + public boolean villagerControllable = true; + public double villagerMaxHealth = 20.0D; + public double villagerScale = 1.0D; + public boolean villagerFollowEmeraldBlock = false; + public double villagerTemptRange = 10.0D; + public boolean villagerCanBeLeashed = false; + public boolean villagerCanBreed = true; + public int villagerBreedingTicks = 6000; + public boolean villagerClericsFarmWarts = false; + public boolean villagerClericFarmersThrowWarts = true; + public Boolean villagerMobGriefingOverride = null; + public boolean villagerTakeDamageFromWater = false; + public boolean villagerAllowTrading = true; + public boolean villagerAlwaysDropExp = false; + public int villagerMinimumDemand = 0; + public boolean villagerLobotomizeEnabled = false; + public int villagerLobotomizeCheckInterval = 100; + public boolean villagerLobotomizeWaitUntilTradeLocked = false; + public boolean villagerDisplayTradeItem = true; + public int villagerSpawnIronGolemRadius = 0; + public int villagerSpawnIronGolemLimit = 0; + public int villagerAcquirePoiSearchRadius = 48; + public int villagerNearestBedSensorSearchRadius = 48; + private void villagerSettings() { + villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); + villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); + villagerControllable = getBoolean("mobs.villager.controllable", villagerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.villager.attributes.max-health", villagerMaxHealth); + set("mobs.villager.attributes.max-health", null); + set("mobs.villager.attributes.max_health", oldValue); + } + villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth); + villagerScale = Mth.clamp(getDouble("mobs.villager.attributes.scale", villagerScale), 0.0625D, 16.0D); + villagerFollowEmeraldBlock = getBoolean("mobs.villager.follow-emerald-blocks", villagerFollowEmeraldBlock); + villagerTemptRange = getDouble("mobs.villager.attributes.tempt_range", villagerTemptRange); + villagerCanBeLeashed = getBoolean("mobs.villager.can-be-leashed", villagerCanBeLeashed); + villagerCanBreed = getBoolean("mobs.villager.can-breed", villagerCanBreed); + villagerBreedingTicks = getInt("mobs.villager.breeding-delay-ticks", villagerBreedingTicks); + villagerClericsFarmWarts = getBoolean("mobs.villager.clerics-farm-warts", villagerClericsFarmWarts); + villagerClericFarmersThrowWarts = getBoolean("mobs.villager.cleric-wart-farmers-throw-warts-at-villagers", villagerClericFarmersThrowWarts); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.villager.bypass-mob-griefing", false); + set("mobs.villager.bypass-mob-griefing", null); + set("mobs.villager.mob-griefing-override", oldVal ? true : "default"); + } + villagerMobGriefingOverride = getBooleanOrDefault("mobs.villager.mob-griefing-override", villagerMobGriefingOverride); + villagerTakeDamageFromWater = getBoolean("mobs.villager.takes-damage-from-water", villagerTakeDamageFromWater); + villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); + villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp); + villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand); + if (PurpurConfig.version < 9) { + boolean oldValue = getBoolean("mobs.villager.lobotomize-1x1", villagerLobotomizeEnabled); + set("mobs.villager.lobotomize.enabled", oldValue); + set("mobs.villager.lobotomize-1x1", null); + } + if (PurpurConfig.version < 27) { + int oldValue = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); + set("mobs.villager.lobotomize.check-interval", oldValue == 60 ? 100 : oldValue); + } + villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); + villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); + villagerLobotomizeWaitUntilTradeLocked = getBoolean("mobs.villager.lobotomize.wait-until-trade-locked", villagerLobotomizeWaitUntilTradeLocked); + villagerDisplayTradeItem = getBoolean("mobs.villager.display-trade-item", villagerDisplayTradeItem); + villagerSpawnIronGolemRadius = getInt("mobs.villager.spawn-iron-golem.radius", villagerSpawnIronGolemRadius); + villagerSpawnIronGolemLimit = getInt("mobs.villager.spawn-iron-golem.limit", villagerSpawnIronGolemLimit); + villagerAcquirePoiSearchRadius = getInt("mobs.villager.search-radius.acquire-poi", villagerAcquirePoiSearchRadius); + villagerNearestBedSensorSearchRadius = getInt("mobs.villager.search-radius.nearest-bed-sensor", villagerNearestBedSensorSearchRadius); + } + + public boolean vindicatorRidable = false; + public boolean vindicatorRidableInWater = true; + public boolean vindicatorControllable = true; + public double vindicatorMaxHealth = 24.0D; + public double vindicatorScale = 1.0D; + public double vindicatorJohnnySpawnChance = 0D; + public boolean vindicatorTakeDamageFromWater = false; + public boolean vindicatorAlwaysDropExp = false; + private void vindicatorSettings() { + vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); + vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); + vindicatorControllable = getBoolean("mobs.vindicator.controllable", vindicatorControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.vindicator.attributes.max-health", vindicatorMaxHealth); + set("mobs.vindicator.attributes.max-health", null); + set("mobs.vindicator.attributes.max_health", oldValue); + } + vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth); + vindicatorScale = Mth.clamp(getDouble("mobs.vindicator.attributes.scale", vindicatorScale), 0.0625D, 16.0D); + vindicatorJohnnySpawnChance = getDouble("mobs.vindicator.johnny.spawn-chance", vindicatorJohnnySpawnChance); + vindicatorTakeDamageFromWater = getBoolean("mobs.vindicator.takes-damage-from-water", vindicatorTakeDamageFromWater); + vindicatorAlwaysDropExp = getBoolean("mobs.vindicator.always-drop-exp", vindicatorAlwaysDropExp); + } + + public boolean wanderingTraderRidable = false; + public boolean wanderingTraderRidableInWater = true; + public boolean wanderingTraderControllable = true; + public double wanderingTraderMaxHealth = 20.0D; + public double wanderingTraderScale = 1.0D; + public boolean wanderingTraderFollowEmeraldBlock = false; + public double wanderingTraderTemptRange = 10.0D; + public boolean wanderingTraderCanBeLeashed = false; + public boolean wanderingTraderTakeDamageFromWater = false; + public boolean wanderingTraderAllowTrading = true; + public boolean wanderingTraderAlwaysDropExp = false; + private void wanderingTraderSettings() { + wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); + wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); + wanderingTraderControllable = getBoolean("mobs.wandering_trader.controllable", wanderingTraderControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.wandering_trader.attributes.max-health", wanderingTraderMaxHealth); + set("mobs.wandering_trader.attributes.max-health", null); + set("mobs.wandering_trader.attributes.max_health", oldValue); + } + wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth); + wanderingTraderScale = Mth.clamp(getDouble("mobs.wandering_trader.attributes.scale", wanderingTraderScale), 0.0625D, 16.0D); + wanderingTraderFollowEmeraldBlock = getBoolean("mobs.wandering_trader.follow-emerald-blocks", wanderingTraderFollowEmeraldBlock); + wanderingTraderTemptRange = getDouble("mobs.wandering_trader.attributes.tempt_range", wanderingTraderTemptRange); + wanderingTraderCanBeLeashed = getBoolean("mobs.wandering_trader.can-be-leashed", wanderingTraderCanBeLeashed); + wanderingTraderTakeDamageFromWater = getBoolean("mobs.wandering_trader.takes-damage-from-water", wanderingTraderTakeDamageFromWater); + wanderingTraderAllowTrading = getBoolean("mobs.wandering_trader.allow-trading", wanderingTraderAllowTrading); + wanderingTraderAlwaysDropExp = getBoolean("mobs.wandering_trader.always-drop-exp", wanderingTraderAlwaysDropExp); + } + + public boolean wardenRidable = false; + public boolean wardenRidableInWater = true; + public boolean wardenControllable = true; + public boolean wardenCanUseSonicBoom = true; + private void wardenSettings() { + wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable); + wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater); + wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable); + wardenCanUseSonicBoom = getBoolean("mobs.warden.can-use-sonic-boom", wardenCanUseSonicBoom); + } + + public boolean witchRidable = false; + public boolean witchRidableInWater = true; + public boolean witchControllable = true; + public double witchMaxHealth = 26.0D; + public double witchScale = 1.0D; + public boolean witchTakeDamageFromWater = false; + public boolean witchAlwaysDropExp = false; + private void witchSettings() { + witchRidable = getBoolean("mobs.witch.ridable", witchRidable); + witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); + witchControllable = getBoolean("mobs.witch.controllable", witchControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.witch.attributes.max-health", witchMaxHealth); + set("mobs.witch.attributes.max-health", null); + set("mobs.witch.attributes.max_health", oldValue); + } + witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth); + witchScale = Mth.clamp(getDouble("mobs.witch.attributes.scale", witchScale), 0.0625D, 16.0D); + witchTakeDamageFromWater = getBoolean("mobs.witch.takes-damage-from-water", witchTakeDamageFromWater); + witchAlwaysDropExp = getBoolean("mobs.witch.always-drop-exp", witchAlwaysDropExp); + } + + public boolean witherRidable = false; + public boolean witherRidableInWater = true; + public boolean witherControllable = true; + public double witherMaxY = 320D; + public double witherMaxHealth = 300.0D; + public double witherScale = 1.0D; + public float witherHealthRegenAmount = 1.0f; + public int witherHealthRegenDelay = 20; + public Boolean witherMobGriefingOverride = null; + public boolean witherTakeDamageFromWater = false; + public boolean witherCanRideVehicles = false; + public float witherExplosionRadius = 1.0F; + public boolean witherPlaySpawnSound = true; + public boolean witherAlwaysDropExp = false; + private void witherSettings() { + witherRidable = getBoolean("mobs.wither.ridable", witherRidable); + witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater); + witherControllable = getBoolean("mobs.wither.controllable", witherControllable); + witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY); + if (PurpurConfig.version < 8) { + double oldValue = getDouble("mobs.wither.max-health", witherMaxHealth); + set("mobs.wither.max_health", null); + set("mobs.wither.attributes.max-health", oldValue); + } else if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.wither.attributes.max-health", witherMaxHealth); + set("mobs.wither.attributes.max-health", null); + set("mobs.wither.attributes.max_health", oldValue); + } + witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth); + witherScale = Mth.clamp(getDouble("mobs.wither.attributes.scale", witherScale), 0.0625D, 16.0D); + witherHealthRegenAmount = (float) getDouble("mobs.wither.health-regen-amount", witherHealthRegenAmount); + witherHealthRegenDelay = getInt("mobs.wither.health-regen-delay", witherHealthRegenDelay); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.wither.bypass-mob-griefing", false); + set("mobs.wither.bypass-mob-griefing", null); + set("mobs.wither.mob-griefing-override", oldVal ? true : "default"); + } + witherMobGriefingOverride = getBooleanOrDefault("mobs.wither.mob-griefing-override", witherMobGriefingOverride); + witherTakeDamageFromWater = getBoolean("mobs.wither.takes-damage-from-water", witherTakeDamageFromWater); + witherCanRideVehicles = getBoolean("mobs.wither.can-ride-vehicles", witherCanRideVehicles); + witherExplosionRadius = (float) getDouble("mobs.wither.explosion-radius", witherExplosionRadius); + witherPlaySpawnSound = getBoolean("mobs.wither.play-spawn-sound", witherPlaySpawnSound); + witherAlwaysDropExp = getBoolean("mobs.wither.always-drop-exp", witherAlwaysDropExp); + } + + public boolean witherSkeletonRidable = false; + public boolean witherSkeletonRidableInWater = true; + public boolean witherSkeletonControllable = true; + public double witherSkeletonMaxHealth = 20.0D; + public double witherSkeletonScale = 1.0D; + public boolean witherSkeletonTakeDamageFromWater = false; + public boolean witherSkeletonAlwaysDropExp = false; + private void witherSkeletonSettings() { + witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); + witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); + witherSkeletonControllable = getBoolean("mobs.wither_skeleton.controllable", witherSkeletonControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.wither_skeleton.attributes.max-health", witherSkeletonMaxHealth); + set("mobs.wither_skeleton.attributes.max-health", null); + set("mobs.wither_skeleton.attributes.max_health", oldValue); + } + witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth); + witherSkeletonScale = Mth.clamp(getDouble("mobs.wither_skeleton.attributes.scale", witherSkeletonScale), 0.0625D, 16.0D); + witherSkeletonTakeDamageFromWater = getBoolean("mobs.wither_skeleton.takes-damage-from-water", witherSkeletonTakeDamageFromWater); + witherSkeletonAlwaysDropExp = getBoolean("mobs.wither_skeleton.always-drop-exp", witherSkeletonAlwaysDropExp); + } + + public boolean wolfRidable = false; + public boolean wolfRidableInWater = true; + public boolean wolfControllable = true; + public double wolfMaxHealth = 8.0D; + public double wolfScale = 1.0D; + public DyeColor wolfDefaultCollarColor = DyeColor.RED; + public boolean wolfMilkCuresRabies = true; + public double wolfNaturalRabid = 0.0D; + public int wolfBreedingTicks = 6000; + public boolean wolfTakeDamageFromWater = false; + public boolean wolfAlwaysDropExp = false; + private void wolfSettings() { + wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); + wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); + wolfControllable = getBoolean("mobs.wolf.controllable", wolfControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.wolf.attributes.max-health", wolfMaxHealth); + set("mobs.wolf.attributes.max-health", null); + set("mobs.wolf.attributes.max_health", oldValue); + } + wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); + wolfScale = Mth.clamp(getDouble("mobs.wolf.attributes.scale", wolfScale), 0.0625D, 16.0D); + try { + wolfDefaultCollarColor = DyeColor.valueOf(getString("mobs.wolf.default-collar-color", wolfDefaultCollarColor.name())); + } catch (IllegalArgumentException ignore) { + wolfDefaultCollarColor = DyeColor.RED; + } + wolfMilkCuresRabies = getBoolean("mobs.wolf.milk-cures-rabid-wolves", wolfMilkCuresRabies); + wolfNaturalRabid = getDouble("mobs.wolf.spawn-rabid-chance", wolfNaturalRabid); + wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); + wolfTakeDamageFromWater = getBoolean("mobs.wolf.takes-damage-from-water", wolfTakeDamageFromWater); + wolfAlwaysDropExp = getBoolean("mobs.wolf.always-drop-exp", wolfAlwaysDropExp); + } + + public boolean zoglinRidable = false; + public boolean zoglinRidableInWater = true; + public boolean zoglinControllable = true; + public double zoglinMaxHealth = 40.0D; + public double zoglinScale = 1.0D; + public boolean zoglinTakeDamageFromWater = false; + public boolean zoglinAlwaysDropExp = false; + private void zoglinSettings() { + zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); + zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); + zoglinControllable = getBoolean("mobs.zoglin.controllable", zoglinControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.zoglin.attributes.max-health", zoglinMaxHealth); + set("mobs.zoglin.attributes.max-health", null); + set("mobs.zoglin.attributes.max_health", oldValue); + } + zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth); + zoglinScale = Mth.clamp(getDouble("mobs.zoglin.attributes.scale", zoglinScale), 0.0625D, 16.0D); + zoglinTakeDamageFromWater = getBoolean("mobs.zoglin.takes-damage-from-water", zoglinTakeDamageFromWater); + zoglinAlwaysDropExp = getBoolean("mobs.zoglin.always-drop-exp", zoglinAlwaysDropExp); + } + + public boolean zombieRidable = false; + public boolean zombieRidableInWater = true; + public boolean zombieControllable = true; + public double zombieMaxHealth = 20.0D; + public double zombieScale = 1.0D; + public double zombieSpawnReinforcements = 0.1D; + public boolean zombieJockeyOnlyBaby = true; + public double zombieJockeyChance = 0.05D; + public boolean zombieJockeyTryExistingChickens = true; + public boolean zombieAggressiveTowardsVillagerWhenLagging = true; + public Boolean zombieMobGriefingOverride = null; + public boolean zombieTakeDamageFromWater = false; + public boolean zombieAlwaysDropExp = false; + public double zombieHeadVisibilityPercent = 0.5D; + private void zombieSettings() { + zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); + zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); + zombieControllable = getBoolean("mobs.zombie.controllable", zombieControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.zombie.attributes.max-health", zombieMaxHealth); + set("mobs.zombie.attributes.max-health", null); + set("mobs.zombie.attributes.max_health", oldValue); + } + zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth); + zombieScale = Mth.clamp(getDouble("mobs.zombie.attributes.scale", zombieScale), 0.0625D, 16.0D); + zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements); + zombieJockeyOnlyBaby = getBoolean("mobs.zombie.jockey.only-babies", zombieJockeyOnlyBaby); + zombieJockeyChance = getDouble("mobs.zombie.jockey.chance", zombieJockeyChance); + zombieJockeyTryExistingChickens = getBoolean("mobs.zombie.jockey.try-existing-chickens", zombieJockeyTryExistingChickens); + zombieAggressiveTowardsVillagerWhenLagging = getBoolean("mobs.zombie.aggressive-towards-villager-when-lagging", zombieAggressiveTowardsVillagerWhenLagging); + if (PurpurConfig.version < 43) { + boolean oldVal = getBoolean("mobs.zombie.bypass-mob-griefing", false); + set("mobs.zombie.bypass-mob-griefing", null); + set("mobs.zombie.mob-griefing-override", oldVal ? true : "default"); + } + zombieMobGriefingOverride = getBooleanOrDefault("mobs.zombie.mob-griefing-override", zombieMobGriefingOverride); + zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater); + zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp); + zombieHeadVisibilityPercent = getDouble("mobs.zombie.head-visibility-percent", zombieHeadVisibilityPercent); + } + + public boolean zombieHorseRidable = false; + public boolean zombieHorseRidableInWater = false; + public boolean zombieHorseCanSwim = false; + public double zombieHorseMaxHealthMin = 15.0D; + public double zombieHorseMaxHealthMax = 15.0D; + public double zombieHorseJumpStrengthMin = 0.4D; + public double zombieHorseJumpStrengthMax = 1.0D; + public double zombieHorseMovementSpeedMin = 0.2D; + public double zombieHorseMovementSpeedMax = 0.2D; + public double zombieHorseSpawnChance = 0.0D; + public boolean zombieHorseTakeDamageFromWater = false; + public boolean zombieHorseAlwaysDropExp = false; + private void zombieHorseSettings() { + zombieHorseRidable = getBoolean("mobs.zombie_horse.ridable", zombieHorseRidable); + zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); + zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.zombie_horse.attributes.max-health", zombieHorseMaxHealthMin); + set("mobs.zombie_horse.attributes.max-health", null); + set("mobs.zombie_horse.attributes.max_health.min", oldValue); + set("mobs.zombie_horse.attributes.max_health.max", oldValue); + } + zombieHorseMaxHealthMin = getDouble("mobs.zombie_horse.attributes.max_health.min", zombieHorseMaxHealthMin); + zombieHorseMaxHealthMax = getDouble("mobs.zombie_horse.attributes.max_health.max", zombieHorseMaxHealthMax); + zombieHorseJumpStrengthMin = getDouble("mobs.zombie_horse.attributes.jump_strength.min", zombieHorseJumpStrengthMin); + zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax); + zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin); + zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax); + zombieHorseSpawnChance = getDouble("mobs.zombie_horse.spawn-chance", zombieHorseSpawnChance); + zombieHorseTakeDamageFromWater = getBoolean("mobs.zombie_horse.takes-damage-from-water", zombieHorseTakeDamageFromWater); + zombieHorseAlwaysDropExp = getBoolean("mobs.zombie_horse.always-drop-exp", zombieHorseAlwaysDropExp); + } + + public double zombieNautilusMaxHealth = 15.0D; + public double zombieNautilusMovementSpeed = 1.1D; + public double zombieNautilusAttackDamage = 3.0D; + public double zombieNautilusKnockbackResistance = 0.3D; + public double zombieNautilusScale = 1.0D; + public boolean zombieNautilusAlwaysDropExp = false; + private void zombieNautilusSettings() { + zombieNautilusMaxHealth = getDouble("mobs.zombie_nautilus.attributes.max_health", zombieNautilusMaxHealth); + zombieNautilusMovementSpeed = getDouble("mobs.zombie_nautilus.attributes.movement_speed", zombieNautilusMovementSpeed); + zombieNautilusAttackDamage = getDouble("mobs.zombie_nautilus.attributes.attack_damage", zombieNautilusAttackDamage); + zombieNautilusKnockbackResistance = getDouble("mobs.zombie_nautilus.attributes.knockback_resistance", zombieNautilusKnockbackResistance); + zombieNautilusScale = Mth.clamp(getDouble("mobs.zombie_nautilus.attributes.scale", zombieNautilusScale), 0.0625D, 16.0D); + zombieNautilusAlwaysDropExp = getBoolean("mobs.zombie_nautilus.always-drop-exp", zombieNautilusAlwaysDropExp); + } + + public boolean zombieVillagerRidable = false; + public boolean zombieVillagerRidableInWater = true; + public boolean zombieVillagerControllable = true; + public double zombieVillagerMaxHealth = 20.0D; + public double zombieVillagerScale = 1.0D; + public double zombieVillagerSpawnReinforcements = 0.1D; + public boolean zombieVillagerJockeyOnlyBaby = true; + public double zombieVillagerJockeyChance = 0.05D; + public boolean zombieVillagerJockeyTryExistingChickens = true; + public boolean zombieVillagerTakeDamageFromWater = false; + public int zombieVillagerCuringTimeMin = 3600; + public int zombieVillagerCuringTimeMax = 6000; + public boolean zombieVillagerCureEnabled = true; + public boolean zombieVillagerAlwaysDropExp = false; + private void zombieVillagerSettings() { + zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); + zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); + zombieVillagerControllable = getBoolean("mobs.zombie_villager.controllable", zombieVillagerControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.zombie_villager.attributes.max-health", zombieVillagerMaxHealth); + set("mobs.zombie_villager.attributes.max-health", null); + set("mobs.zombie_villager.attributes.max_health", oldValue); + } + zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth); + zombieVillagerScale = Mth.clamp(getDouble("mobs.zombie_villager.attributes.scale", zombieVillagerScale), 0.0625D, 16.0D); + zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements); + zombieVillagerJockeyOnlyBaby = getBoolean("mobs.zombie_villager.jockey.only-babies", zombieVillagerJockeyOnlyBaby); + zombieVillagerJockeyChance = getDouble("mobs.zombie_villager.jockey.chance", zombieVillagerJockeyChance); + zombieVillagerJockeyTryExistingChickens = getBoolean("mobs.zombie_villager.jockey.try-existing-chickens", zombieVillagerJockeyTryExistingChickens); + zombieVillagerTakeDamageFromWater = getBoolean("mobs.zombie_villager.takes-damage-from-water", zombieVillagerTakeDamageFromWater); + zombieVillagerCuringTimeMin = getInt("mobs.zombie_villager.curing_time.min", zombieVillagerCuringTimeMin); + zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax); + zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled); + zombieVillagerAlwaysDropExp = getBoolean("mobs.zombie_villager.always-drop-exp", zombieVillagerAlwaysDropExp); + } + + public boolean zombifiedPiglinRidable = false; + public boolean zombifiedPiglinRidableInWater = true; + public boolean zombifiedPiglinControllable = true; + public double zombifiedPiglinMaxHealth = 20.0D; + public double zombifiedPiglinScale = 1.0D; + public double zombifiedPiglinSpawnReinforcements = 0.0D; + public boolean zombifiedPiglinJockeyOnlyBaby = true; + public double zombifiedPiglinJockeyChance = 0.05D; + public boolean zombifiedPiglinJockeyTryExistingChickens = true; + public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = false; + public boolean zombifiedPiglinTakeDamageFromWater = false; + public boolean zombifiedPiglinAlwaysDropExp = false; + private void zombifiedPiglinSettings() { + zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); + zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); + zombifiedPiglinControllable = getBoolean("mobs.zombified_piglin.controllable", zombifiedPiglinControllable); + if (PurpurConfig.version < 10) { + double oldValue = getDouble("mobs.zombified_piglin.attributes.max-health", zombifiedPiglinMaxHealth); + set("mobs.zombified_piglin.attributes.max-health", null); + set("mobs.zombified_piglin.attributes.max_health", oldValue); + } + zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth); + zombifiedPiglinScale = Mth.clamp(getDouble("mobs.zombified_piglin.attributes.scale", zombifiedPiglinScale), 0.0625D, 16.0D); + zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements); + zombifiedPiglinJockeyOnlyBaby = getBoolean("mobs.zombified_piglin.jockey.only-babies", zombifiedPiglinJockeyOnlyBaby); + zombifiedPiglinJockeyChance = getDouble("mobs.zombified_piglin.jockey.chance", zombifiedPiglinJockeyChance); + zombifiedPiglinJockeyTryExistingChickens = getBoolean("mobs.zombified_piglin.jockey.try-existing-chickens", zombifiedPiglinJockeyTryExistingChickens); + if (PurpurConfig.version < 42) { + set("mobs.zombified_piglin.count-as-player-kill-when-angry", false); + } + zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry); + zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater); + zombifiedPiglinAlwaysDropExp = getBoolean("mobs.zombified_piglin.always-drop-exp", zombifiedPiglinAlwaysDropExp); + } + + public float hungerStarvationDamage = 1.0F; + private void hungerSettings() { + hungerStarvationDamage = (float) getDouble("hunger.starvation-damage", hungerStarvationDamage); + } + + public int conduitDistance = 16; + public double conduitDamageDistance = 8; + public float conduitDamageAmount = 4; + public Block[] conduitBlocks; + private void conduitSettings() { + conduitDistance = getInt("blocks.conduit.effect-distance", conduitDistance); + conduitDamageDistance = getDouble("blocks.conduit.mob-damage.distance", conduitDamageDistance); + conduitDamageAmount = (float) getDouble("blocks.conduit.mob-damage.damage-amount", conduitDamageAmount); + List conduitBlockList = new ArrayList<>(); + getList("blocks.conduit.valid-ring-blocks", new ArrayList(){{ + add("minecraft:prismarine"); + add("minecraft:prismarine_bricks"); + add("minecraft:sea_lantern"); + add("minecraft:dark_prismarine"); + }}).forEach(key -> { + Block block = BuiltInRegistries.BLOCK.getValue(Identifier.parse(key.toString())); + if (!block.defaultBlockState().isAir()) { + conduitBlockList.add(block); + } + }); + conduitBlocks = conduitBlockList.toArray(Block[]::new); + } + + public float cauldronRainChance = 0.05F; + public float cauldronPowderSnowChance = 0.1F; + public float cauldronDripstoneWaterFillChance = 0.17578125F; + public float cauldronDripstoneLavaFillChance = 0.05859375F; + private void cauldronSettings() { + cauldronRainChance = (float) getDouble("blocks.cauldron.fill-chances.rain", cauldronRainChance); + cauldronPowderSnowChance = (float) getDouble("blocks.cauldron.fill-chances.powder-snow", cauldronPowderSnowChance); + cauldronDripstoneWaterFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-water", cauldronDripstoneWaterFillChance); + cauldronDripstoneLavaFillChance = (float) getDouble("blocks.cauldron.fill-chances.dripstone-lava", cauldronDripstoneLavaFillChance); + } + + public float shearsCanDefuseTntChance = 0.00F; + public boolean shearsCanDefuseTnt = false; + private void shearsCanDefuseTntSettings() { + shearsCanDefuseTntChance = (float) getDouble("gameplay-mechanics.item.shears.defuse-tnt-chance", 0.00D); + shearsCanDefuseTnt = shearsCanDefuseTntChance > 0.00F; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/CompassCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/CompassCommand.java new file mode 100644 index 000000000..99d344d7f --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/CompassCommand.java @@ -0,0 +1,28 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.task.CompassTask; + +public class CompassCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("compass") + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.compass")) + .executes(context -> { + ServerPlayer player = context.getSource().getPlayerOrException(); + CompassTask task = CompassTask.instance(); + if (player.compassBar()) { + task.removePlayer(player.getBukkitEntity()); + player.compassBar(false); + } else { + task.addPlayer(player.getBukkitEntity()); + player.compassBar(true); + } + return 1; + }) + ); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java new file mode 100644 index 000000000..01c6be4ef --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/CreditsCommand.java @@ -0,0 +1,36 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; + +import java.util.Collection; +import java.util.Collections; + +public class CreditsCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("credits") + .requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.credits")) + .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) + .then(Commands.argument("targets", EntityArgument.players()) + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.credits.other")) + .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) + ) + ); + } + + private static int execute(CommandSourceStack sender, Collection targets) { + for (ServerPlayer player : targets) { + ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, 1F); + player.connection.send(packet); + String output = String.format(PurpurConfig.creditsCommandOutput, player.getGameProfile().name()); + sender.sendSuccess(output, false); + } + return targets.size(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/DemoCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/DemoCommand.java new file mode 100644 index 000000000..30259ea94 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/DemoCommand.java @@ -0,0 +1,36 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.protocol.game.ClientboundGameEventPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; + +import java.util.Collection; +import java.util.Collections; + +public class DemoCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("demo") + .requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.demo")) + .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) + .then(Commands.argument("targets", EntityArgument.players()) + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.demo.other")) + .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) + ) + ); + } + + private static int execute(CommandSourceStack sender, Collection targets) { + for (ServerPlayer player : targets) { + ClientboundGameEventPacket packet = new ClientboundGameEventPacket(ClientboundGameEventPacket.DEMO_EVENT, 0); + player.connection.send(packet); + String output = String.format(PurpurConfig.demoCommandOutput, player.getGameProfile().name()); + sender.sendSuccess(output, false); + } + return targets.size(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/PingCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/PingCommand.java new file mode 100644 index 000000000..ae32b99e0 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/PingCommand.java @@ -0,0 +1,33 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; + +import java.util.Collection; +import java.util.Collections; + +public class PingCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("ping") + .requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ping")) + .executes((context) -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) + .then(Commands.argument("targets", EntityArgument.players()) + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ping.other")) + .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) + ) + ); + } + + private static int execute(CommandSourceStack sender, Collection targets) { + for (ServerPlayer player : targets) { + String output = String.format(PurpurConfig.pingCommandOutput, player.getGameProfile().name(), player.connection.latency()); + sender.sendSuccess(output, false); + } + return targets.size(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java new file mode 100644 index 000000000..7163c8247 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/PurpurCommand.java @@ -0,0 +1,66 @@ +package org.purpurmc.purpur.command; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import org.purpurmc.purpur.PurpurConfig; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class PurpurCommand extends Command { + public PurpurCommand(String name) { + super(name); + this.description = "Purpur related commands"; + this.usageMessage = "/purpur [reload | version]"; + this.setPermission("bukkit.command.purpur"); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + if (args.length == 1) { + return Stream.of("reload", "version") + .filter(arg -> arg.startsWith(args[0].toLowerCase())) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + if (args[0].equalsIgnoreCase("reload")) { + Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); + Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); + + MinecraftServer console = MinecraftServer.getServer(); + PurpurConfig.init((File) console.options.valueOf("purpur-settings")); + for (ServerLevel level : console.getAllLevels()) { + level.purpurConfig.init(); + level.resetBreedingCooldowns(); // Purpur - Add adjustable breeding cooldown to config + } + console.server.reloadCount++; + + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete."); + } else if (args[0].equalsIgnoreCase("version")) { + Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); + if (verCmd != null) { + return verCmd.execute(sender, commandLabel, new String[0]); + } + } + + return true; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java new file mode 100644 index 000000000..78ae578aa --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/RamBarCommand.java @@ -0,0 +1,45 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; +import org.purpurmc.purpur.task.RamBarTask; + +import java.util.Collection; +import java.util.Collections; + +public class RamBarCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("rambar") + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.rambar")) + .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) + .then(Commands.argument("targets", EntityArgument.players()) + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.rambar.other")) + .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) + ) + ); + } + + private static int execute(CommandSourceStack sender, Collection targets) { + for (ServerPlayer player : targets) { + boolean result = RamBarTask.instance().togglePlayer(player.getBukkitEntity()); + player.ramBar(result); + + Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.rambarCommandOutput, + Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off") + .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)), + Placeholder.parsed("target", player.getGameProfile().name())); + + sender.sendSuccess(output, false); + } + return targets.size(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/RamCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/RamCommand.java new file mode 100644 index 000000000..3a1d6eecf --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/RamCommand.java @@ -0,0 +1,31 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; +import org.purpurmc.purpur.task.RamBarTask; + +public class RamCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("ram") + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.ram")) + .executes(context -> { + CommandSourceStack sender = context.getSource(); + RamBarTask ramBar = RamBarTask.instance(); + sender.sendSuccess(() -> PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(PurpurConfig.ramCommandOutput, + Placeholder.component("allocated", ramBar.format(ramBar.getAllocated())), + Placeholder.component("used", ramBar.format(ramBar.getUsed())), + Placeholder.component("xmx", ramBar.format(ramBar.getXmx())), + Placeholder.component("xms", ramBar.format(ramBar.getXms())), + Placeholder.unparsed("percent", ((int) (ramBar.getPercent() * 100)) + "%") + )), false); + return 1; + }) + ); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java new file mode 100644 index 000000000..668b633c7 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/TPSBarCommand.java @@ -0,0 +1,45 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; +import org.purpurmc.purpur.task.TPSBarTask; + +import java.util.Collection; +import java.util.Collections; + +public class TPSBarCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("tpsbar") + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.tpsbar")) + .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) + .then(Commands.argument("targets", EntityArgument.players()) + .requires(listener -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.tpsbar.other")) + .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) + ) + ); + } + + private static int execute(CommandSourceStack sender, Collection targets) { + for (ServerPlayer player : targets) { + boolean result = TPSBarTask.instance().togglePlayer(player.getBukkitEntity()); + player.tpsBar(result); + + Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.tpsbarCommandOutput, + Placeholder.component("onoff", Component.translatable(result ? "options.on" : "options.off") + .color(result ? NamedTextColor.GREEN : NamedTextColor.RED)), + Placeholder.parsed("target", player.getGameProfile().name())); + + sender.sendSuccess(output, false); + } + return targets.size(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java b/purpur-server/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java new file mode 100644 index 000000000..fb0ca957f --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/UptimeCommand.java @@ -0,0 +1,56 @@ +package org.purpurmc.purpur.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.permissions.Permissions; +import org.purpurmc.purpur.PurpurConfig; + +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +public class UptimeCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("uptime") + .requires((listener) -> listener.hasPermission(Permissions.COMMANDS_GAMEMASTER, "bukkit.command.uptime")) + .executes((context) -> execute(context.getSource())) + ); + } + + private static int execute(CommandSourceStack sender) { + Data data = new Data(); + + data.format = PurpurConfig.uptimeFormat; + data.hide = true; + data.millis = System.currentTimeMillis() - MinecraftServer.startTimeMillis; + + process(data, "", PurpurConfig.uptimeDay, PurpurConfig.uptimeDays, TimeUnit.DAYS, TimeUnit.MILLISECONDS::toDays); + process(data, "", PurpurConfig.uptimeHour, PurpurConfig.uptimeHours, TimeUnit.HOURS, TimeUnit.MILLISECONDS::toHours); + process(data, "", PurpurConfig.uptimeMinute, PurpurConfig.uptimeMinutes, TimeUnit.MINUTES, TimeUnit.MILLISECONDS::toMinutes); + data.hide = false; // never hide seconds + process(data, "", PurpurConfig.uptimeSecond, PurpurConfig.uptimeSeconds, TimeUnit.SECONDS, TimeUnit.MILLISECONDS::toSeconds); + + Component output = MiniMessage.miniMessage().deserialize(PurpurConfig.uptimeCommandOutput, Placeholder.unparsed("uptime", data.format)); + sender.sendSuccess(output, false); + return 1; + } + + private static void process(Data data, String replace, String singular, String plural, TimeUnit unit, Function func) { + if (data.format.contains(replace)) { + long val = func.apply(data.millis); + if (data.hide) data.hide = val == 0; + if (!data.hide) data.millis -= unit.toMillis(val); + data.format = data.format.replace(replace, data.hide ? "" : String.format(val == 1 ? singular : plural, val)); + } + } + + private static class Data { + String format; + boolean hide; + long millis; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/configuration/transformation/FarEndTerrainGenerationMigration.java b/purpur-server/src/main/java/org/purpurmc/purpur/configuration/transformation/FarEndTerrainGenerationMigration.java new file mode 100644 index 000000000..eaa3e8a7a --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/configuration/transformation/FarEndTerrainGenerationMigration.java @@ -0,0 +1,41 @@ +package org.purpurmc.purpur.configuration.transformation; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.purpurmc.purpur.PurpurConfig; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import static org.spongepowered.configurate.NodePath.path; + +public class FarEndTerrainGenerationMigration implements TransformAction { + + public static boolean HAS_BEEN_REGISTERED = false; + + public static final String MISC_KEY = "misc"; + public static final String FIX_FAR_END_TERRAIN_GENERATION_KEY = "fix-far-end-terrain-generation"; + + @Override + public Object @Nullable [] visitPath(final NodePath path, final ConfigurationNode value) throws ConfigurateException { + String purpurGenerateEndVoidRingsPath = "settings.generate-end-void-rings"; + ConfigurationNode fixFarEndTerrainGenerationNode = value.node(MISC_KEY, FIX_FAR_END_TERRAIN_GENERATION_KEY); + if (PurpurConfig.config.contains(purpurGenerateEndVoidRingsPath)) { + boolean purpurGenerateEndVoidRings = PurpurConfig.config.getBoolean(purpurGenerateEndVoidRingsPath); + if (purpurGenerateEndVoidRings) { + fixFarEndTerrainGenerationNode.set(false); + } + PurpurConfig.config.set(purpurGenerateEndVoidRingsPath, null); + } + + return null; + } + + public static void apply(final ConfigurationTransformation.Builder builder) { + if (PurpurConfig.version < 46) { + HAS_BEEN_REGISTERED = true; + builder.addAction(path(), new FarEndTerrainGenerationMigration()); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java b/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java new file mode 100644 index 000000000..940bcc6f7 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingMoveControllerWASD.java @@ -0,0 +1,74 @@ +package org.purpurmc.purpur.controller; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Input; +import net.minecraft.world.entity.player.Player; + +public class FlyingMoveControllerWASD extends MoveControllerWASD { + protected final float groundSpeedModifier; + protected final float flyingSpeedModifier; + protected int tooHighCooldown = 0; + protected boolean setNoGravityFlag; + + public FlyingMoveControllerWASD(Mob entity) { + this(entity, 1.0F); + } + + public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier) { + this(entity, groundSpeedModifier, 1.0F, true); + } + + public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier) { + this(entity, groundSpeedModifier, flyingSpeedModifier, true); + } + + public FlyingMoveControllerWASD(Mob entity, float groundSpeedModifier, float flyingSpeedModifier, boolean setNoGravityFlag) { + super(entity); + this.groundSpeedModifier = groundSpeedModifier; + this.flyingSpeedModifier = flyingSpeedModifier; + this.setNoGravityFlag = setNoGravityFlag; + } + + @Override + public void purpurTick(Player rider) { + Input lastClientInput = ((ServerPlayer) rider).getLastClientInput(); + float forward = lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : 0.0F; + float vertical = forward == 0.0F ? 0.0F : -(rider.xRotO / 45.0F); + float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F); + + if (lastClientInput.jump() && spacebarEvent(entity)) { + entity.onSpacebar(); + } + + if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) { + if (tooHighCooldown <= 0) { + tooHighCooldown = 20; + } + entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.05D, 0.0D)); + vertical = 0.0F; + } + + setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED)); + float speed = (float) getSpeedModifier(); + + if (entity.onGround) { + speed *= groundSpeedModifier; // TODO = fix this! + } else { + speed *= flyingSpeedModifier; + } + + if (setNoGravityFlag) { + entity.setNoGravity(forward > 0); + } + + entity.setSpeed(speed); + entity.setVerticalMot(vertical); + entity.setStrafeMot(strafe); + entity.setForwardMot(forward); + + setForward(entity.getForwardMot()); + setStrafe(entity.getStrafeMot()); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java b/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java new file mode 100644 index 000000000..e0bbaec05 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/controller/FlyingWithSpacebarMoveControllerWASD.java @@ -0,0 +1,66 @@ +package org.purpurmc.purpur.controller; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Input; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; + +public class FlyingWithSpacebarMoveControllerWASD extends FlyingMoveControllerWASD { + public FlyingWithSpacebarMoveControllerWASD(Mob entity) { + super(entity); + } + + public FlyingWithSpacebarMoveControllerWASD(Mob entity, float groundSpeedModifier) { + super(entity, groundSpeedModifier); + } + + @Override + public void purpurTick(Player rider) { + Input lastClientInput = ((ServerPlayer) rider).getLastClientInput(); + float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F); + float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.5F; + float vertical = 0; + + if (forward < 0.0F) { + forward *= 0.5F; + strafe *= 0.5F; + } + + float speed = (float) entity.getAttributeValue(Attributes.MOVEMENT_SPEED); + + if (entity.onGround) { + speed *= groundSpeedModifier; + } + + if (lastClientInput.jump() && spacebarEvent(entity) && !entity.onSpacebar()) { + entity.setNoGravity(true); + vertical = 1.0F; + } else { + entity.setNoGravity(false); + } + + if (entity.getY() >= entity.getMaxY() || --tooHighCooldown > 0) { + if (tooHighCooldown <= 0) { + tooHighCooldown = 20; + } + entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, -0.2D, 0.0D)); + vertical = 0.0F; + } + + setSpeedModifier(speed); + entity.setSpeed((float) getSpeedModifier()); + entity.setVerticalMot(vertical); + entity.setStrafeMot(strafe); + entity.setForwardMot(forward); + + setForward(entity.getForwardMot()); + setStrafe(entity.getStrafeMot()); + + Vec3 mot = entity.getDeltaMovement(); + if (mot.y > 0.2D) { + entity.setDeltaMovement(mot.x, 0.2D, mot.z); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java b/purpur-server/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java new file mode 100644 index 000000000..9b177a9a2 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/controller/LookControllerWASD.java @@ -0,0 +1,79 @@ +package org.purpurmc.purpur.controller; + + +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.control.LookControl; +import net.minecraft.world.entity.player.Player; + +public class LookControllerWASD extends LookControl { + protected final Mob entity; + private float yOffset = 0; + private float xOffset = 0; + + public LookControllerWASD(Mob entity) { + super(entity); + this.entity = entity; + } + + // tick + @Override + public void tick() { + if (entity.getRider() != null && entity.isControllable()) { + purpurTick(entity.getRider()); + } else { + vanillaTick(); + } + } + + protected void purpurTick(Player rider) { + setYawPitch(rider.getYRot(), rider.getXRot()); + } + + public void vanillaTick() { + super.tick(); + } + + public void setYawPitch(float yRot, float xRot) { + entity.setXRot(normalizePitch(xRot + xOffset)); + entity.setYRot(normalizeYaw(yRot + yOffset)); + entity.setYHeadRot(entity.getYRot()); + entity.xRotO = entity.getXRot(); + entity.yRotO = entity.getYRot(); + + ClientboundMoveEntityPacket.PosRot entityPacket = new ClientboundMoveEntityPacket.PosRot( + entity.getId(), + (short) 0, (short) 0, (short) 0, + (byte) Mth.floor(entity.getYRot() * 256.0F / 360.0F), + (byte) Mth.floor(entity.getXRot() * 256.0F / 360.0F), + entity.onGround + ); + ((ServerLevel) entity.level()).getChunkSource().sendToTrackingPlayers(entity, entityPacket); + } + + public void setOffsets(float yaw, float pitch) { + yOffset = yaw; + xOffset = pitch; + } + + public float normalizeYaw(float yaw) { + yaw %= 360.0f; + if (yaw >= 180.0f) { + yaw -= 360.0f; + } else if (yaw < -180.0f) { + yaw += 360.0f; + } + return yaw; + } + + public float normalizePitch(float pitch) { + if (pitch > 90.0f) { + pitch = 90.0f; + } else if (pitch < -90.0f) { + pitch = -90.0f; + } + return pitch; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java b/purpur-server/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java new file mode 100644 index 000000000..34f3c43fa --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/controller/MoveControllerWASD.java @@ -0,0 +1,92 @@ +package org.purpurmc.purpur.controller; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.control.MoveControl; +import net.minecraft.world.entity.player.Input; +import net.minecraft.world.entity.player.Player; +import org.purpurmc.purpur.event.entity.RidableSpacebarEvent; + +public class MoveControllerWASD extends MoveControl { + protected final Mob entity; + private final double speedModifier; + + public MoveControllerWASD(Mob entity) { + this(entity, 1.0D); + } + + public MoveControllerWASD(Mob entity, double speedModifier) { + super(entity); + this.entity = entity; + this.speedModifier = speedModifier; + } + + @Override + public boolean hasWanted() { + return entity.getRider() != null ? strafeForwards != 0 || strafeRight != 0 : super.hasWanted(); + } + + @Override + public void tick() { + if (entity.getRider() != null && entity.isControllable()) { + purpurTick(entity.getRider()); + } else { + vanillaTick(); + } + } + + public void vanillaTick() { + super.tick(); + } + + public void purpurTick(Player rider) { + Input lastClientInput = ((ServerPlayer) rider).getLastClientInput(); + float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F) * 0.5F; + float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.25F; + + if (forward <= 0.0F) { + forward *= 0.5F; + } + + float yawOffset = 0; + if (strafe != 0) { + if (forward == 0) { + yawOffset += strafe > 0 ? -90 : 90; + forward = Math.abs(strafe * 2); + } else { + yawOffset += strafe > 0 ? -30 : 30; + strafe /= 2; + if (forward < 0) { + yawOffset += strafe > 0 ? -110 : 110; + forward *= -1; + } + } + } else if (forward < 0) { + yawOffset -= 180; + forward *= -1; + } + + ((LookControllerWASD) entity.getLookControl()).setOffsets(yawOffset, 0); + + if (lastClientInput.jump() && spacebarEvent(entity) && !entity.onSpacebar() && entity.onGround) { + entity.jumpFromGround(); + } + + setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier); + + entity.setSpeed((float) getSpeedModifier()); + entity.setForwardMot(forward); + + setForward(entity.getForwardMot()); + setStrafe(entity.getStrafeMot()); + } + + public static boolean spacebarEvent(Mob entity) { + if (RidableSpacebarEvent.getHandlerList().getRegisteredListeners().length > 0) { + return new RidableSpacebarEvent(entity.getBukkitEntity()).callEvent(); + } else { + return true; + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java b/purpur-server/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java new file mode 100644 index 000000000..922e48799 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/controller/WaterMoveControllerWASD.java @@ -0,0 +1,53 @@ +package org.purpurmc.purpur.controller; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Input; +import net.minecraft.world.entity.player.Player; + +public class WaterMoveControllerWASD extends MoveControllerWASD { + private final double speedModifier; + + public WaterMoveControllerWASD(Mob entity) { + this(entity, 1.0D); + } + + public WaterMoveControllerWASD(Mob entity, double speedModifier) { + super(entity); + this.speedModifier = speedModifier; + } + + @Override + public void purpurTick(Player rider) { + Input lastClientInput = ((ServerPlayer) rider).getLastClientInput(); + float forward = (lastClientInput.forward() == lastClientInput.backward() ? 0.0F : lastClientInput.forward() ? 1.0F : -1.0F); + float strafe = (lastClientInput.left() == lastClientInput.right() ? 0.0F : lastClientInput.left() ? 1.0F : -1.0F) * 0.5F; // strafe slower by default + float vertical = -(rider.xRotO / 90); + + if (forward == 0.0F) { + // strafe slower if not moving forward + strafe *= 0.5F; + // do not move vertically if not moving forward + vertical = 0.0F; + } else if (forward < 0.0F) { + // water animals can't swim backwards + forward = 0.0F; + vertical = 0.0F; + } + + if (rider.jumping && spacebarEvent(entity)) { + entity.onSpacebar(); + } + + setSpeedModifier(entity.getAttributeValue(Attributes.MOVEMENT_SPEED) * speedModifier); + entity.setSpeed((float) getSpeedModifier() * 0.1F); + + entity.setForwardMot(forward * (float) speedModifier); + entity.setStrafeMot(strafe * (float) speedModifier); + entity.setVerticalMot(vertical * (float) speedModifier); + + setForward(entity.getForwardMot()); + setStrafe(entity.getStrafeMot()); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java new file mode 100644 index 000000000..c468cfa59 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java @@ -0,0 +1,116 @@ +package org.purpurmc.purpur.entity; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.minecraft.core.RegistryAccess; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.level.block.entity.BeehiveBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.ValueInput; +import org.bukkit.block.EntityBlockStorage; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.Bee; +import org.bukkit.entity.EntityType; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import java.util.Locale; + +public class PurpurStoredBee implements StoredEntity { + static final Logger LOGGER = LogUtils.getLogger(); + + private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); + + private final EntityBlockStorage blockStorage; + private final BeehiveBlockEntity.BeeData handle; + private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(PurpurStoredBee.DATA_TYPE_REGISTRY); + + private Component customName; + + public PurpurStoredBee(BeehiveBlockEntity.BeeData data, EntityBlockStorage blockStorage, final BeehiveBlockEntity blockEntity) { + this.handle = data; + this.blockStorage = blockStorage; + + CompoundTag customData = handle.occupant.entityData().copyTagWithEntityId(); + + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(blockEntity.problemPath(), LOGGER)) { + ValueInput valueInput = TagValueInput.create(scopedCollector, RegistryAccess.EMPTY, customData); + + net.minecraft.network.chat.Component customNameMinecraft = BlockEntity.parseCustomNameSafe(valueInput, "CustomName"); + this.customName = customNameMinecraft == null ? null : PaperAdventure.asAdventure(customNameMinecraft); + + if (customData.get("BukkitValues") instanceof CompoundTag compoundTag) { + this.persistentDataContainer.putAll(compoundTag); + } + } + } + + public BeehiveBlockEntity.BeeData getHandle() { + return handle; + } + + @Override + public @Nullable Component customName() { + return customName; + } + + @Override + public void customName(@Nullable Component customName) { + this.customName = customName; + } + + @Override + public @Nullable String getCustomName() { + return PaperAdventure.asPlain(customName, Locale.US); + } + + @Override + public void setCustomName(@Nullable String name) { + customName(name != null ? Component.text(name) : null); + } + + @Override + public @NotNull PersistentDataContainer getPersistentDataContainer() { + return persistentDataContainer; + } + + @Override + public boolean hasBeenReleased() { + return !blockStorage.getEntities().contains(this); + } + + @Override + public @Nullable Bee release() { + return blockStorage.releaseEntity(this); + } + + @Override + public @Nullable EntityBlockStorage getBlockStorage() { + if(hasBeenReleased()) { + return null; + } + + return blockStorage; + } + + @Override + public @NotNull EntityType getType() { + return EntityType.BEE; + } + + @Override + public void update() { + handle.occupant.entityData().copyTagWithEntityId().put("BukkitValues", this.persistentDataContainer.toTagCompound()); + if(customName == null) { + handle.occupant.entityData().copyTagWithEntityId().remove("CustomName"); + } else { + handle.occupant.entityData().copyTagWithEntityId().putString("CustomName", CraftChatMessage.toJSON(PaperAdventure.asVanilla(customName))); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java new file mode 100644 index 000000000..8babdaddd --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HasRider.java @@ -0,0 +1,20 @@ +package org.purpurmc.purpur.entity.ai; + +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.ai.goal.Goal; + +import java.util.EnumSet; + +public class HasRider extends Goal { + public final Mob entity; + + public HasRider(Mob entity) { + this.entity = entity; + setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.TARGET, Flag.UNKNOWN_BEHAVIOR)); + } + + @Override + public boolean canUse() { + return entity.getRider() != null && entity.isControllable(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java new file mode 100644 index 000000000..121b78edd --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/HorseHasRider.java @@ -0,0 +1,17 @@ +package org.purpurmc.purpur.entity.ai; + +import net.minecraft.world.entity.animal.equine.AbstractHorse; + +public class HorseHasRider extends HasRider { + public final AbstractHorse horse; + + public HorseHasRider(AbstractHorse entity) { + super(entity); + this.horse = entity; + } + + @Override + public boolean canUse() { + return super.canUse() && horse.isSaddled(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java new file mode 100644 index 000000000..c7fc34327 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/LlamaHasRider.java @@ -0,0 +1,17 @@ +package org.purpurmc.purpur.entity.ai; + +import net.minecraft.world.entity.animal.equine.Llama; + +public class LlamaHasRider extends HasRider { + public final Llama llama; + + public LlamaHasRider(Llama entity) { + super(entity); + this.llama = entity; + } + + @Override + public boolean canUse() { + return super.canUse() && llama.isSaddled() && llama.isControllable(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java new file mode 100644 index 000000000..39c3b4d55 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/ai/ReceiveFlower.java @@ -0,0 +1,95 @@ +package org.purpurmc.purpur.entity.ai; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityReference; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.animal.golem.IronGolem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; + +import java.util.EnumSet; +import java.util.UUID; +import org.jetbrains.annotations.NotNull; + +public class ReceiveFlower extends Goal { + private final IronGolem irongolem; + private ServerPlayer target; + private int cooldown; + + public ReceiveFlower(IronGolem entity) { + this.irongolem = entity; + setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + if (this.irongolem.getOfferFlowerTick() > 0) { + return false; + } + if (!this.irongolem.isAngry()) { + return false; + } + EntityReference persistentAngerTarget = this.irongolem.getPersistentAngerTarget(); + if (persistentAngerTarget == null) { + return false; + } + + LivingEntity target = EntityReference.getLivingEntity(persistentAngerTarget, this.irongolem.level()); + if (!(target instanceof ServerPlayer player)) { + return false; + } + InteractionHand hand = getPoppyHand(player); + if (hand == null) { + return false; + } + removeFlower(player, hand); + this.target = player; + return true; + } + + @Override + public boolean canContinueToUse() { + return this.cooldown > 0; + } + + @Override + public void start() { + this.cooldown = 100; + this.irongolem.stopBeingAngry(); + this.irongolem.offerFlower(true); + } + + @Override + public void stop() { + this.irongolem.offerFlower(false); + this.target = null; + } + + @Override + public void tick() { + this.irongolem.getLookControl().setLookAt(this.target, 30.0F, 30.0F); + --this.cooldown; + } + + private InteractionHand getPoppyHand(ServerPlayer player) { + if (isPoppy(player.getMainHandItem())) { + return InteractionHand.MAIN_HAND; + } + if (isPoppy(player.getOffhandItem())) { + return InteractionHand.OFF_HAND; + } + return null; + } + + private void removeFlower(ServerPlayer player, InteractionHand hand) { + player.setItemInHand(hand, ItemStack.EMPTY); + } + + private boolean isPoppy(ItemStack item) { + return item.getItem() == Blocks.POPPY.asItem(); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/DolphinSpit.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/DolphinSpit.java new file mode 100644 index 000000000..6bd739353 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/DolphinSpit.java @@ -0,0 +1,105 @@ +package org.purpurmc.purpur.entity.projectile; + +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.dolphin.Dolphin; +import net.minecraft.world.entity.projectile.LlamaSpit; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import org.bukkit.event.entity.EntityRemoveEvent; + +public class DolphinSpit extends LlamaSpit { + public LivingEntity dolphin; + public int ticksLived; + + public DolphinSpit(EntityType type, Level world) { + super(type, world); + } + + public DolphinSpit(Level world, Dolphin dolphin) { + this(EntityType.LLAMA_SPIT, world); + this.setOwner(dolphin.getRider() != null ? dolphin.getRider() : dolphin); + this.dolphin = dolphin; + this.setPos( + dolphin.getX() - (double) (dolphin.getBbWidth() + 1.0F) * 0.5 * (double) Mth.sin(dolphin.yBodyRot * (float) (Math.PI / 180.0)), + dolphin.getEyeY() - 0.1F, + dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5 * (double) Mth.cos(dolphin.yBodyRot * (float) (Math.PI / 180.0))); + } + + @Override + public boolean canSaveToDisk() { + return false; + } + + public void tick() { + projectileTick(); + + Vec3 mot = this.getDeltaMovement(); + HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); + + this.preHitTargetOrDeflectSelf(hitResult); + + double x = this.getX() + mot.x; + double y = this.getY() + mot.y; + double z = this.getZ() + mot.z; + + this.updateRotation(); + + Vec3 motDouble = mot.scale(2.0); + for (int i = 0; i < 5; i++) { + ((ServerLevel) level()).sendParticlesSource(null, ParticleTypes.BUBBLE, + true, false, + getX() + random.nextFloat() / 2 - 0.25F, + getY() + random.nextFloat() / 2 - 0.25F, + getZ() + random.nextFloat() / 2 - 0.25F, + 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D); + } + + if (++ticksLived > 20) { + this.discard(EntityRemoveEvent.Cause.DISCARD); + } else { + this.setDeltaMovement(mot.scale(0.99D)); + if (!this.isNoGravity()) { + this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D)); + } + + this.setPos(x, y, z); + } + } + + @Override + public void shoot(double x, double y, double z, float speed, float inaccuracy) { + setDeltaMovement(new Vec3(x, y, z).normalize().add( + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy) + .scale(speed)); + } + + @Override + protected void onHitEntity(EntityHitResult entityHitResult) { + Entity shooter = this.getOwner(); + if (shooter instanceof LivingEntity) { + entityHitResult.getEntity().hurt(entityHitResult.getEntity().damageSources().mobProjectile(this, (LivingEntity) shooter), level().purpurConfig.dolphinSpitDamage); + } + } + + @Override + protected void onHitBlock(BlockHitResult blockHitResult) { + if (this.hitCancelled) { + return; + } + BlockState state = this.level().getBlockState(blockHitResult.getBlockPos()); + state.onProjectileHit(this.level(), state, blockHitResult, this); + this.discard(EntityRemoveEvent.Cause.DISCARD); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/PhantomFlames.java b/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/PhantomFlames.java new file mode 100644 index 000000000..b3fe215e1 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/projectile/PhantomFlames.java @@ -0,0 +1,127 @@ +package org.purpurmc.purpur.entity.projectile; + +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.monster.Phantom; +import net.minecraft.world.entity.projectile.LlamaSpit; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public class PhantomFlames extends LlamaSpit { + public Phantom phantom; + public int ticksLived; + public boolean canGrief = false; + + public PhantomFlames(EntityType type, Level world) { + super(type, world); + } + + public PhantomFlames(Level world, Phantom phantom) { + this(EntityType.LLAMA_SPIT, world); + setOwner(phantom.getRider() != null ? phantom.getRider() : phantom); + this.phantom = phantom; + this.setPos( + phantom.getX() - (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(phantom.yBodyRot * (float) (Math.PI / 180.0)), + phantom.getEyeY() - 0.10000000149011612D, + phantom.getZ() + (double) (phantom.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(phantom.yBodyRot * (float) (Math.PI / 180.0))); + } + + @Override + public boolean canSaveToDisk() { + return false; + } + + public void tick() { + projectileTick(); + + Vec3 mot = this.getDeltaMovement(); + HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); + + this.preHitTargetOrDeflectSelf(hitResult); + + double x = this.getX() + mot.x; + double y = this.getY() + mot.y; + double z = this.getZ() + mot.z; + + this.updateRotation(); + + Vec3 motDouble = mot.scale(2.0); + for (int i = 0; i < 5; i++) { + ((ServerLevel) level()).sendParticlesSource(null, ParticleTypes.FLAME, + true, false, + getX() + random.nextFloat() / 2 - 0.25F, + getY() + random.nextFloat() / 2 - 0.25F, + getZ() + random.nextFloat() / 2 - 0.25F, + 0, motDouble.x(), motDouble.y(), motDouble.z(), 0.1D); + } + + if (++ticksLived > 20) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); + } else if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); + } else if (this.isInWater()) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); + } else { + this.setDeltaMovement(mot.scale(0.99D)); + if (!this.isNoGravity()) { + this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.06D, 0.0D)); + } + + this.setPos(x, y, z); + } + } + + @Override + public void shoot(double x, double y, double z, float speed, float inaccuracy) { + setDeltaMovement(new Vec3(x, y, z).normalize().add( + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy, + random.nextGaussian() * (double) 0.0075F * (double) inaccuracy) + .scale(speed)); + } + + @Override + protected void onHitEntity(EntityHitResult entityHitResult) { + Level world = this.level(); + + if (world instanceof ServerLevel worldserver) { + Entity shooter = this.getOwner(); + if (shooter instanceof LivingEntity) { + Entity target = entityHitResult.getEntity(); + if (canGrief || (target instanceof LivingEntity && !(target instanceof ArmorStand))) { + boolean hurt = target.hurtServer(worldserver, target.damageSources().mobProjectile(this, (LivingEntity) shooter), worldserver.purpurConfig.phantomFlameDamage); + if (hurt && worldserver.purpurConfig.phantomFlameFireTime > 0) { + target.igniteForSeconds(worldserver.purpurConfig.phantomFlameFireTime); + } + } + } + } + } + + @Override + protected void onHitBlock(BlockHitResult blockHitResult) { + Level world = this.level(); + + if (world instanceof ServerLevel worldserver) { + if (this.hitCancelled) { + return; + } + if (this.canGrief) { + BlockState state = worldserver.getBlockState(blockHitResult.getBlockPos()); + state.onProjectileHit(worldserver, state, blockHitResult, this); + } + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/gui/GUIColor.java b/purpur-server/src/main/java/org/purpurmc/purpur/gui/GUIColor.java new file mode 100644 index 000000000..550222758 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/gui/GUIColor.java @@ -0,0 +1,58 @@ +package org.purpurmc.purpur.gui; + +import net.md_5.bungee.api.ChatColor; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; + +public enum GUIColor { + BLACK(ChatColor.BLACK, new Color(0x000000)), + DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)), + DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)), + DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)), + DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)), + DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)), + GOLD(ChatColor.GOLD, new Color(0xBB8800)), + GRAY(ChatColor.GRAY, new Color(0x888888)), + DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)), + BLUE(ChatColor.BLUE, new Color(0x5555FF)), + GREEN(ChatColor.GREEN, new Color(0x55FF55)), + AQUA(ChatColor.AQUA, new Color(0x55DDDD)), + RED(ChatColor.RED, new Color(0xFF5555)), + LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)), + YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)), + WHITE(ChatColor.WHITE, new Color(0xBBBBBB)); + + private final ChatColor chat; + private final Color color; + + private static final Map BY_CHAT = new HashMap<>(); + + GUIColor(ChatColor chat, Color color) { + this.chat = chat; + this.color = color; + } + + public Color getColor() { + return color; + } + + public ChatColor getChatColor() { + return chat; + } + + public String getCode() { + return chat.toString(); + } + + public static GUIColor getColor(ChatColor chat) { + return BY_CHAT.get(chat); + } + + static { + for (GUIColor color : values()) { + BY_CHAT.put(color.chat, color); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java b/purpur-server/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java new file mode 100644 index 000000000..d75fb5e77 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java @@ -0,0 +1,83 @@ +package org.purpurmc.purpur.gui; + +import com.google.common.collect.Sets; +import javax.swing.UIManager; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; + +import javax.swing.JTextPane; +import javax.swing.Timer; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; +import java.util.Set; + +public class JColorTextPane extends JTextPane { + private static final GUIColor DEFAULT_COLOR; + static { + DEFAULT_COLOR = UIManager.getSystemLookAndFeelClassName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel") + ? GUIColor.WHITE : GUIColor.BLACK; + } + + + public void append(String msg) { + // TODO: update to use adventure instead + BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, DEFAULT_COLOR.getChatColor()); + for (BaseComponent component : components) { + String text = component.toPlainText(); + if (text == null || text.isEmpty()) { + continue; + } + + GUIColor guiColor = GUIColor.getColor(component.getColor()); + + StyleContext context = StyleContext.getDefaultStyleContext(); + AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor()); + attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR); + attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic()); + attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined()); + attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough()); + //attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly + + try { + int pos = getDocument().getLength(); + getDocument().insertString(pos, text, attr); + + if (component.isObfuscated()) { + // dirty hack to blink some text + Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground())); + BLINKS.add(blink); + } + } catch (BadLocationException ignore) { + } + } + } + + private static final Set BLINKS = Sets.newHashSet(); + private static boolean SYNC_BLINK; + + static { + new Timer(500, e -> { + SYNC_BLINK = !SYNC_BLINK; + BLINKS.forEach(Blink::blink); + }).start(); + } + + public class Blink { + private final int start, length; + private final AttributeSet attr1, attr2; + + private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) { + this.start = start; + this.length = length; + this.attr1 = attr1; + this.attr2 = attr2; + } + + private void blink() { + getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java b/purpur-server/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java new file mode 100644 index 000000000..b257f35ca --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/item/GlowBerryItem.java @@ -0,0 +1,26 @@ +package org.purpurmc.purpur.item; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import org.bukkit.event.entity.EntityPotionEffectEvent; + +public class GlowBerryItem extends BlockItem { + public GlowBerryItem(Block block, Properties settings) { + super(block, settings); + } + + @Override + public ItemStack finishUsingItem(ItemStack stack, Level world, LivingEntity user) { + ItemStack result = super.finishUsingItem(stack, world, user); + if (world.purpurConfig.glowBerriesEatGlowDuration > 0 && user instanceof ServerPlayer player) { + player.addEffect(new MobEffectInstance(MobEffects.GLOWING, world.purpurConfig.glowBerriesEatGlowDuration), EntityPotionEffectEvent.Cause.FOOD); + } + return result; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java b/purpur-server/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java new file mode 100644 index 000000000..cfa02aa02 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java @@ -0,0 +1,43 @@ +package org.purpurmc.purpur.item; + +import java.util.Optional; +import net.minecraft.core.BlockPos; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.SpawnerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class SpawnerItem extends BlockItem { + + public SpawnerItem(Block block, Properties settings) { + super(block, settings); + } + + @Override + protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player, ItemStack stack, BlockState state) { + boolean handled = super.updateCustomBlockEntityTag(pos, level, player, stack, state); + if (level.purpurConfig.silkTouchEnabled && player.getBukkitEntity().hasPermission("purpur.place.spawners")) { + BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof SpawnerBlockEntity spawner) { + CompoundTag customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + Optional mobTypeStringOptional = customData.getString("Purpur.mob_type"); + if (mobTypeStringOptional.isPresent()) { + EntityType.byString(mobTypeStringOptional.get()).ifPresent(type -> spawner.getSpawner().setEntityId(type, level, level.random, pos)); + } else if (customData.contains("Purpur.SpawnData")) { + customData.getCompound("Purpur.SpawnData") + .flatMap(spawnerData -> spawnerData.read("SpawnData", net.minecraft.world.level.SpawnData.CODEC)) + .ifPresent(spawnData -> spawner.getSpawner().nextSpawnData = spawnData); + } + } + } + return handled; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java b/purpur-server/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java new file mode 100644 index 000000000..cc0c7b4a4 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/network/ClientboundBeehivePayload.java @@ -0,0 +1,27 @@ +package org.purpurmc.purpur.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.NotNull; + +public record ClientboundBeehivePayload(BlockPos pos, int numOfBees) implements CustomPacketPayload { + public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ClientboundBeehivePayload::write, ClientboundBeehivePayload::new); + public static final Type TYPE = new Type<>(Identifier.fromNamespaceAndPath("purpur", "beehive_s2c")); + + public ClientboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) { + this(friendlyByteBuf.readBlockPos(), friendlyByteBuf.readInt()); + } + + private void write(FriendlyByteBuf friendlyByteBuf) { + friendlyByteBuf.writeBlockPos(this.pos); + friendlyByteBuf.writeInt(this.numOfBees); + } + + @Override + public @NotNull Type type() { + return TYPE; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java b/purpur-server/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java new file mode 100644 index 000000000..6b980e826 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java @@ -0,0 +1,26 @@ +package org.purpurmc.purpur.network; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.NotNull; + +public record ServerboundBeehivePayload(BlockPos pos) implements CustomPacketPayload { + public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ServerboundBeehivePayload::write, ServerboundBeehivePayload::new); + public static final Type TYPE = new Type<>(Identifier.fromNamespaceAndPath("purpur", "beehive_c2s")); + + public ServerboundBeehivePayload(FriendlyByteBuf friendlyByteBuf) { + this(friendlyByteBuf.readBlockPos()); + } + + private void write(FriendlyByteBuf friendlyByteBuf) { + friendlyByteBuf.writeBlockPos(this.pos); + } + + @Override + public @NotNull Type type() { + return TYPE; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java b/purpur-server/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java new file mode 100644 index 000000000..664f9d5e1 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java @@ -0,0 +1,67 @@ +package org.purpurmc.purpur.task; + +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.entity.BeehiveBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.messaging.PluginMessageListener; +import org.jetbrains.annotations.NotNull; +import org.purpurmc.purpur.network.ClientboundBeehivePayload; +import org.purpurmc.purpur.network.ServerboundBeehivePayload; +import org.purpurmc.purpur.util.MinecraftInternalPlugin; + +public class BeehiveTask implements PluginMessageListener { + + private static BeehiveTask instance; + + public static BeehiveTask instance() { + if (instance == null) { + instance = new BeehiveTask(); + } + return instance; + } + + private final PluginBase plugin = new MinecraftInternalPlugin(); + + private BeehiveTask() { + } + + public void register() { + Bukkit.getMessenger().registerOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString()); + Bukkit.getMessenger().registerIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString(), this); + } + + public void unregister() { + Bukkit.getMessenger().unregisterOutgoingPluginChannel(this.plugin, ClientboundBeehivePayload.TYPE.id().toString()); + Bukkit.getMessenger().unregisterIncomingPluginChannel(this.plugin, ServerboundBeehivePayload.TYPE.id().toString()); + } + + @Override + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] bytes) { + FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.copiedBuffer(bytes)); + ServerboundBeehivePayload payload = ServerboundBeehivePayload.STREAM_CODEC.decode(byteBuf); + + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + // targeted block info max range specified in client at net.minecraft.client.gui.hud.DebugHud#render + if (!payload.pos().getCenter().closerThan(serverPlayer.position(), 20)) return; // Targeted Block info max range is 20 + if (serverPlayer.level().getChunkIfLoaded(payload.pos()) == null) return; + + BlockEntity blockEntity = serverPlayer.level().getBlockEntity(payload.pos()); + if (!(blockEntity instanceof BeehiveBlockEntity beehive)) { + return; + } + + ClientboundBeehivePayload customPacketPayload = new ClientboundBeehivePayload(payload.pos(), beehive.getOccupantCount()); + FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer()); + ClientboundBeehivePayload.STREAM_CODEC.encode(friendlyByteBuf, customPacketPayload); + byte[] byteArray = new byte[friendlyByteBuf.readableBytes()]; + friendlyByteBuf.readBytes(byteArray); + player.sendPluginMessage(this.plugin, customPacketPayload.type().id().toString(), byteArray); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/task/BossBarTask.java b/purpur-server/src/main/java/org/purpurmc/purpur/task/BossBarTask.java new file mode 100644 index 000000000..3c3d4cd52 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/task/BossBarTask.java @@ -0,0 +1,121 @@ +package org.purpurmc.purpur.task; + +import net.kyori.adventure.bossbar.BossBar; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import org.purpurmc.purpur.util.MinecraftInternalPlugin; + +public abstract class BossBarTask extends BukkitRunnable { + private final Map bossbars = new HashMap<>(); + private boolean started; + + abstract BossBar createBossBar(); + + abstract void updateBossBar(BossBar bossbar, Player player); + + @Override + public void run() { + Iterator> iter = bossbars.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + Player player = Bukkit.getPlayer(entry.getKey()); + if (player == null) { + iter.remove(); + continue; + } + updateBossBar(entry.getValue(), player); + } + } + + @Override + public void cancel() { + super.cancel(); + new HashSet<>(this.bossbars.keySet()).forEach(uuid -> { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + removePlayer(player); + } + }); + this.bossbars.clear(); + } + + public boolean removePlayer(Player player) { + BossBar bossbar = this.bossbars.remove(player.getUniqueId()); + if (bossbar != null) { + player.hideBossBar(bossbar); + return true; + } + return false; + } + + public void addPlayer(Player player) { + removePlayer(player); + BossBar bossbar = createBossBar(); + this.bossbars.put(player.getUniqueId(), bossbar); + this.updateBossBar(bossbar, player); + player.showBossBar(bossbar); + } + + public boolean hasPlayer(UUID uuid) { + return this.bossbars.containsKey(uuid); + } + + public boolean togglePlayer(Player player) { + if (removePlayer(player)) { + return false; + } + addPlayer(player); + return true; + } + + public void start() { + stop(); + this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1); + started = true; + } + + public void stop() { + if (started) { + cancel(); + } + } + + public static void startAll() { + RamBarTask.instance().start(); + TPSBarTask.instance().start(); + CompassTask.instance().start(); + } + + public static void stopAll() { + RamBarTask.instance().stop(); + TPSBarTask.instance().stop(); + CompassTask.instance().stop(); + } + + public static void addToAll(ServerPlayer player) { + Player bukkit = player.getBukkitEntity(); + if (player.ramBar()) { + RamBarTask.instance().addPlayer(bukkit); + } + if (player.tpsBar()) { + TPSBarTask.instance().addPlayer(bukkit); + } + if (player.compassBar()) { + CompassTask.instance().addPlayer(bukkit); + } + } + + public static void removeFromAll(Player player) { + RamBarTask.instance().removePlayer(player); + TPSBarTask.instance().removePlayer(player); + CompassTask.instance().removePlayer(player); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/task/CompassTask.java b/purpur-server/src/main/java/org/purpurmc/purpur/task/CompassTask.java new file mode 100644 index 000000000..bece7eefc --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/task/CompassTask.java @@ -0,0 +1,68 @@ +package org.purpurmc.purpur.task; + +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.item.Items; +import org.bukkit.entity.Player; +import org.purpurmc.purpur.PurpurConfig; + +public class CompassTask extends BossBarTask { + private static CompassTask instance; + + private int tick = 0; + + public static CompassTask instance() { + if (instance == null) { + instance = new CompassTask(); + } + return instance; + } + + @Override + public void run() { + if (++tick < PurpurConfig.commandCompassBarTickInterval) { + return; + } + tick = 0; + + MinecraftServer.getServer().getAllLevels().forEach((level) -> { + if (level.purpurConfig.compassItemShowsBossBar) { + level.players().forEach(player -> { + if (!player.compassBar()) { + if (player.getMainHandItem().getItem() != Items.COMPASS && player.getOffhandItem().getItem() != Items.COMPASS) { + removePlayer(player.getBukkitEntity()); + } else if (!hasPlayer(player.getUUID())) { + addPlayer(player.getBukkitEntity()); + } + } + }); + } + }); + + super.run(); + } + + @Override + BossBar createBossBar() { + return BossBar.bossBar(Component.text(""), PurpurConfig.commandCompassBarProgressPercent, PurpurConfig.commandCompassBarProgressColor, PurpurConfig.commandCompassBarProgressOverlay); + } + + @Override + void updateBossBar(BossBar bossbar, Player player) { + float yaw = player.getLocation().getYaw(); + int length = PurpurConfig.commandCompassBarTitle.length(); + int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F)); + bossbar.name(Component.text(PurpurConfig.commandCompassBarTitle.substring(pos - 25, pos + 25))); + } + + private float normalize(float yaw) { + while (yaw < -180.0F) { + yaw += 360.0F; + } + while (yaw > 180.0F) { + yaw -= 360.0F; + } + return yaw; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/task/RamBarTask.java b/purpur-server/src/main/java/org/purpurmc/purpur/task/RamBarTask.java new file mode 100644 index 000000000..8e98c0ae7 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/task/RamBarTask.java @@ -0,0 +1,117 @@ +package org.purpurmc.purpur.task; + +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; +import org.purpurmc.purpur.PurpurConfig; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryUsage; + +public class RamBarTask extends BossBarTask { + private static RamBarTask instance; + private long allocated = 0L; + private long used = 0L; + private long xmx = 0L; + private long xms = 0L; + private float percent = 0F; + private int tick = 0; + + public static RamBarTask instance() { + if (instance == null) { + instance = new RamBarTask(); + } + return instance; + } + + @Override + BossBar createBossBar() { + return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandRamBarProgressOverlay); + } + + @Override + void updateBossBar(BossBar bossbar, Player player) { + bossbar.progress(getBossBarProgress()); + bossbar.color(getBossBarColor()); + bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandRamBarTitle, + Placeholder.component("allocated", format(this.allocated)), + Placeholder.component("used", format(this.used)), + Placeholder.component("xmx", format(this.xmx)), + Placeholder.component("xms", format(this.xms)), + Placeholder.unparsed("percent", ((int) (this.percent * 100)) + "%") + )); + } + + @Override + public void run() { + if (++this.tick < PurpurConfig.commandRamBarTickInterval) { + return; + } + this.tick = 0; + + MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); + + this.allocated = heap.getCommitted(); + this.used = heap.getUsed(); + this.xmx = heap.getMax(); + this.xms = heap.getInit(); + this.percent = Math.max(Math.min((float) this.used / this.xmx, 1.0F), 0.0F); + + super.run(); + } + + private float getBossBarProgress() { + return this.percent; + } + + private BossBar.Color getBossBarColor() { + if (this.percent < 0.5F) { + return PurpurConfig.commandRamBarProgressColorGood; + } else if (this.percent < 0.75F) { + return PurpurConfig.commandRamBarProgressColorMedium; + } else { + return PurpurConfig.commandRamBarProgressColorLow; + } + } + + public Component format(long v) { + String color; + if (this.percent < 0.60F) { + color = PurpurConfig.commandRamBarTextColorGood; + } else if (this.percent < 0.85F) { + color = PurpurConfig.commandRamBarTextColorMedium; + } else { + color = PurpurConfig.commandRamBarTextColorLow; + } + String value; + if (v < 1024) { + value = v + "B"; + } else { + int z = (63 - Long.numberOfLeadingZeros(v)) / 10; + value = String.format("%.1f%s", (double) v / (1L << (z * 10)), "BKMGTPE".charAt(z)); + } + return MiniMessage.miniMessage().deserialize(color, Placeholder.unparsed("text", value)); + } + + public long getAllocated() { + return this.allocated; + } + + public long getUsed() { + return this.used; + } + + public long getXmx() { + return this.xmx; + } + + public long getXms() { + return this.xms; + } + + public float getPercent() { + return this.percent; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java b/purpur-server/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java new file mode 100644 index 000000000..8769993e7 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/task/TPSBarTask.java @@ -0,0 +1,142 @@ +package org.purpurmc.purpur.task; + +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.purpurmc.purpur.PurpurConfig; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class TPSBarTask extends BossBarTask { + private static TPSBarTask instance; + private double tps = 20.0D; + private double mspt = 0.0D; + private int tick = 0; + + public static TPSBarTask instance() { + if (instance == null) { + instance = new TPSBarTask(); + } + return instance; + } + + @Override + BossBar createBossBar() { + return BossBar.bossBar(Component.text(""), 0.0F, instance().getBossBarColor(), PurpurConfig.commandTPSBarProgressOverlay); + } + + @Override + void updateBossBar(BossBar bossbar, Player player) { + bossbar.progress(getBossBarProgress()); + bossbar.color(getBossBarColor()); + bossbar.name(MiniMessage.miniMessage().deserialize(PurpurConfig.commandTPSBarTitle, + Placeholder.component("tps", getTPSColor()), + Placeholder.component("mspt", getMSPTColor()), + Placeholder.component("ping", getPingColor(player.getPing())) + )); + } + + @Override + public void run() { + if (++tick < PurpurConfig.commandTPSBarTickInterval) { + return; + } + tick = 0; + + this.tps = Math.max(Math.min(Bukkit.getTPS()[0], 20.0D), 0.0D); + this.mspt = Bukkit.getAverageTickTime(); + + super.run(); + } + + private float getBossBarProgress() { + if (PurpurConfig.commandTPSBarProgressFillMode == FillMode.MSPT) { + return Math.max(Math.min((float) mspt / 50.0F, 1.0F), 0.0F); + } else { + return Math.max(Math.min((float) tps / 20.0F, 1.0F), 0.0F); + } + } + + private BossBar.Color getBossBarColor() { + if (isGood(PurpurConfig.commandTPSBarProgressFillMode)) { + return PurpurConfig.commandTPSBarProgressColorGood; + } else if (isMedium(PurpurConfig.commandTPSBarProgressFillMode)) { + return PurpurConfig.commandTPSBarProgressColorMedium; + } else { + return PurpurConfig.commandTPSBarProgressColorLow; + } + } + + private boolean isGood(FillMode mode) { + return isGood(mode, 0); + } + + private boolean isGood(FillMode mode, int ping) { + if (mode == FillMode.MSPT) { + return mspt < 40; + } else if (mode == FillMode.TPS) { + return tps >= 19; + } else if (mode == FillMode.PING) { + return ping < 100; + } else { + return false; + } + } + + private boolean isMedium(FillMode mode) { + return isMedium(mode, 0); + } + + private boolean isMedium(FillMode mode, int ping) { + if (mode == FillMode.MSPT) { + return mspt < 50; + } else if (mode == FillMode.TPS) { + return tps >= 15; + } else if (mode == FillMode.PING) { + return ping < 200; + } else { + return false; + } + } + + private Component getTPSColor() { + String color; + if (isGood(FillMode.TPS)) { + color = PurpurConfig.commandTPSBarTextColorGood; + } else if (isMedium(FillMode.TPS)) { + color = PurpurConfig.commandTPSBarTextColorMedium; + } else { + color = PurpurConfig.commandTPSBarTextColorLow; + } + return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps))); + } + + private Component getMSPTColor() { + String color; + if (isGood(FillMode.MSPT)) { + color = PurpurConfig.commandTPSBarTextColorGood; + } else if (isMedium(FillMode.MSPT)) { + color = PurpurConfig.commandTPSBarTextColorMedium; + } else { + color = PurpurConfig.commandTPSBarTextColorLow; + } + return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", mspt))); + } + + private Component getPingColor(int ping) { + String color; + if (isGood(FillMode.PING, ping)) { + color = PurpurConfig.commandTPSBarTextColorGood; + } else if (isMedium(FillMode.PING, ping)) { + color = PurpurConfig.commandTPSBarTextColorMedium; + } else { + color = PurpurConfig.commandTPSBarTextColorLow; + } + return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping))); + } + + public enum FillMode { + TPS, MSPT, PING + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Actionable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Actionable.java new file mode 100644 index 000000000..e18c37f06 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Actionable.java @@ -0,0 +1,24 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Map; + +public abstract class Actionable { + private final Block into; + private final Map drops; + + public Actionable(Block into, Map drops) { + this.into = into; + this.drops = drops; + } + + public Block into() { + return into; + } + + public Map drops() { + return drops; + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Flattenable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Flattenable.java new file mode 100644 index 000000000..345d4ee4f --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Flattenable.java @@ -0,0 +1,12 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Map; + +public class Flattenable extends Actionable { + public Flattenable(Block into, Map drops) { + super(into, drops); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Strippable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Strippable.java new file mode 100644 index 000000000..bf5402214 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Strippable.java @@ -0,0 +1,12 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Map; + +public class Strippable extends Actionable { + public Strippable(Block into, Map drops) { + super(into, drops); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Tillable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Tillable.java new file mode 100644 index 000000000..715f6dd44 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Tillable.java @@ -0,0 +1,50 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.HoeItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; + +public class Tillable extends Actionable { + private final Condition condition; + + public Tillable(Condition condition, Block into, Map drops) { + super(into, drops); + this.condition = condition; + } + + public Condition condition() { + return condition; + } + + public enum Condition { + AIR_ABOVE(HoeItem::onlyIfAirAbove), + ALWAYS((useOnContext) -> true); + + private final Predicate predicate; + + Condition(Predicate predicate) { + this.predicate = predicate; + } + + public Predicate predicate() { + return predicate; + } + + private static final Map BY_NAME = new HashMap<>(); + + static { + for (Condition condition : values()) { + BY_NAME.put(condition.name(), condition); + } + } + + public static Condition get(String name) { + return BY_NAME.get(name.toUpperCase(java.util.Locale.ROOT)); + } + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Waxable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Waxable.java new file mode 100644 index 000000000..64adb13b2 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Waxable.java @@ -0,0 +1,12 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Map; + +public class Waxable extends Actionable { + public Waxable(Block into, Map drops) { + super(into, drops); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/tool/Weatherable.java b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Weatherable.java new file mode 100644 index 000000000..b7586f494 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/tool/Weatherable.java @@ -0,0 +1,12 @@ +package org.purpurmc.purpur.tool; + +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Map; + +public class Weatherable extends Actionable { + public Weatherable(Block into, Map drops) { + super(into, drops); + } +} diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java b/purpur-server/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java new file mode 100644 index 000000000..77a18f304 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java @@ -0,0 +1,149 @@ +package org.purpurmc.purpur.util; + +import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.PluginLogger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.InputStream; +import java.util.List; + +public class MinecraftInternalPlugin extends PluginBase { + private boolean enabled = true; + + private final String pluginName; + private PluginDescriptionFile pdf; + + public MinecraftInternalPlugin() { + this.pluginName = "Minecraft"; + pdf = new PluginDescriptionFile(pluginName, "1.0", "nms"); + } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public File getDataFolder() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public PluginDescriptionFile getDescription() { + return pdf; + } + + @Override + public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() { + return pdf; + } + + @Override + public FileConfiguration getConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public InputStream getResource(String filename) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveDefaultConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void saveResource(String resourcePath, boolean replace) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void reloadConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public PluginLogger getLogger() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public PluginLoader getPluginLoader() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Server getServer() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void onDisable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void onLoad() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void onEnable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isNaggable() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void setNaggable(boolean canNag) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public @NotNull LifecycleEventManager getLifecycleManager() { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/scripts/upstreamCommit.sh b/scripts/upstreamCommit.sh index 0883dfb06..c272a2dba 100755 --- a/scripts/upstreamCommit.sh +++ b/scripts/upstreamCommit.sh @@ -7,7 +7,7 @@ # flag: --pufferfish HASH - the commit hash to use for comparing commits between pufferfish (pufferfish-gg/Pufferfish/compare/HASH...HEAD) function getCommits() { - curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/"$1"/compare/"$2"..."$3" | jq -r '.commits[] | "'"$1"'@\(.sha[:7]) \(.commit.message | split("\r\n")[0] | split("\n")[0])"' + curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/"$1"/compare/"$2"..."$3" | jq -r '.commits[] | "'"$1"'@\(.sha[:8]) \(.commit.message | split("\r\n")[0] | split("\n")[0])" | sub("\\[ci( |-)skip]"; "[ci/skip]")' } ( diff --git a/settings.gradle.kts b/settings.gradle.kts index 6a14c9086..7d2649054 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } if (!file(".git").exists()) { @@ -32,8 +32,26 @@ if (!file(".git").exists()) { } rootProject.name = "purpur" -for (name in listOf("Purpur-API", "Purpur-Server")) { +for (name in listOf("purpur-api", "purpur-server")) { val projName = name.lowercase(Locale.ENGLISH) include(projName) findProject(":$projName")!!.projectDir = file(name) } + +optionalInclude("test-plugin") + +fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) { + val settingsFile = file("$name.settings.gradle.kts") + if (settingsFile.exists()) { + apply(from = settingsFile) + findProject(":$name")?.let { op?.invoke(it) } + } else { + settingsFile.writeText( + """ + // Uncomment to enable the '$name' project + // include(":$name") + + """.trimIndent() + ) + } +} diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts new file mode 100644 index 000000000..4e1830aa8 --- /dev/null +++ b/test-plugin/build.gradle.kts @@ -0,0 +1,17 @@ +version = "1.0.0-SNAPSHOT" + +dependencies { + compileOnly(project(":purpur-api")) +} + +tasks.processResources { + val apiVersion = rootProject.providers.gradleProperty("mcVersion").get() + val props = mapOf( + "version" to project.version, + "apiversion" to "\"$apiVersion\"", + ) + inputs.properties(props) + filesMatching("paper-plugin.yml") { + expand(props) + } +} diff --git a/test-plugin/src/main/java/org/purpurmc/testplugin/TestPlugin.java b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPlugin.java new file mode 100644 index 000000000..39e2b3557 --- /dev/null +++ b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPlugin.java @@ -0,0 +1,12 @@ +package org.purpurmc.testplugin; + +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +public class TestPlugin extends JavaPlugin implements Listener { + + @Override + public void onEnable() { + this.getServer().getPluginManager().registerEvents(this, this); + } +} diff --git a/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginBootstrap.java b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginBootstrap.java new file mode 100644 index 000000000..612aa3e6f --- /dev/null +++ b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginBootstrap.java @@ -0,0 +1,4 @@ +package org.purpurmc.testplugin; + +public class TestPluginBootstrap { +} diff --git a/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginLoader.java b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginLoader.java new file mode 100644 index 000000000..6a00427bd --- /dev/null +++ b/test-plugin/src/main/java/org/purpurmc/testplugin/TestPluginLoader.java @@ -0,0 +1,4 @@ +package org.purpurmc.testplugin; + +public class TestPluginLoader { +} diff --git a/test-plugin/src/main/resources/paper-plugin.yml b/test-plugin/src/main/resources/paper-plugin.yml new file mode 100644 index 000000000..4357c79e2 --- /dev/null +++ b/test-plugin/src/main/resources/paper-plugin.yml @@ -0,0 +1,12 @@ +name: Purpur-Test-Plugin +version: ${version} +main: org.purpurmc.testplugin.TestPlugin +description: Purpur Test Plugin +author: PurpurMC +api-version: ${apiversion} +load: STARTUP +#bootstrapper: org.purpurmc.testplugin.TestPluginBootstrap +#loader: org.purpurmc.testplugin.TestPluginLoader +defaultPerm: FALSE +permissions: +dependencies: